Skip to content

Commit 99f6aee

Browse files
File changes
1 parent 9af38be commit 99f6aee

File tree

5 files changed

+774
-0
lines changed

5 files changed

+774
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import React, { useState } from 'react';
2+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
3+
import { base44 } from '@/api/base44Client';
4+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
5+
import { Button } from '@/components/ui/button';
6+
import { Badge } from '@/components/ui/badge';
7+
import { Avatar } from '@/components/ui/avatar';
8+
import { Users, Sparkles, MessageCircle, UserPlus } from 'lucide-react';
9+
import { toast } from 'sonner';
10+
11+
export default function AIConnectionSuggestions({ user }) {
12+
const [loading, setLoading] = useState(false);
13+
const queryClient = useQueryClient();
14+
15+
const { data: suggestions, isLoading } = useQuery({
16+
queryKey: ['ai-connection-suggestions', user?.email],
17+
queryFn: async () => {
18+
setLoading(true);
19+
try {
20+
const response = await base44.integrations.Core.InvokeLLM({
21+
prompt: `As a new employee joining a remote-first company, suggest 3-5 teammates this person should connect with.
22+
23+
User Role: ${user.role === 'admin' ? 'admin' : user.user_type || 'participant'}
24+
User Email: ${user.email}
25+
26+
Provide practical connection suggestions with reasons why they should connect.`,
27+
response_json_schema: {
28+
type: 'object',
29+
properties: {
30+
suggestions: {
31+
type: 'array',
32+
items: {
33+
type: 'object',
34+
properties: {
35+
role: { type: 'string' },
36+
reason: { type: 'string' },
37+
suggested_action: { type: 'string' }
38+
}
39+
}
40+
}
41+
}
42+
}
43+
});
44+
return response.suggestions;
45+
} finally {
46+
setLoading(false);
47+
}
48+
},
49+
enabled: !!user?.email,
50+
staleTime: 3600000 // 1 hour
51+
});
52+
53+
// Fetch actual users matching these roles
54+
const { data: allUsers } = useQuery({
55+
queryKey: ['all-users-for-connections'],
56+
queryFn: () => base44.entities.User.list('full_name', 100),
57+
enabled: !!suggestions
58+
});
59+
60+
const createBuddyMatchMutation = useMutation({
61+
mutationFn: async (buddyEmail) => {
62+
await base44.entities.BuddyMatch.create({
63+
user_email: user.email,
64+
buddy_email: buddyEmail,
65+
match_type: 'peer_buddy',
66+
status: 'pending',
67+
match_score: 80
68+
});
69+
},
70+
onSuccess: () => {
71+
queryClient.invalidateQueries(['buddy-matches']);
72+
toast.success('Connection request sent!');
73+
},
74+
onError: (error) => toast.error('Failed to send request: ' + error.message)
75+
});
76+
77+
return (
78+
<Card className="border-indigo-200 bg-gradient-to-br from-indigo-50 to-purple-50">
79+
<CardHeader>
80+
<CardTitle className="flex items-center gap-2">
81+
<Sparkles className="h-5 w-5 text-indigo-600" />
82+
Suggested Connections
83+
</CardTitle>
84+
</CardHeader>
85+
86+
<CardContent className="space-y-4">
87+
{loading && (
88+
<div className="flex items-center gap-2 text-sm text-slate-500">
89+
<Sparkles className="h-4 w-4 animate-pulse" />
90+
Finding great connections for you...
91+
</div>
92+
)}
93+
94+
{suggestions?.map((suggestion, idx) => {
95+
// Try to match with actual users
96+
const matchedUser = allUsers?.find(u =>
97+
u.role === suggestion.role ||
98+
u.user_type === suggestion.role.toLowerCase()
99+
);
100+
101+
return (
102+
<div
103+
key={idx}
104+
className="p-4 bg-white rounded-lg border border-slate-200 hover:border-indigo-300 transition-all"
105+
>
106+
<div className="flex items-start gap-3">
107+
<div className="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center">
108+
<Users className="h-5 w-5 text-indigo-600" />
109+
</div>
110+
<div className="flex-1">
111+
<div className="flex items-center gap-2 mb-1">
112+
<h4 className="font-medium text-slate-900">
113+
{matchedUser?.full_name || `${suggestion.role} Team Member`}
114+
</h4>
115+
<Badge variant="outline" className="text-xs">
116+
{suggestion.role}
117+
</Badge>
118+
</div>
119+
120+
{matchedUser && (
121+
<p className="text-sm text-slate-500 mb-2">{matchedUser.email}</p>
122+
)}
123+
124+
<p className="text-sm text-slate-600 mb-3">{suggestion.reason}</p>
125+
126+
<div className="bg-blue-50 border border-blue-200 rounded p-2 mb-3">
127+
<p className="text-xs text-blue-900">
128+
<strong>Suggested:</strong> {suggestion.suggested_action}
129+
</p>
130+
</div>
131+
132+
<div className="flex gap-2">
133+
{matchedUser && (
134+
<>
135+
<Button
136+
size="sm"
137+
variant="outline"
138+
onClick={() => createBuddyMatchMutation.mutate(matchedUser.email)}
139+
disabled={createBuddyMatchMutation.isPending}
140+
>
141+
<UserPlus className="h-3 w-3 mr-1" />
142+
Connect
143+
</Button>
144+
<Button
145+
size="sm"
146+
variant="outline"
147+
onClick={() => window.location.href = `/pages/Channels`}
148+
>
149+
<MessageCircle className="h-3 w-3 mr-1" />
150+
Message
151+
</Button>
152+
</>
153+
)}
154+
</div>
155+
</div>
156+
</div>
157+
</div>
158+
);
159+
})}
160+
161+
{!isLoading && !suggestions?.length && (
162+
<div className="text-center py-8 text-slate-500">
163+
<Users className="h-12 w-12 text-slate-300 mx-auto mb-2" />
164+
<p className="text-sm">Connection suggestions will appear here</p>
165+
</div>
166+
)}
167+
</CardContent>
168+
</Card>
169+
);
170+
}

0 commit comments

Comments
 (0)