AI Assistant Capabilities
Smart Recommendations
Context-aware suggestions for next actions, follow-ups, and deal strategies
Automated Insights
Real-time analysis of client behavior, market trends, and opportunity identification
Content Generation
AI-powered email templates, property descriptions, and marketing content
Decision Support
Data-driven recommendations for pricing, timing, and negotiation strategies
AI Assistant Architecture
Intelligent Recommendation Engine
Core AI Assistant Service
// lib/ai/ai-assistant-service.ts
export class AIAssistantService {
private nlpEngine: NLPEngine;
private recommendationEngine: RecommendationEngine;
private marketAnalyzer: MarketAnalyzer;
private contentGenerator: ContentGenerator;
constructor() {
this.nlpEngine = new NLPEngine();
this.recommendationEngine = new RecommendationEngine();
this.marketAnalyzer = new MarketAnalyzer();
this.contentGenerator = new ContentGenerator();
}
async generateContextualRecommendations(
userId: string,
organizationId: string,
context: AssistantContext
): Promise<AIRecommendations> {
try {
// Gather comprehensive user context
const userContext = await this.gatherUserContext(userId, organizationId);
// Analyze current situation
const situationAnalysis = await this.analyzeSituation(userContext, context);
// Generate targeted recommendations
const recommendations = await this.generateRecommendations(
userContext,
situationAnalysis
);
// Prioritize and personalize
const prioritizedRecommendations = await this.prioritizeRecommendations(
recommendations,
userContext
);
// Generate supporting content
const supportingContent = await this.generateSupportingContent(
prioritizedRecommendations,
userContext
);
return {
recommendations: prioritizedRecommendations,
insights: situationAnalysis.insights,
supportingContent,
confidence: this.calculateConfidence(recommendations),
generatedAt: new Date(),
expiresAt: this.calculateExpiration(recommendations)
};
} catch (error) {
log.error('AI Assistant recommendation generation failed', {
userId,
organizationId,
error: parseError(error)
});
throw error;
}
}
private async gatherUserContext(
userId: string,
organizationId: string
): Promise<UserContext> {
// Gather comprehensive user data
const user = await database.user.findUnique({
where: { clerkId: userId },
include: {
OrganizationMembership: {
where: { organizationId },
include: {
Organization: {
include: {
settings: true,
promptSettings: true
}
}
}
}
}
});
// Get recent activity
const recentContacts = await database.person.findMany({
where: {
organizationId,
lastContactAt: {
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days
}
},
include: {
deals: { where: { status: 'OPEN' } },
communications: {
take: 5,
orderBy: { createdAt: 'desc' }
}
},
orderBy: { lastContactAt: 'desc' },
take: 50
});
// Get active deals
const activeDeals = await database.deal.findMany({
where: {
organizationId,
status: 'OPEN'
},
include: {
person: true,
property: true,
tasks: {
where: { status: { not: 'COMPLETED' } },
orderBy: { dueDate: 'asc' }
}
},
orderBy: { lastStageChange: 'asc' }
});
// Get pending tasks
const pendingTasks = await database.task.findMany({
where: {
organizationId,
status: { not: 'COMPLETED' },
dueDate: {
lte: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // Next 7 days
}
},
include: {
person: true,
deal: true
},
orderBy: { dueDate: 'asc' }
});
// Get performance metrics
const performanceMetrics = await this.gatherPerformanceMetrics(
userId,
organizationId
);
return {
user,
recentContacts,
activeDeals,
pendingTasks,
performanceMetrics,
marketConditions: await this.marketAnalyzer.getCurrentConditions(organizationId)
};
}
private async analyzeSituation(
userContext: UserContext,
context: AssistantContext
): Promise<SituationAnalysis> {
const insights: AssistantInsight[] = [];
// Analyze deal pipeline health
const pipelineAnalysis = this.analyzePipelineHealth(userContext.activeDeals);
insights.push(...pipelineAnalysis);
// Analyze contact engagement
const engagementAnalysis = this.analyzeContactEngagement(userContext.recentContacts);
insights.push(...engagementAnalysis);
// Analyze task management
const taskAnalysis = this.analyzeTaskManagement(userContext.pendingTasks);
insights.push(...taskAnalysis);
// Analyze market opportunities
const marketAnalysis = await this.analyzeMarketOpportunities(userContext);
insights.push(...marketAnalysis);
// Performance gap analysis
const performanceAnalysis = this.analyzePerformanceGaps(
userContext.performanceMetrics
);
insights.push(...performanceAnalysis);
return {
insights,
urgentItems: this.identifyUrgentItems(userContext),
opportunities: this.identifyOpportunities(userContext),
riskFactors: this.identifyRiskFactors(userContext),
overallScore: this.calculateOverallHealth(insights)
};
}
private async generateRecommendations(
userContext: UserContext,
situationAnalysis: SituationAnalysis
): Promise<AIRecommendation[]> {
const recommendations: AIRecommendation[] = [];
// High-priority urgent actions
for (const urgentItem of situationAnalysis.urgentItems) {
recommendations.push(await this.generateUrgentActionRecommendation(urgentItem));
}
// Opportunity-based recommendations
for (const opportunity of situationAnalysis.opportunities) {
recommendations.push(await this.generateOpportunityRecommendation(opportunity));
}
// Performance improvement recommendations
for (const insight of situationAnalysis.insights) {
if (insight.actionable) {
recommendations.push(await this.generateImprovementRecommendation(insight));
}
}
// Proactive recommendations
const proactiveRecs = await this.generateProactiveRecommendations(userContext);
recommendations.push(...proactiveRecs);
return recommendations;
}
private async generateUrgentActionRecommendation(
urgentItem: UrgentItem
): Promise<AIRecommendation> {
return {
id: generateId(),
type: 'URGENT_ACTION',
priority: 'HIGH',
title: urgentItem.title,
description: urgentItem.description,
actionType: urgentItem.actionType,
estimatedImpact: 'HIGH',
estimatedEffort: urgentItem.effort,
deadline: urgentItem.deadline,
actions: [
{
type: urgentItem.actionType,
label: urgentItem.actionLabel,
url: urgentItem.actionUrl,
params: urgentItem.actionParams
}
],
reasoning: urgentItem.reasoning,
suggestedContent: await this.generateSuggestedContent(urgentItem)
};
}
private async generateOpportunityRecommendation(
opportunity: Opportunity
): Promise<AIRecommendation> {
const suggestedActions = await this.generateOpportunityActions(opportunity);
return {
id: generateId(),
type: 'OPPORTUNITY',
priority: opportunity.priority,
title: opportunity.title,
description: opportunity.description,
actionType: 'PURSUE_OPPORTUNITY',
estimatedImpact: opportunity.estimatedValue,
estimatedEffort: 'MEDIUM',
deadline: opportunity.timeline,
actions: suggestedActions,
reasoning: opportunity.reasoning,
supportingData: opportunity.data,
suggestedContent: await this.generateOpportunityContent(opportunity)
};
}
private async generateProactiveRecommendations(
userContext: UserContext
): Promise<AIRecommendation[]> {
const recommendations: AIRecommendation[] = [];
// Market-based recommendations
const marketRecs = await this.generateMarketRecommendations(userContext);
recommendations.push(...marketRecs);
// Relationship maintenance recommendations
const relationshipRecs = await this.generateRelationshipRecommendations(userContext);
recommendations.push(...relationshipRecs);
// Content creation recommendations
const contentRecs = await this.generateContentRecommendations(userContext);
recommendations.push(...contentRecs);
// Skill development recommendations
const skillRecs = await this.generateSkillRecommendations(userContext);
recommendations.push(...skillRecs);
return recommendations;
}
}
interface AIRecommendations {
recommendations: AIRecommendation[];
insights: AssistantInsight[];
supportingContent: SupportingContent[];
confidence: number;
generatedAt: Date;
expiresAt: Date;
}
interface AIRecommendation {
id: string;
type: 'URGENT_ACTION' | 'OPPORTUNITY' | 'IMPROVEMENT' | 'PROACTIVE';
priority: 'HIGH' | 'MEDIUM' | 'LOW';
title: string;
description: string;
actionType: string;
estimatedImpact: 'HIGH' | 'MEDIUM' | 'LOW';
estimatedEffort: 'HIGH' | 'MEDIUM' | 'LOW';
deadline?: Date;
actions: RecommendedAction[];
reasoning: string;
supportingData?: unknown;
suggestedContent?: SuggestedContent;
}
interface AssistantInsight {
id: string;
category: 'PIPELINE' | 'CONTACTS' | 'PERFORMANCE' | 'MARKET' | 'TASKS';
severity: 'INFO' | 'WARNING' | 'CRITICAL';
title: string;
description: string;
actionable: boolean;
impact: 'HIGH' | 'MEDIUM' | 'LOW';
data?: unknown;
}
Smart Content Generation
AI-Powered Email Templates
// lib/ai/content-generator.ts
export class AIContentGenerator {
private openAI: OpenAI;
private templateEngine: TemplateEngine;
constructor() {
this.openAI = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
this.templateEngine = new TemplateEngine();
}
async generatePersonalizedEmail(
context: EmailGenerationContext
): Promise<GeneratedEmail> {
const prompt = this.buildEmailPrompt(context);
const response = await this.openAI.chat.completions.create({
model: MODEL_DEFAULTS.chat,
messages: [
{
role: 'system',
content: `You are an expert real estate email copywriter. Generate professional,
personalized emails that are engaging but not overly salesy. Focus on providing
value and building relationships. Keep emails concise and action-oriented.`
},
{
role: 'user',
content: prompt
}
],
temperature: 0.7,
max_tokens: 500
});
const generatedContent = response.choices[0]?.message?.content || '';
return {
subject: this.extractSubject(generatedContent),
body: this.extractBody(generatedContent),
tone: context.preferredTone,
personalization: context.personalizationData,
suggestedFollowUp: this.generateFollowUpSuggestion(context),
estimatedEngagement: this.estimateEngagementScore(generatedContent, context)
};
}
private buildEmailPrompt(context: EmailGenerationContext): string {
return `
Generate a personalized email for the following scenario:
**Recipient:** ${context.recipient.firstName} ${context.recipient.lastName}
**Relationship Stage:** ${context.recipient.status}
**Email Purpose:** ${context.purpose}
**Key Context:** ${context.context}
**Recipient Details:**
- Location: ${context.recipient.city}, ${context.recipient.state}
- Lead Source: ${context.recipient.source}
- Last Contact: ${context.recipient.lastContactAt}
- Lead Score: ${context.recipient.leadScore}/100
**Recent Activity:**
${context.recentActivity?.map(activity => `- ${activity}`).join('\n') || 'No recent activity'}
**Market Context:**
- Current market conditions: ${context.marketConditions}
- Recent comparable sales: ${context.marketData?.recentSales || 'None available'}
**Instructions:**
- Tone: ${context.preferredTone}
- Include call-to-action: ${context.callToAction}
- Reference: ${context.reference || 'None'}
- Max length: 200 words
- Include subject line
Format as:
SUBJECT: [subject line]
BODY: [email body]
`;
}
async generatePropertyDescription(
property: Property,
targetAudience: string
): Promise<PropertyDescription> {
const prompt = `
Write a compelling property description for:
**Property Details:**
- Address: ${property.address}
- Type: ${property.propertyType}
- Bedrooms: ${property.bedrooms}
- Bathrooms: ${property.bathrooms}
- Square Feet: ${property.squareFootage}
- Lot Size: ${property.lotSize}
- Year Built: ${property.yearBuilt}
- Price: $${property.price?.toLocaleString()}
**Key Features:**
${property.features?.map(feature => `- ${feature}`).join('\n') || ''}
**Target Audience:** ${targetAudience}
Requirements:
- Highlight unique selling points
- Use engaging, descriptive language
- Include lifestyle benefits
- Mention neighborhood/location advantages
- Max 300 words
- Include suggested hashtags for social media
`;
const response = await this.openAI.chat.completions.create({
model: MODEL_DEFAULTS.chat,
messages: [
{
role: 'system',
content: `You are a professional real estate copywriter specializing in
property descriptions that sell. Write compelling, accurate descriptions
that highlight benefits and create emotional connection.`
},
{
role: 'user',
content: prompt
}
],
temperature: 0.8,
max_tokens: 400
});
const content = response.choices[0]?.message?.content || '';
return {
description: content,
keyHighlights: this.extractKeyHighlights(content),
suggestedHashtags: this.extractHashtags(content),
targetKeywords: this.generateTargetKeywords(property, targetAudience),
tone: 'professional',
estimatedEngagement: this.estimatePropertyDescriptionEngagement(content)
};
}
async generateMarketInsightContent(
marketData: MarketData,
audience: 'buyers' | 'sellers' | 'investors'
): Promise<MarketInsightContent> {
const prompt = `
Create market insight content based on:
**Market Data:**
- Average Price: $${marketData.averagePrice?.toLocaleString()}
- Price Change: ${marketData.priceChange}% vs last quarter
- Days on Market: ${marketData.averageDaysOnMarket} days
- Inventory: ${marketData.activeListings} active listings
- Sales Volume: ${marketData.salesVolume} transactions this month
**Market Trends:**
${marketData.trends?.map(trend => `- ${trend}`).join('\n') || ''}
**Target Audience:** ${audience}
Create:
1. Executive summary (50 words)
2. Key insights (3-4 bullet points)
3. Actionable recommendations
4. Market outlook
Make it valuable and actionable for ${audience}.
`;
const response = await this.openAI.chat.completions.create({
model: MODEL_DEFAULTS.chat,
messages: [
{
role: 'system',
content: `You are a real estate market analyst creating insightful,
data-driven content for real estate professionals and their clients.`
},
{
role: 'user',
content: prompt
}
],
temperature: 0.6,
max_tokens: 600
});
return {
content: response.choices[0]?.message?.content || '',
audience,
dataPoints: Object.keys(marketData),
suggestedDistribution: this.suggestDistributionChannels(audience),
followUpTopics: this.generateFollowUpTopics(marketData, audience)
};
}
}
interface GeneratedEmail {
subject: string;
body: string;
tone: string;
personalization: PersonalizationData;
suggestedFollowUp: string;
estimatedEngagement: number;
}
interface PropertyDescription {
description: string;
keyHighlights: string[];
suggestedHashtags: string[];
targetKeywords: string[];
tone: string;
estimatedEngagement: number;
}
interface MarketInsightContent {
content: string;
audience: string;
dataPoints: string[];
suggestedDistribution: string[];
followUpTopics: string[];
}
Real-time Assistant Interface
AI Assistant Chat Component
// components/ai-assistant-chat.tsx
export function AIAssistantChat() {
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [isTyping, setIsTyping] = useState(false);
const [inputValue, setInputValue] = useState('');
const [context, setContext] = useState<AssistantContext | null>(null);
const { user, organization } = useAuth();
const messagesEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Initialize with welcome message and recommendations
initializeAssistant();
}, []);
useEffect(() => {
scrollToBottom();
}, [messages]);
const initializeAssistant = async () => {
try {
const response = await fetch('/api/ai/assistant/initialize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: user.id,
organizationId: organization.id
})
});
const data = await response.json();
setMessages([
{
id: generateId(),
type: 'assistant',
content: data.welcomeMessage,
recommendations: data.initialRecommendations,
timestamp: new Date()
}
]);
setContext(data.context);
} catch (error) {
console.error('Failed to initialize AI assistant:', error);
}
};
const handleSendMessage = async () => {
if (!inputValue.trim()) return;
const userMessage: ChatMessage = {
id: generateId(),
type: 'user',
content: inputValue,
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage]);
setInputValue('');
setIsTyping(true);
try {
const response = await fetch('/api/ai/assistant/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: inputValue,
context,
conversationHistory: messages.slice(-10) // Last 10 messages for context
})
});
const data = await response.json();
const assistantMessage: ChatMessage = {
id: generateId(),
type: 'assistant',
content: data.response,
recommendations: data.recommendations,
actions: data.suggestedActions,
timestamp: new Date()
};
setMessages(prev => [...prev, assistantMessage]);
setContext(data.updatedContext);
} catch (error) {
const errorMessage: ChatMessage = {
id: generateId(),
type: 'error',
content: 'Sorry, I encountered an error. Please try again.',
timestamp: new Date()
};
setMessages(prev => [...prev, errorMessage]);
} finally {
setIsTyping(false);
}
};
const executeRecommendation = async (recommendation: AIRecommendation) => {
try {
await fetch('/api/ai/assistant/execute-recommendation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
recommendationId: recommendation.id,
action: recommendation.actions[0] // Execute first action
})
});
toast.success('Action executed successfully');
// Add confirmation message
const confirmationMessage: ChatMessage = {
id: generateId(),
type: 'assistant',
content: `✅ I've executed the recommendation: ${recommendation.title}`,
timestamp: new Date()
};
setMessages(prev => [...prev, confirmationMessage]);
} catch (error) {
toast.error('Failed to execute recommendation');
}
};
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
return (
<div className="flex flex-col h-full max-h-[600px]">
{/* Chat Header */}
<div className="flex items-center justify-between p-4 border-b">
<div className="flex items-center space-x-3">
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
<Bot className="h-5 w-5 text-white" />
</div>
<div>
<h3 className="font-semibold">AI Assistant</h3>
<p className="text-sm text-gray-500">
{isTyping ? 'Thinking...' : 'Ready to help'}
</p>
</div>
</div>
<Button variant="ghost" size="sm">
<Settings className="h-4 w-4" />
</Button>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.type === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] rounded-lg p-3 ${
message.type === 'user'
? 'bg-blue-500 text-white'
: message.type === 'error'
? 'bg-red-100 text-red-800'
: 'bg-gray-100 text-gray-800'
}`}
>
<p className="text-sm">{message.content}</p>
{/* Recommendations */}
{message.recommendations && message.recommendations.length > 0 && (
<div className="mt-3 space-y-2">
<p className="text-xs font-semibold">Recommendations:</p>
{message.recommendations.map((rec) => (
<div
key={rec.id}
className="bg-white rounded border p-2 text-gray-800"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<p className="text-xs font-medium">{rec.title}</p>
<p className="text-xs text-gray-600 mt-1">
{rec.description}
</p>
</div>
<Badge
variant={rec.priority === 'HIGH' ? 'destructive' : 'secondary'}
className="text-xs"
>
{rec.priority}
</Badge>
</div>
{rec.actions && rec.actions.length > 0 && (
<div className="mt-2 flex space-x-2">
{rec.actions.map((action, index) => (
<Button
key={index}
size="sm"
variant="outline"
onClick={() => executeRecommendation(rec)}
className="text-xs"
>
{action.label}
</Button>
))}
</div>
)}
</div>
))}
</div>
)}
<p className="text-xs opacity-70 mt-2">
{formatDistanceToNow(message.timestamp)} ago
</p>
</div>
</div>
))}
{isTyping && (
<div className="flex justify-start">
<div className="bg-gray-100 rounded-lg p-3">
<div className="flex space-x-1">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }} />
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }} />
</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
{/* Input */}
<div className="border-t p-4">
<div className="flex space-x-2">
<Input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Ask me anything about your real estate business..."
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
}}
className="flex-1"
/>
<Button
onClick={handleSendMessage}
disabled={!inputValue.trim() || isTyping}
size="sm"
>
<Send className="h-4 w-4" />
</Button>
</div>
{/* Quick Actions */}
<div className="flex flex-wrap gap-2 mt-2">
<Button
variant="outline"
size="sm"
onClick={() => setInputValue('Show me my hot leads')}
className="text-xs"
>
Hot Leads
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setInputValue('What deals need attention?')}
className="text-xs"
>
Deal Health
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setInputValue('Generate market report')}
className="text-xs"
>
Market Report
</Button>
</div>
</div>
</div>
);
}
interface ChatMessage {
id: string;
type: 'user' | 'assistant' | 'error';
content: string;
recommendations?: AIRecommendation[];
actions?: RecommendedAction[];
timestamp: Date;
}
Recommendation Dashboard
AI Insights Widget
// components/ai-insights-dashboard.tsx
export function AIInsightsDashboard() {
const { data: insights } = useQuery({
queryKey: ['ai-insights'],
queryFn: () => fetch('/api/ai/insights/dashboard').then(res => res.json()),
refetchInterval: 5 * 60 * 1000 // Refresh every 5 minutes
});
const [filter, setFilter] = useState<'all' | 'urgent' | 'opportunities' | 'improvements'>('all');
const filteredInsights = useMemo(() => {
if (!insights?.recommendations) return [];
switch (filter) {
case 'urgent':
return insights.recommendations.filter((r: AIRecommendation) => r.priority === 'HIGH');
case 'opportunities':
return insights.recommendations.filter((r: AIRecommendation) => r.type === 'OPPORTUNITY');
case 'improvements':
return insights.recommendations.filter((r: AIRecommendation) => r.type === 'IMPROVEMENT');
default:
return insights.recommendations;
}
}, [insights, filter]);
return (
<div className="space-y-6">
{/* AI Health Score */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Brain className="h-5 w-5" />
<span>AI Business Health Score</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center space-x-4">
<div className="relative w-16 h-16">
<CircularProgressbar
value={insights?.healthScore || 0}
text={`${insights?.healthScore || 0}`}
styles={buildStyles({
textSize: '24px',
pathColor: getHealthScoreColor(insights?.healthScore || 0),
textColor: getHealthScoreColor(insights?.healthScore || 0)
})}
/>
</div>
<div className="flex-1">
<p className="text-sm text-gray-600">
{getHealthScoreDescription(insights?.healthScore || 0)}
</p>
<div className="mt-2 flex space-x-4 text-xs">
<div>
<span className="text-gray-500">Pipeline Health:</span>
<span className="ml-1 font-medium">{insights?.pipelineHealth || 0}/100</span>
</div>
<div>
<span className="text-gray-500">Activity Score:</span>
<span className="ml-1 font-medium">{insights?.activityScore || 0}/100</span>
</div>
<div>
<span className="text-gray-500">Engagement:</span>
<span className="ml-1 font-medium">{insights?.engagementScore || 0}/100</span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Quick Stats */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">Active Recommendations</p>
<p className="text-2xl font-bold">{insights?.stats?.activeRecommendations || 0}</p>
</div>
<Lightbulb className="h-8 w-8 text-yellow-500" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">Opportunities Identified</p>
<p className="text-2xl font-bold">{insights?.stats?.opportunities || 0}</p>
</div>
<Target className="h-8 w-8 text-green-500" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">Actions Taken</p>
<p className="text-2xl font-bold">{insights?.stats?.actionsTaken || 0}</p>
</div>
<CheckCircle className="h-8 w-8 text-blue-500" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">Success Rate</p>
<p className="text-2xl font-bold">{insights?.stats?.successRate || 0}%</p>
</div>
<TrendingUp className="h-8 w-8 text-purple-500" />
</div>
</CardContent>
</Card>
</div>
{/* Filters */}
<div className="flex space-x-2">
{(['all', 'urgent', 'opportunities', 'improvements'] as const).map((filterType) => (
<Button
key={filterType}
variant={filter === filterType ? 'default' : 'outline'}
size="sm"
onClick={() => setFilter(filterType)}
className="capitalize"
>
{filterType === 'all' ? 'All' : filterType}
</Button>
))}
</div>
{/* Recommendations List */}
<div className="space-y-4">
{filteredInsights.map((recommendation: AIRecommendation) => (
<Card key={recommendation.id} className="hover:shadow-md transition-shadow">
<CardContent className="pt-4">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center space-x-2 mb-2">
<Badge
variant={recommendation.priority === 'HIGH' ? 'destructive' : 'secondary'}
>
{recommendation.priority}
</Badge>
<Badge variant="outline">
{recommendation.type.replace('_', ' ')}
</Badge>
</div>
<h3 className="font-semibold mb-1">{recommendation.title}</h3>
<p className="text-sm text-gray-600 mb-3">
{recommendation.description}
</p>
<div className="flex items-center space-x-4 text-xs text-gray-500 mb-3">
<div className="flex items-center space-x-1">
<Target className="h-3 w-3" />
<span>Impact: {recommendation.estimatedImpact}</span>
</div>
<div className="flex items-center space-x-1">
<Clock className="h-3 w-3" />
<span>Effort: {recommendation.estimatedEffort}</span>
</div>
{recommendation.deadline && (
<div className="flex items-center space-x-1">
<Calendar className="h-3 w-3" />
<span>Due: {formatDistanceToNow(recommendation.deadline)}</span>
</div>
)}
</div>
<div className="text-xs text-gray-600 bg-gray-50 p-2 rounded">
<strong>Reasoning:</strong> {recommendation.reasoning}
</div>
</div>
<div className="ml-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{recommendation.actions.map((action, index) => (
<DropdownMenuItem key={index}>
{action.label}
</DropdownMenuItem>
))}
<DropdownMenuSeparator />
<DropdownMenuItem>Dismiss</DropdownMenuItem>
<DropdownMenuItem>Snooze</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
);
}
const getHealthScoreColor = (score: number): string => {
if (score >= 80) return '#10b981';
if (score >= 60) return '#f59e0b';
return '#ef4444';
};
const getHealthScoreDescription = (score: number): string => {
if (score >= 80) return 'Your business is performing excellently! Keep up the great work.';
if (score >= 60) return 'Good performance with room for improvement in some areas.';
return 'Several areas need attention to optimize your business performance.';
};
Next Steps
Predictive Models
Explore market prediction and forecasting capabilities
Communication Hub
See how AI integrates with communication features
Analytics Dashboard
Comprehensive business analytics and reporting
The AI Assistant transforms real estate productivity by providing intelligent, context-aware recommendations and automating routine decision-making processes.