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",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.3.0",
|
||||
"input-otp": "^1.2.4",
|
||||
"lucide-react": "^0.462.0",
|
||||
|
@ -4196,9 +4196,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.3.0",
|
||||
"input-otp": "^1.2.4",
|
||||
"lucide-react": "^0.462.0",
|
||||
|
|
|
@ -1,42 +1,87 @@
|
|||
|
||||
import { User } from "lucide-react";
|
||||
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 {
|
||||
id: string;
|
||||
contact: string;
|
||||
lastMessage: string;
|
||||
timestamp: string;
|
||||
// Initialize Supabase client
|
||||
const supabaseUrl = "https://ikmbnngahzcellthaysf.supabase.co";
|
||||
const supabaseAnonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlrbWJubmdhaHpjZWxsdGhheXNmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDA1ODY5NjUsImV4cCI6MjA1NjE2Mjk2NX0.puDSeLAaTirOPyj6ndF2Mzzu8CKxlwJsoFP6gK8cWuA";
|
||||
|
||||
if (!supabaseUrl || !supabaseAnonKey) {
|
||||
throw new Error('Supabase URL and Anon Key are required');
|
||||
}
|
||||
|
||||
const mockConversations: Conversation[] = [
|
||||
{
|
||||
id: "1",
|
||||
contact: "+1 (555) 987-6543",
|
||||
lastMessage: "Thanks for your message",
|
||||
timestamp: "2 min ago",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
contact: "+1 (555) 876-5432",
|
||||
lastMessage: "I'll get back to you shortly",
|
||||
timestamp: "1 hour ago",
|
||||
},
|
||||
];
|
||||
const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
phone_number: string;
|
||||
message: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="space-y-4 p-4 animate-fadeIn">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Conversations</h2>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{mockConversations.length} active
|
||||
{conversations.length} active
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid gap-4">
|
||||
{mockConversations.map((conversation) => (
|
||||
{conversations.map((message) => (
|
||||
<Card
|
||||
key={conversation.id}
|
||||
key={message.id}
|
||||
className="p-4 transition-all hover:shadow-md cursor-pointer group"
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
|
@ -45,13 +90,13 @@ export function ConversationList() {
|
|||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<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">
|
||||
{conversation.timestamp}
|
||||
{formatDistanceToNow(new Date(message.created_at), { addSuffix: true })}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground truncate">
|
||||
{conversation.lastMessage}
|
||||
{message.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue