customeragent-api / client /src /components /AnonymousUsersGeo.jsx
anasraza526's picture
Clean deploy to Hugging Face
ac90985
import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import api from '../api/axiosConfig';
import { MapPin, MessageSquare, Users, ChevronRight } from 'lucide-react';
const AnonymousUsersGeo = ({ websiteId }) => {
const [selectedUser, setSelectedUser] = useState(null);
const { data: users, isLoading, error } = useQuery({
queryKey: ['anonymous-users-geo', websiteId],
queryFn: async () => {
const response = await api.get(`/dashboard/anonymous-users-geo/${websiteId}`);
return response.data.anonymous_users || [];
},
refetchInterval: 30000,
retry: 1,
});
const { data: chatHistory } = useQuery({
queryKey: ['user-chat-geo', websiteId, selectedUser?.ip],
queryFn: async () => {
if (!selectedUser) return null;
try {
const response = await api.get(`/dashboard/user-chat-geo/${websiteId}/${selectedUser.ip}`);
return response.data;
} catch (error) {
console.error('Failed to fetch chat history:', error);
return null;
}
},
enabled: !!selectedUser,
});
if (isLoading) {
return (
<div className="bg-white rounded-2xl shadow-sm border border-secondary-100 p-6">
<div className="animate-pulse space-y-4">
{[...Array(3)].map((_, i) => (
<div key={i} className="h-20 bg-secondary-100 rounded-xl" />
))}
</div>
</div>
);
}
if (error) {
return (
<div className="bg-white rounded-2xl shadow-sm border border-secondary-100 p-6">
<p className="text-red-600">Failed to load anonymous users</p>
</div>
);
}
return (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Users List */}
<div className="lg:col-span-1 bg-white rounded-2xl shadow-sm border border-secondary-100 overflow-hidden flex flex-col">
<div className="p-4 md:p-6 border-b border-secondary-100">
<h3 className="text-lg font-bold text-secondary-900 flex items-center gap-2">
<Users className="w-5 h-5 text-primary-600" />
Anonymous Users
</h3>
<p className="text-sm text-secondary-500 mt-1">{users?.length || 0} unique locations</p>
</div>
<div className="divide-y divide-secondary-100 flex-1 overflow-y-auto">
{users && users.length > 0 ? (
users.map((user) => (
<button
key={user.ip}
onClick={() => setSelectedUser(user)}
className={`w-full p-4 text-left transition-colors hover:bg-secondary-50 ${
selectedUser?.ip === user.ip ? 'bg-primary-50 border-l-4 border-primary-600' : 'border-l-4 border-transparent'
}`}
>
<div className="space-y-2">
<div className="font-mono text-sm font-semibold text-secondary-900 truncate">
{user.ip}
</div>
<div className="text-sm text-secondary-600 truncate">
{user.display_name}
</div>
<div className="flex items-center gap-3 text-xs text-secondary-500 flex-wrap">
<span className="flex items-center gap-1">
<MessageSquare className="w-3 h-3" />
{user.total_messages}
</span>
<span className="flex items-center gap-1">
<Users className="w-3 h-3" />
{user.sessions.length}
</span>
</div>
</div>
</button>
))
) : (
<div className="p-8 text-center text-secondary-500">
<p className="text-sm">No anonymous users yet</p>
</div>
)}
</div>
</div>
{/* Chat History */}
<div className="lg:col-span-2 bg-white rounded-2xl shadow-sm border border-secondary-100 overflow-hidden flex flex-col">
{selectedUser ? (
<>
{/* Header */}
<div className="p-4 md:p-6 border-b border-secondary-100 bg-gradient-to-r from-primary-50 to-transparent">
<div className="space-y-2">
<div className="font-mono text-lg font-bold text-secondary-900 truncate">
{selectedUser.ip}
</div>
<div className="text-secondary-600 flex items-center gap-2 text-sm">
<MapPin className="w-4 h-4 text-primary-600 flex-shrink-0" />
<span className="truncate">{selectedUser.display_name}</span>
</div>
{chatHistory && (
<div className="flex gap-4 md:gap-6 text-xs md:text-sm text-secondary-500 pt-2 flex-wrap">
<span>{chatHistory.total_messages} messages</span>
<span>{chatHistory.total_sessions} sessions</span>
</div>
)}
</div>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 md:p-6 space-y-4 bg-secondary-50/30">
{chatHistory?.chat_history && chatHistory.chat_history.length > 0 ? (
chatHistory.chat_history.map((msg, idx) => (
<div
key={idx}
className={`flex ${msg.sender === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-xs px-4 py-3 rounded-lg text-sm ${
msg.sender === 'user'
? 'bg-primary-600 text-white rounded-br-none'
: 'bg-white text-secondary-900 border border-secondary-200 rounded-bl-none'
}`}
>
<p className="break-words">{msg.text}</p>
<p className={`text-xs mt-1 ${msg.sender === 'user' ? 'text-primary-100' : 'text-secondary-400'}`}>
{new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
</div>
</div>
))
) : (
<div className="flex items-center justify-center h-full text-secondary-500">
<p className="text-sm">No messages yet</p>
</div>
)}
</div>
</>
) : (
<div className="flex-1 flex items-center justify-center text-secondary-400">
<div className="text-center px-6">
<MessageSquare className="w-12 h-12 mx-auto mb-4 text-secondary-300" />
<p className="text-sm">Select a user to view chat history</p>
<p className="text-xs text-secondary-400 mt-2">Click on any user from the list</p>
</div>
</div>
)}
</div>
</div>
);
};
export default AnonymousUsersGeo;