import { useEffect, useMemo } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { supabase } from "@/components/atomic-crm/providers/supabase/supabase"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ds/ui/card"; import { Badge } from "@/components/ds/ui/badge"; import { Skeleton } from "@/components/ds/ui/skeleton"; import { Mail, Phone, MessageSquare, StickyNote, Play, Calendar, User, Clock, CheckCircle2, AlertCircle, } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; import { Button } from "@/components/ds/ui/button"; import { useTranslate, useLocale } from "ra-core"; import { getDateFnsLocale } from "@/i18n/date-fns"; interface ActivityFeedProps { contactId?: number; salesId?: number; // Optional: filter by agent className?: string; } export const ActivityFeed = ({ contactId, salesId, className, }: ActivityFeedProps) => { const queryClient = useQueryClient(); const queryKey = useMemo( () => ["activities", contactId, salesId], [contactId, salesId], ); const translate = useTranslate(); // 1. Initial Fetch const { data: activities, isLoading } = useQuery({ queryKey, queryFn: async () => { let query = supabase .from("activities") .select( ` *, sales:sales_id (first_name, last_name) `, ) .order("created_at", { ascending: false }) .limit(50); if (contactId) { query = query.eq("contact_id", contactId); } if (salesId) { query = query.eq("sales_id", salesId); } const { data, error } = await query; if (error) throw error; return data; }, }); // 2. Realtime Subscription useEffect(() => { const channel = supabase .channel("activities-feed") .on( "postgres_changes", { event: "*", schema: "public", table: "activities", filter: contactId ? `contact_id=eq.${contactId}` : undefined, }, (payload) => { console.log("Realtime update:", payload); // Invalidate query to refetch (simplest way to keep sync) queryClient.invalidateQueries({ queryKey }); }, ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [contactId, queryClient, queryKey]); if (isLoading) { return (
); } if (!activities || activities.length === 0) { return (
{translate("crm.activity_feed.empty")}
); } return (
{activities.map((activity) => ( ))}
); }; const ActivityCard = ({ activity }: { activity: any }) => { const isPending = activity.status === "pending" || activity.status === "claimed"; const isFailed = activity.status === "failed"; const translate = useTranslate(); const locale = useLocale(); return ( {isPending && (
)}
{translate(`crm.activity_feed.type.${activity.type}`, { _: activity.type, })} {activity.direction === "inbound" && ( {translate("crm.activity_feed.direction.inbound")} )} {activity.direction === "outbound" && ( {translate("crm.activity_feed.direction.outbound")} )}
{formatDistanceToNow(new Date(activity.created_at), { addSuffix: true, locale: getDateFnsLocale(locale), })} {activity.sales && ( <> {activity.sales.first_name} {activity.sales.last_name} )}
{/* Content Body */}
{isPending ? (
{translate("crm.activity_feed.label.processing_content")}
) : isFailed ? (

{translate("crm.activity_feed.label.processing_failed")}

) : (
{/* Transcript / Text */} {activity.processed_data?.transcript ? (
{activity.processed_data.transcript}
) : activity.raw_data?.source_type === "text" ? (

{activity.raw_data.content}

) : null} {/* Audio Player */} {activity.raw_data?.source_type === "url" && (
{translate("crm.activity_feed.label.audio_recording")}
)} {/* Atomic Facts / Summary */} {activity.processed_data?.summary && (
{translate("crm.activity_feed.label.summary")} {activity.processed_data.summary}
)}
)}
); }; const ActivityIcon = ({ type }: { type: string }) => { const className = "h-5 w-5 text-muted-foreground"; switch (type) { case "email": return ; case "call": return ; case "sms": return ; case "meeting": return ; default: return ; } }; const StatusBadge = ({ status }: { status: string }) => { const translate = useTranslate(); switch (status) { case "pending": case "claimed": case "processing": return ( {translate("crm.activity_feed.status.processing")} ); case "completed": return ; case "failed": return ; default: return null; } };