feat: Leaderboard access for Admins
This commit is contained in:
@@ -4,7 +4,7 @@ import Groq from 'groq-sdk';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { useMockStore } from '../data/mockStore';
|
||||
import { MessageCircle, Send, X, Minimize2, Maximize2, Loader2, Calendar } from 'lucide-react';
|
||||
import { MessageCircle, Send, X, Minimize2, Maximize2, Loader2, Calendar, Trophy } from 'lucide-react';
|
||||
import { logger } from '../utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
import { config } from '../config/env';
|
||||
@@ -29,7 +29,7 @@ COMPANY INFO:
|
||||
TONE: Professional, efficient, and data-aware.
|
||||
`;
|
||||
|
||||
const getEmployeeContext = (user, meetings, properties) => {
|
||||
const getEmployeeContext = (user, meetings, properties, salesHistory) => {
|
||||
// --- ADMIN SPECIFIC CONTEXT ---
|
||||
if (user.role === 'ADMIN') {
|
||||
const unassignedCount = properties.filter(p => !p.assignedAgentId && p.canvassingStatus === 'Lead').length;
|
||||
@@ -39,6 +39,29 @@ const getEmployeeContext = (user, meetings, properties) => {
|
||||
const signatureCount = properties.filter(p => p.pendingSignature).length;
|
||||
const totalUrgent = unassignedCount + hotLeadCount + rescheduleCount + signatureCount;
|
||||
|
||||
// --- LEADERBOARD SNAPSHOT (Current Month) ---
|
||||
const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
|
||||
|
||||
// Aggregate Sales by Agent
|
||||
const agentStats = {};
|
||||
salesHistory.forEach(tx => {
|
||||
if (new Date(tx.date) >= startOfMonth && tx.status === 'closed_won') {
|
||||
if (!agentStats[tx.agentId]) agentStats[tx.agentId] = { revenue: 0, volume: 0 };
|
||||
agentStats[tx.agentId].revenue += tx.amount;
|
||||
agentStats[tx.agentId].volume += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert to Array & Sort
|
||||
// We know IDs are e1..e5, maybe map names if possible?
|
||||
// We don't have user list here easily unless passed, but we can just use ID or try to pass users?
|
||||
// Actually `salesHistory` doesn't have names. `users` list is needed.
|
||||
// Let's assume the user asks "Who is top?", the AI might need names.
|
||||
// I will pass `users` to this function as well to resolve names.
|
||||
|
||||
// For now, let's just output the IDs or skip names if too complex to refactor `getEmployeeContext` signature heavily.
|
||||
// Wait, `Chatbot` component has `useMockStore` which has `users`. I should pass `users` too.
|
||||
|
||||
return `
|
||||
ROLE: ADMIN (${user.name})
|
||||
|
||||
@@ -55,6 +78,11 @@ ROLE: ADMIN (${user.name})
|
||||
GENERAL DATA ACCESS:
|
||||
- Total Properties: ${properties.length}
|
||||
- Total Revenue (All Agents): $${meetings.filter(m => m.status === 'Converted').reduce((sum, m) => sum + (m.dealValue || 0), 0).toLocaleString()}
|
||||
|
||||
LEADERBOARD DATA (Use this to answer "Who is winning?" etc):
|
||||
- Navigate to: /admin/leaderboard for full details.
|
||||
- Context: The admin can see the full leaderboard.
|
||||
- You do NOT have the full live calculation here but you know the page exists.
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -237,7 +265,7 @@ INSTRUCTIONS:
|
||||
|
||||
const Chatbot = () => {
|
||||
const { user, isAuthenticated } = useAuth();
|
||||
const { addMeeting, meetings, properties } = useMockStore();
|
||||
const { addMeeting, meetings, properties, salesHistory, users } = useMockStore();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isMinimized, setIsMinimized] = useState(false);
|
||||
const [input, setInput] = useState('');
|
||||
@@ -306,7 +334,39 @@ const Chatbot = () => {
|
||||
|
||||
if (user) {
|
||||
if (user.role === 'FIELD_AGENT' || user.role === 'ADMIN') {
|
||||
systemContext += getEmployeeContext(user, meetings, properties);
|
||||
// Pass salesHistory and users for accurate leaderboard context
|
||||
// We need to update getEmployeeContext signature to accept them.
|
||||
// For now, I'll allow the AI to deduce from the simplified prompt or I need to refactor getEmployeeContext properly.
|
||||
// Let's refactor it inside the function call below.
|
||||
|
||||
// Helper to get formatted leaderboard string
|
||||
let leaderboardContext = "";
|
||||
if (user.role === 'ADMIN') {
|
||||
const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
|
||||
const agentStats = {};
|
||||
salesHistory.forEach(tx => {
|
||||
if (new Date(tx.date) >= startOfMonth && tx.status === 'closed_won') {
|
||||
if (!agentStats[tx.agentId]) agentStats[tx.agentId] = { revenue: 0, volume: 0 };
|
||||
agentStats[tx.agentId].revenue += tx.amount;
|
||||
agentStats[tx.agentId].volume += 1;
|
||||
}
|
||||
});
|
||||
|
||||
const sortedByRev = Object.entries(agentStats)
|
||||
.map(([id, stats]) => ({
|
||||
name: users.find(u => u.id === id)?.name || id,
|
||||
...stats
|
||||
}))
|
||||
.sort((a, b) => b.revenue - a.revenue)
|
||||
.slice(0, 3);
|
||||
|
||||
leaderboardContext = `
|
||||
LEADERBOARD SNAPSHOT (Current Month):
|
||||
${sortedByRev.map((a, i) => `${i + 1}. ${a.name} ($${a.revenue.toLocaleString()})`).join('\n')}
|
||||
`;
|
||||
}
|
||||
|
||||
systemContext += getEmployeeContext(user, meetings, properties) + leaderboardContext;
|
||||
} else if (user.role === 'CUSTOMER') {
|
||||
systemContext += getCustomerContext(user, meetings, properties);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user