feat: Implement AI Assistance Portal with types and components for student requests and insights

This commit is contained in:
defiQUG
2025-10-05 05:14:58 -07:00
parent 914c4180b5
commit 47d913cf34
9 changed files with 6761 additions and 101 deletions

683
PHASE3_AI_IMPLEMENTATION.md Normal file
View File

@@ -0,0 +1,683 @@
# Phase 3 Implementation Plan: Enterprise AI Integration
## 🤖 Priority 1: AI-Powered Student Assistance Matching
### Implementation Strategy
This document outlines the immediate next steps to begin Phase 3 implementation with the AI-powered student assistance matching engine - the highest impact feature for immediate organizational transformation.
### Technical Architecture
#### 1. AI Model Infrastructure
```typescript
// src/ai/StudentMatchingEngine.ts
interface StudentRequest {
id: string
studentId: string
description: string
category: AssistanceCategory
urgency: UrgencyLevel
location: GeographicLocation
constraints: RequestConstraints
deadline?: Date
}
interface MatchResult {
resourceId: string
confidenceScore: number
estimatedImpact: number
logisticalComplexity: number
volunteerMatch?: VolunteerAssignment
estimatedCost: number
fulfillmentTimeline: Timeline
}
class StudentAssistanceAI {
private vectorizer: TextVectorizer
private matchingModel: tf.LayersModel
private impactPredictor: tf.LayersModel
constructor() {
this.initializeModels()
}
private async initializeModels() {
// Load pre-trained TensorFlow.js models
this.matchingModel = await tf.loadLayersModel('/models/student-matching.json')
this.impactPredictor = await tf.loadLayersModel('/models/impact-prediction.json')
this.vectorizer = new TextVectorizer()
}
async processRequest(request: StudentRequest): Promise<MatchResult[]> {
// 1. Analyze and vectorize request
const analysis = await this.analyzeRequest(request)
// 2. Find optimal resource matches
const candidates = await this.findCandidateResources(analysis)
// 3. Score and rank matches
const scoredMatches = await this.scoreMatches(candidates, analysis)
// 4. Predict impact and logistics
const enrichedMatches = await this.enrichWithPredictions(scoredMatches)
return enrichedMatches.sort((a, b) => b.confidenceScore - a.confidenceScore)
}
private async analyzeRequest(request: StudentRequest): Promise<RequestAnalysis> {
// NLP analysis of request description
const textVector = await this.vectorizer.encode(request.description)
// Extract key features
const features = {
categoryVector: this.encodeCategoryVector(request.category),
urgencyScore: this.encodeUrgency(request.urgency),
locationVector: this.encodeLocation(request.location),
temporalFeatures: this.encodeTemporalConstraints(request.constraints),
semanticFeatures: textVector
}
return {
primaryNeeds: await this.extractNeedCategories(textVector),
urgencyScore: features.urgencyScore,
complexityEstimate: await this.estimateComplexity(features),
resourceRequirements: await this.estimateResources(features)
}
}
private async findCandidateResources(analysis: RequestAnalysis): Promise<ResourceCandidate[]> {
// Query available resources based on analysis
const availableResources = await ResourceManager.getAvailableResources({
categories: analysis.primaryNeeds,
location: analysis.locationConstraints,
availability: analysis.timeConstraints
})
// Add volunteer availability
const volunteerCandidates = await VolunteerManager.getAvailableVolunteers({
skills: analysis.requiredSkills,
location: analysis.locationConstraints,
availability: analysis.timeConstraints
})
return this.combineResourcesAndVolunteers(availableResources, volunteerCandidates)
}
private async scoreMatches(candidates: ResourceCandidate[], analysis: RequestAnalysis): Promise<ScoredMatch[]> {
const scoredMatches: ScoredMatch[] = []
for (const candidate of candidates) {
// Prepare input tensor for ML model
const inputFeatures = this.prepareFeaturesForML(candidate, analysis)
// Get confidence score from trained model
const prediction = this.matchingModel.predict(inputFeatures) as tf.Tensor
const confidenceScore = await prediction.data()
scoredMatches.push({
...candidate,
confidenceScore: confidenceScore[0],
reasoningFactors: this.explainScore(candidate, analysis)
})
prediction.dispose() // Clean up memory
}
return scoredMatches
}
async predictImpact(match: ScoredMatch): Promise<ImpactPrediction> {
// Use impact prediction model
const impactFeatures = this.prepareImpactFeatures(match)
const impactPrediction = this.impactPredictor.predict(impactFeatures) as tf.Tensor
const impactScore = await impactPrediction.data()
impactPrediction.dispose()
return {
estimatedBeneficiaries: Math.round(impactScore[0]),
successProbability: impactScore[1],
timeToImpact: impactScore[2],
sustainabilityScore: impactScore[3],
rippleEffects: await this.predictRippleEffects(match)
}
}
}
```
#### 2. Real-time Processing Pipeline
```typescript
// src/ai/ProcessingPipeline.ts
class RealTimeProcessingPipeline {
private queue: Queue<StudentRequest>
private aiEngine: StudentAssistanceAI
private notificationService: NotificationService
constructor() {
this.queue = new Queue('assistance-requests')
this.aiEngine = new StudentAssistanceAI()
this.setupQueueProcessors()
}
private setupQueueProcessors() {
// Process requests as they come in
this.queue.process('analyze-request', 5, async (job) => {
const request = job.data as StudentRequest
try {
// AI analysis and matching
const matches = await this.aiEngine.processRequest(request)
// Auto-approval for high-confidence matches
if (matches[0]?.confidenceScore > 0.9) {
await this.autoApproveRequest(request, matches[0])
} else {
await this.routeForHumanReview(request, matches)
}
// Update real-time dashboard
await this.updateDashboard(request.id, matches)
} catch (error) {
await this.handleProcessingError(request, error)
}
})
}
async submitRequest(request: StudentRequest): Promise<string> {
// Add to processing queue
const job = await this.queue.add('analyze-request', request, {
priority: this.calculatePriority(request.urgency),
attempts: 3,
backoff: 'exponential'
})
// Immediate acknowledgment
await this.sendAcknowledgment(request)
return job.id
}
private async autoApproveRequest(request: StudentRequest, match: MatchResult): Promise<void> {
// Create assistance assignment
const assignment = await AssignmentManager.createAssignment({
requestId: request.id,
resourceId: match.resourceId,
volunteerId: match.volunteerMatch?.id,
scheduledDate: match.fulfillmentTimeline.startDate,
estimatedCost: match.estimatedCost,
approvalStatus: 'auto-approved',
confidence: match.confidenceScore
})
// Notify all stakeholders
await Promise.all([
this.notificationService.notifyStudent(request.studentId, assignment),
this.notificationService.notifyVolunteer(assignment.volunteerId, assignment),
this.notificationService.notifyCoordinators(assignment),
this.notificationService.updateDonors(assignment.estimatedCost)
])
// Track for learning
await this.trackDecision(request, match, 'auto-approved')
}
private async routeForHumanReview(request: StudentRequest, matches: MatchResult[]): Promise<void> {
// Determine best reviewer based on request type and matches
const reviewer = await this.selectOptimalReviewer(request, matches)
// Create review assignment
const reviewTask = await ReviewManager.createReviewTask({
requestId: request.id,
assignedTo: reviewer.id,
aiRecommendations: matches,
priority: this.calculateReviewPriority(request, matches),
deadline: this.calculateReviewDeadline(request.urgency)
})
// Notify reviewer with AI insights
await this.notificationService.notifyReviewer(reviewer, reviewTask, {
aiConfidence: matches[0]?.confidenceScore,
recommendedAction: this.generateRecommendation(matches),
riskFactors: this.identifyRiskFactors(request, matches)
})
}
}
```
#### 3. Learning and Improvement System
```typescript
// src/ai/LearningSystem.ts
class ContinuousLearningSystem {
private feedbackCollector: FeedbackCollector
private modelTrainer: ModelTrainer
async collectOutcome(assignmentId: string, outcome: AssignmentOutcome): Promise<void> {
// Collect real-world outcomes for model improvement
const assignment = await AssignmentManager.getById(assignmentId)
const originalRequest = await RequestManager.getById(assignment.requestId)
const aiDecision = await this.getOriginalAIDecision(assignmentId)
const trainingExample = {
features: aiDecision.inputFeatures,
prediction: aiDecision.prediction,
actualOutcome: {
success: outcome.successful,
impactAchieved: outcome.measuredImpact,
costActual: outcome.actualCost,
timeToComplete: outcome.completionTime,
satisfactionScore: outcome.satisfactionRatings
}
}
// Add to training dataset
await this.feedbackCollector.addTrainingExample(trainingExample)
// Trigger model retraining if sufficient new data
if (await this.shouldRetrain()) {
await this.scheduleRetraining()
}
}
async identifyImprovementOpportunities(): Promise<ImprovementInsight[]> {
const insights: ImprovementInsight[] = []
// Analyze prediction accuracy trends
const accuracyTrends = await this.analyzeAccuracyTrends()
if (accuracyTrends.declining) {
insights.push({
type: 'accuracy-decline',
severity: accuracyTrends.severity,
recommendation: 'Model retraining recommended',
estimatedImpact: 'High'
})
}
// Identify bias in predictions
const biasAnalysis = await this.analyzeBias()
if (biasAnalysis.significantBias) {
insights.push({
type: 'prediction-bias',
biasFactors: biasAnalysis.factors,
recommendation: 'Implement bias correction',
estimatedImpact: 'Critical'
})
}
// Find optimization opportunities
const optimizations = await this.findOptimizations()
insights.push(...optimizations)
return insights
}
private async scheduleRetraining(): Promise<void> {
// Schedule model retraining job
const retrainingJob = await this.queue.add('retrain-models', {
modelTypes: ['matching', 'impact-prediction'],
trainingDataVersion: await this.getLatestDataVersion(),
validationSplit: 0.2,
hyperparameterTuning: true
}, {
priority: 1,
delay: 60000 // Start in 1 minute
})
await this.notifyAdministrators({
message: 'AI model retraining initiated',
jobId: retrainingJob.id,
estimatedDuration: '45-60 minutes'
})
}
}
```
#### 4. Frontend Integration Components
```typescript
// src/components/AIAssistancePortal.tsx
import React, { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
interface AIAssistancePortalProps {
userRole: 'student' | 'coordinator' | 'admin'
}
export function AIAssistancePortal({ userRole }: AIAssistancePortalProps) {
const [requests, setRequests] = useState<StudentRequest[]>([])
const [aiInsights, setAIInsights] = useState<AIInsight[]>([])
const [processing, setProcessing] = useState(false)
useEffect(() => {
// Real-time updates via WebSocket
const ws = new WebSocket(`wss://api.miraclesinmotion.org/ai-updates`)
ws.onmessage = (event) => {
const update = JSON.parse(event.data)
handleRealTimeUpdate(update)
}
return () => ws.close()
}, [])
const handleRealTimeUpdate = (update: AIUpdate) => {
switch (update.type) {
case 'request-processed':
setRequests(prev => prev.map(r =>
r.id === update.requestId
? { ...r, status: update.status, aiRecommendations: update.recommendations }
: r
))
break
case 'new-insight':
setAIInsights(prev => [update.insight, ...prev.slice(0, 9)])
break
case 'auto-approval':
// Show success notification
showNotification({
type: 'success',
title: 'Request Auto-Approved',
message: `High-confidence match found for ${update.studentName}`,
action: {
label: 'View Details',
onClick: () => navigateToRequest(update.requestId)
}
})
break
}
}
return (
<div className="ai-assistance-portal">
{/* AI Insights Panel */}
<motion.div
className="insights-panel"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Brain className="w-5 h-5 text-purple-500" />
AI Insights
</h3>
<AnimatePresence mode="popLayout">
{aiInsights.map((insight) => (
<motion.div
key={insight.id}
className="insight-card p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg mb-2"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
whileHover={{ scale: 1.02 }}
>
<div className="flex items-start gap-2">
<div className={`w-2 h-2 rounded-full mt-2 ${getInsightColor(insight.type)}`} />
<div className="flex-1">
<p className="font-medium text-sm">{insight.title}</p>
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">
{insight.description}
</p>
{insight.confidence && (
<div className="mt-2 flex items-center gap-2">
<div className="w-20 bg-gray-200 rounded-full h-1">
<div
className="bg-purple-500 h-1 rounded-full transition-all"
style={{ width: `${insight.confidence * 100}%` }}
/>
</div>
<span className="text-xs font-medium">
{Math.round(insight.confidence * 100)}% confidence
</span>
</div>
)}
</div>
</div>
</motion.div>
))}
</AnimatePresence>
</motion.div>
{/* Request Processing Interface */}
<div className="request-processing">
<h3 className="text-lg font-semibold mb-4">Smart Request Processing</h3>
{requests.map((request) => (
<RequestCard
key={request.id}
request={request}
onApprove={handleApproval}
onModify={handleModification}
showAIRecommendations={userRole !== 'student'}
/>
))}
</div>
{/* Performance Metrics */}
<AIPerformanceMetrics />
</div>
)
}
function RequestCard({ request, onApprove, onModify, showAIRecommendations }: RequestCardProps) {
return (
<motion.div
className="request-card p-4 border rounded-lg mb-4"
whileHover={{ y: -2, boxShadow: "0 4px 12px rgba(0,0,0,0.1)" }}
>
<div className="flex justify-between items-start mb-3">
<div>
<h4 className="font-medium">{request.description}</h4>
<p className="text-sm text-gray-500">
Student: {request.studentName} {formatDistanceToNow(request.submittedAt)} ago
</p>
</div>
<UrgencyBadge urgency={request.urgency} />
</div>
{showAIRecommendations && request.aiRecommendations && (
<motion.div
className="ai-recommendations bg-blue-50 dark:bg-blue-900/20 p-3 rounded-lg mb-3"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
>
<div className="flex items-center gap-2 mb-2">
<Cpu className="w-4 h-4 text-blue-500" />
<span className="text-sm font-medium text-blue-700 dark:text-blue-300">
AI Recommendation
</span>
<ConfidenceIndicator confidence={request.aiRecommendations[0].confidenceScore} />
</div>
<div className="space-y-2">
{request.aiRecommendations.slice(0, 2).map((rec, index) => (
<div key={index} className="flex justify-between items-center text-sm">
<span>{rec.resourceName}</span>
<div className="flex items-center gap-2">
<span className="text-green-600">${rec.estimatedCost}</span>
<span className="text-blue-600">{rec.fulfillmentTimeline}</span>
</div>
</div>
))}
</div>
<div className="mt-3 flex gap-2">
<motion.button
onClick={() => onApprove(request.id, request.aiRecommendations[0])}
className="btn-primary text-xs px-3 py-1"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Approve AI Recommendation
</motion.button>
<button
onClick={() => onModify(request.id)}
className="btn-secondary text-xs px-3 py-1"
>
Modify
</button>
</div>
</motion.div>
)}
<div className="flex justify-between items-center">
<div className="flex gap-2">
<CategoryBadge category={request.category} />
<LocationBadge location={request.location} />
</div>
<ActionButtons request={request} />
</div>
</motion.div>
)
}
function AIPerformanceMetrics() {
const [metrics, setMetrics] = useState<AIMetrics>()
useEffect(() => {
// Fetch AI performance metrics
fetchAIMetrics().then(setMetrics)
}, [])
if (!metrics) return null
return (
<div className="ai-performance-metrics mt-6">
<h4 className="text-md font-semibold mb-3">AI Performance</h4>
<div className="grid grid-cols-2 gap-4">
<MetricCard
title="Accuracy Rate"
value={`${(metrics.accuracyRate * 100).toFixed(1)}%`}
trend={metrics.accuracyTrend}
color="green"
/>
<MetricCard
title="Avg Processing Time"
value={`${metrics.avgProcessingTime}s`}
trend={metrics.speedTrend}
color="blue"
/>
<MetricCard
title="Auto-Approval Rate"
value={`${(metrics.autoApprovalRate * 100).toFixed(1)}%`}
trend={metrics.automationTrend}
color="purple"
/>
<MetricCard
title="Impact Accuracy"
value={`${(metrics.impactPredictionAccuracy * 100).toFixed(1)}%`}
trend={metrics.impactTrend}
color="orange"
/>
</div>
</div>
)
}
```
## 🚀 Implementation Timeline (Weeks 1-2)
### Week 1: Foundation Setup
**Days 1-2: Infrastructure**
- Set up TensorFlow.js environment
- Create AI model loading infrastructure
- Implement basic text vectorization system
- Set up Redis for caching ML predictions
**Days 3-4: Core AI Engine**
- Build `StudentAssistanceAI` class structure
- Implement request analysis pipeline
- Create resource matching algorithms
- Add confidence scoring system
**Days 5-7: Integration Layer**
- Create processing pipeline with queue system
- Implement WebSocket for real-time updates
- Build AI portal React components
- Add notification integration
### Week 2: Enhancement & Testing
**Days 8-10: Learning System**
- Implement feedback collection
- Create model retraining pipeline
- Add performance monitoring
- Build improvement insights system
**Days 11-12: Frontend Polish**
- Complete AI portal interface
- Add visualizations for AI confidence
- Implement real-time updates
- Create admin controls for AI parameters
**Days 13-14: Testing & Optimization**
- Comprehensive testing with sample data
- Performance optimization
- Security review
- Documentation completion
## 📊 Expected Impact
### Immediate Benefits (Week 2)
- **50% faster** request processing
- **30% improvement** in match accuracy
- **Real-time insights** for coordinators
- **Automated low-risk approvals**
### Short-term Benefits (Month 1)
- **75% reduction** in manual review time
- **90% accuracy** in resource matching
- **Predictive analytics** for resource planning
- **Continuous learning** from outcomes
### Long-term Benefits (3-6 months)
- **AI-driven optimization** of entire operation
- **Predictive demand forecasting**
- **Automated workflow recommendations**
- **Data-driven program improvements**
## 💻 Technical Requirements
### Dependencies to Add
```bash
npm install @tensorflow/tfjs @tensorflow/tfjs-node
npm install bull redis ioredis
npm install ws socket.io-client
npm install natural compromise
npm install ml-matrix
```
### Environment Setup
```bash
# Redis for caching and queues
docker run -d -p 6379:6379 redis:alpine
# GPU support for faster ML (optional)
npm install @tensorflow/tfjs-node-gpu
```
### Model Files Structure
```
/public/models/
├── student-matching.json # Core matching model
├── student-matching.bin # Model weights
├── impact-prediction.json # Impact prediction model
├── impact-prediction.bin # Impact weights
└── text-vectorizer.json # Text processing config
```
## 🎯 Success Metrics for Phase 3A
### Technical Metrics
- **Model Accuracy**: >85% initial, >90% after learning
- **Processing Speed**: <2 seconds per request
- **System Uptime**: >99.5%
- **Auto-Approval Rate**: 60-70% of requests
### Business Metrics
- **Coordinator Efficiency**: 50% time savings
- **Student Satisfaction**: >4.5/5 rating
- **Resource Utilization**: 25% improvement
- **Response Time**: <2 hours for urgent requests
Ready to begin Phase 3 AI implementation! This foundation will revolutionize how Miracles in Motion matches students with resources, creating unprecedented efficiency and impact measurement capabilities.

668
PHASE3_ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,668 @@
# Phase 3: Enterprise Nonprofit Platform Architecture
## 🏗️ System Architecture Overview
### Core Enterprise Components
#### 1. Microservices Backend Architecture
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ API Gateway │ │ Load Balancer │ │ CDN Network │
│ (Kong/Nginx) │────│ (HAProxy) │────│ (CloudFlare) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Authentication │ │ Donation │ │ Volunteer │
│ Service │ │ Service │ │ Service │
│ (Auth0/JWT) │ │ (Stripe API) │ │ (Scheduling) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CRM Service │ │ Analytics Svc │ │ Notification │
│ (Salesforce) │ │ (Real-time) │ │ Service │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
#### 2. Data Architecture
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PostgreSQL │ │ Redis │ │ Elasticsearch │
│ (Primary DB) │────│ (Cache) │────│ (Search) │
│ Multi-tenant │ │ Sessions │ │ Analytics │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Data Lake │ │ ML Pipeline │ │ Reporting │
│ (AWS S3) │ │ (TensorFlow) │ │ (Tableau) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## 🤖 AI & Machine Learning Layer
### Smart Assistance Matching Engine
```typescript
interface AssistanceAI {
matchStudent(request: StudentRequest): Promise<MatchResult[]>
predictNeeds(studentProfile: StudentProfile): Promise<PredictionResult>
optimizeResources(availableResources: Resource[]): Promise<OptimizationPlan>
}
class StudentAssistanceAI {
private mlModel: TensorFlow.LayersModel
private vectorizer: TextVectorizer
async matchStudent(request: StudentRequest): Promise<MatchResult[]> {
// 1. Vectorize request text and categorize needs
const requestVector = await this.vectorizer.encode(request.description)
const category = await this.classifyNeed(requestVector)
// 2. Find similar past successful matches
const historicalMatches = await this.findSimilarMatches(requestVector)
// 3. Score available resources
const scoredResources = await this.scoreResources(category, historicalMatches)
// 4. Consider logistics (location, timing, volunteer availability)
return this.optimizeMatches(scoredResources, request.constraints)
}
async predictImpact(intervention: Intervention): Promise<ImpactPrediction> {
// ML model trained on historical data to predict intervention success
const features = this.extractFeatures(intervention)
const prediction = await this.mlModel.predict(features)
return {
successProbability: prediction.dataSync()[0],
estimatedBeneficiaries: Math.round(prediction.dataSync()[1]),
timeToImpact: prediction.dataSync()[2],
confidenceInterval: [
prediction.dataSync()[3],
prediction.dataSync()[4]
]
}
}
}
```
### Donor Engagement Intelligence
```typescript
class DonorEngagementAI {
async predictDonationTiming(donor: DonorProfile): Promise<OptimalTiming> {
// Analyze donor history, external events, seasonal patterns
const features = {
pastDonations: donor.donationHistory,
emailEngagement: donor.emailMetrics,
seasonality: this.getSeasonalFactors(),
externalEvents: await this.getRelevantEvents(donor.interests)
}
return {
nextOptimalAsk: new Date(prediction.nextAskDate),
suggestedAmount: prediction.suggestedAmount,
preferredChannel: prediction.channel,
confidence: prediction.confidence
}
}
async generatePersonalizedContent(donor: DonorProfile): Promise<PersonalizedContent> {
// Use GPT-style model fine-tuned on successful donor communications
const context = {
donorValues: donor.motivations,
pastSupport: donor.supportedPrograms,
communicationStyle: donor.preferences
}
return {
emailSubject: await this.generateSubject(context),
bodyContent: await this.generateBody(context),
callToAction: await this.generateCTA(context),
imageRecommendations: await this.selectImages(context)
}
}
}
```
## 🔄 Advanced Workflow Automation
### Intelligent Request Processing
```typescript
class AutomatedRequestProcessor {
private aiMatcher: StudentAssistanceAI
private workflowEngine: WorkflowEngine
async processRequest(request: AssistanceRequest): Promise<ProcessingResult> {
// 1. Auto-categorization and urgency scoring
const analysis = await this.analyzeRequest(request)
// 2. Fraud/spam detection
const securityCheck = await this.performSecurityCheck(request)
if (!securityCheck.isValid) {
return this.handleSuspiciousRequest(request, securityCheck)
}
// 3. Auto-approval for routine requests
if (analysis.confidence > 0.95 && analysis.urgency < 0.3) {
return await this.autoApprove(request, analysis)
}
// 4. Route to appropriate human reviewer
return await this.routeForReview(request, analysis)
}
private async autoApprove(request: AssistanceRequest, analysis: RequestAnalysis) {
// Find optimal resource match
const matches = await this.aiMatcher.matchStudent(request)
const bestMatch = matches[0]
// Auto-assign volunteer and schedule delivery
const assignment = await this.assignVolunteer(bestMatch)
await this.scheduleDelivery(assignment)
// Generate communications
await this.notifyStudent(request, assignment)
await this.notifyVolunteer(assignment)
await this.notifyDonors(request, assignment.estimatedCost)
return {
status: 'auto-approved',
assignment,
estimatedFulfillment: assignment.scheduledDate
}
}
}
```
### Smart Donation Workflows
```typescript
class SmartDonationWorkflow {
async processDonation(donation: Donation): Promise<DonationResult> {
// 1. Real-time fraud detection
const fraudScore = await this.assessFraudRisk(donation)
// 2. Tax optimization suggestions
const taxAdvice = await this.generateTaxAdvice(donation)
// 3. Impact prediction and allocation
const impactForecast = await this.predictImpact(donation.amount)
// 4. Auto-generate personalized thank you
const thankYou = await this.generateThankYou(donation, impactForecast)
// 5. Schedule follow-up engagement
await this.scheduleFollowUps(donation, impactForecast)
return {
transactionId: donation.id,
impactForecast,
taxAdvice,
thankYou,
nextEngagement: await this.getNextEngagement(donation.donor)
}
}
async optimizeRecurringGifts(donor: DonorProfile): Promise<OptimizationPlan> {
// Analyze optimal frequency and amounts based on donor behavior
const analysis = await this.analyzeDonorCapacity(donor)
return {
recommendedFrequency: analysis.optimalFrequency,
suggestedAmount: analysis.optimalAmount,
projectedAnnualIncrease: analysis.growthPotential,
retentionProbability: analysis.retentionRisk
}
}
}
```
## 🏢 Enterprise Integration Hub
### CRM Integration Layer
```typescript
interface CRMConnector {
// Salesforce Nonprofit Cloud Integration
salesforce: {
contacts: ContactManager
opportunities: OpportunityManager
campaigns: CampaignManager
grants: GrantManager
}
// HubSpot Nonprofit Integration
hubspot: {
contacts: HubSpotContactAPI
deals: HubSpotDealsAPI
workflows: HubSpotWorkflowAPI
}
}
class SalesforceIntegration implements CRMConnector['salesforce'] {
async syncDonor(donor: DonorProfile): Promise<SalesforceContact> {
// Bi-directional sync with Salesforce NPSP
const contact = await this.salesforceAPI.createOrUpdateContact({
firstName: donor.firstName,
lastName: donor.lastName,
email: donor.email,
phone: donor.phone,
donorLevel: this.calculateDonorLevel(donor.totalGiving),
lastGift: donor.lastDonation,
lifetimeGiving: donor.totalGiving,
customFields: {
preferredCommunication: donor.communicationPreference,
volunteerInterest: donor.volunteerInterest,
programInterests: donor.programInterests
}
})
// Sync donation history
await this.syncDonationHistory(donor.id, contact.id)
return contact
}
async createOpportunity(donation: PendingDonation): Promise<Opportunity> {
return await this.salesforceAPI.createOpportunity({
accountId: donation.donor.salesforceId,
amount: donation.amount,
stageName: 'Pledged',
closeDate: donation.expectedDate,
recordType: 'Donation',
campaign: donation.campaign?.salesforceId,
customFields: {
donationSource: donation.source,
paymentMethod: donation.paymentMethod,
isRecurring: donation.recurring
}
})
}
}
```
### Financial System Integration
```typescript
class QuickBooksIntegration {
async recordDonation(donation: CompletedDonation): Promise<QBTransaction> {
// Auto-categorize donation for proper bookkeeping
const account = await this.categorizeRevenue(donation)
const transaction = await this.qbAPI.createTransaction({
type: 'Income',
account: account.id,
amount: donation.netAmount,
description: `Online donation - ${donation.donor.name}`,
class: donation.program?.qbClass,
customer: await this.getOrCreateDonor(donation.donor),
customFields: {
campaignId: donation.campaign?.id,
processingFee: donation.processingFee,
grossAmount: donation.amount
}
})
// Auto-generate receipt
await this.generateReceipt(donation, transaction.id)
return transaction
}
async reconcilePayments(startDate: Date, endDate: Date): Promise<ReconciliationReport> {
// Auto-match bank deposits with recorded donations
const bankDeposits = await this.getBankDeposits(startDate, endDate)
const recordedDonations = await this.getRecordedDonations(startDate, endDate)
return this.performReconciliation(bankDeposits, recordedDonations)
}
}
```
## 📈 Advanced Analytics & Intelligence
### Real-time Intelligence Dashboard
```typescript
class AdvancedAnalyticsDashboard {
async getRealTimeMetrics(): Promise<LiveMetrics> {
return {
// Live donation tracking
donations: {
todayTotal: await this.getTodayDonations(),
hourlyTrend: await this.getHourlyTrend(),
conversionRate: await this.getLiveConversionRate(),
averageGift: await this.getAverageGift(),
recurringSignups: await this.getRecurringSignups()
},
// Volunteer engagement
volunteers: {
activeToday: await this.getActiveVolunteers(),
pendingAssignments: await this.getPendingAssignments(),
completionRate: await this.getCompletionRate(),
responseTime: await this.getAverageResponseTime()
},
// Student assistance
students: {
requestsToday: await this.getTodayRequests(),
fulfillmentRate: await this.getFulfillmentRate(),
averageResponseTime: await this.getAverageProcessingTime(),
impactDelivered: await this.getTodayImpact()
},
// Predictive insights
predictions: {
monthEndProjection: await this.projectMonthEnd(),
seasonalForecast: await this.getSeasonalForecast(),
churnRisk: await this.getChurnRisk(),
growthOpportunities: await this.getGrowthOpportunities()
}
}
}
async generateInsights(): Promise<AIInsight[]> {
const insights: AIInsight[] = []
// Anomaly detection
const anomalies = await this.detectAnomalies()
insights.push(...anomalies.map(a => ({
type: 'anomaly',
title: a.title,
description: a.description,
severity: a.severity,
actionItems: a.suggestedActions
})))
// Optimization opportunities
const optimizations = await this.findOptimizations()
insights.push(...optimizations.map(o => ({
type: 'optimization',
title: o.title,
description: o.description,
potentialImpact: o.estimatedBenefit,
actionItems: o.recommendedActions
})))
// Trend analysis
const trends = await this.analyzeTrends()
insights.push(...trends.map(t => ({
type: 'trend',
title: t.title,
description: t.description,
trajectory: t.direction,
confidence: t.confidence
})))
return insights
}
}
```
### Predictive Analytics Engine
```typescript
class PredictiveAnalytics {
async forecastDonations(timeframe: DateRange): Promise<DonationForecast> {
// Multi-model ensemble for accurate predictions
const models = [
await this.seasonalModel.predict(timeframe),
await this.trendModel.predict(timeframe),
await this.eventBasedModel.predict(timeframe),
await this.economicModel.predict(timeframe)
]
const ensemble = this.combineModels(models)
return {
expectedTotal: ensemble.amount,
confidenceInterval: ensemble.interval,
breakdown: {
new: ensemble.newDonors,
recurring: ensemble.recurringDonors,
major: ensemble.majorGifts
},
riskFactors: await this.identifyRisks(timeframe),
opportunities: await this.identifyOpportunities(timeframe)
}
}
async predictVolunteerNeeds(): Promise<VolunteerForecast> {
// Predict volunteer capacity needs based on:
// - Student request patterns
// - Seasonal variations
// - Volunteer availability trends
// - Special events and campaigns
const demandForecast = await this.forecastStudentDemand()
const supplyForecast = await this.forecastVolunteerSupply()
return {
projectedGap: demandForecast.total - supplyForecast.available,
criticalPeriods: this.identifyCriticalPeriods(demandForecast, supplyForecast),
recruitmentNeeds: this.calculateRecruitmentNeeds(),
skillGaps: await this.identifySkillGaps()
}
}
}
```
## 🌐 Multi-Tenant Architecture
### Organization Management System
```typescript
class MultiTenantManager {
async createOrganization(config: OrganizationConfig): Promise<Organization> {
// Create isolated tenant environment
const org = await this.createTenant({
name: config.name,
subdomain: config.subdomain,
plan: config.subscriptionPlan,
features: this.getFeaturesByPlan(config.subscriptionPlan)
})
// Setup isolated database schema
await this.setupTenantSchema(org.id)
// Configure branding and customization
await this.setupBranding(org.id, config.branding)
// Initialize default workflows and settings
await this.initializeDefaults(org.id, config.organizationType)
return org
}
async scaleResources(orgId: string, metrics: UsageMetrics): Promise<ScalingPlan> {
// Auto-scale resources based on usage
const currentUsage = await this.getUsageMetrics(orgId)
const prediction = await this.predictGrowth(orgId, currentUsage)
if (prediction.needsScaling) {
return await this.implementScaling(orgId, prediction.requirements)
}
return { status: 'no-action-needed', currentCapacity: currentUsage }
}
}
```
### Data Isolation & Security
```typescript
class SecureDataManager {
async accessData(request: DataRequest): Promise<DataResponse> {
// Tenant isolation validation
await this.validateTenantAccess(request.userId, request.tenantId)
// Row-level security enforcement
const securityContext = await this.buildSecurityContext(request.userId)
// Encrypted data access
const encryptedData = await this.queryWithSecurity(
request.query,
securityContext
)
// Decrypt for authorized user
return this.decryptForUser(encryptedData, request.userId)
}
async auditAccess(request: DataRequest, response: DataResponse): Promise<void> {
await this.logAccess({
userId: request.userId,
tenantId: request.tenantId,
dataAccessed: response.dataTypes,
timestamp: new Date(),
ipAddress: request.ipAddress,
userAgent: request.userAgent
})
}
}
```
## 📱 Native Mobile Applications
### React Native Cross-Platform Apps
```typescript
// Mobile App Architecture
interface MobileApp {
authentication: OfflineAuthManager
synchronization: OfflineSyncManager
notifications: PushNotificationManager
geolocation: LocationServicesManager
camera: DocumentScanManager
}
class MiraclesMobileApp {
async initializeApp(): Promise<void> {
// Setup offline-first architecture
await this.setupOfflineStorage()
await this.initializeSyncEngine()
await this.setupPushNotifications()
// Initialize secure authentication
await this.setupBiometricAuth()
await this.configureSecureStorage()
}
async syncData(): Promise<SyncResult> {
// Intelligent sync based on connection quality
const connectionType = await this.detectConnectionType()
const syncStrategy = this.selectSyncStrategy(connectionType)
return await this.performSync(syncStrategy)
}
}
// Volunteer Mobile Features
class VolunteerMobileApp extends MiraclesMobileApp {
async acceptAssignment(assignmentId: string): Promise<void> {
// Offline-capable assignment acceptance
await this.queueAction('accept_assignment', { assignmentId })
await this.updateLocalState(assignmentId, 'accepted')
await this.notifyCoordinator(assignmentId)
}
async scanDeliveryReceipt(imageUri: string): Promise<ProcessedReceipt> {
// AI-powered receipt processing
const ocrResult = await this.processReceiptOCR(imageUri)
const extracted = await this.extractReceiptData(ocrResult)
return {
vendor: extracted.vendor,
amount: extracted.amount,
items: extracted.items,
date: extracted.date,
confidence: extracted.confidence
}
}
async trackDelivery(studentId: string): Promise<void> {
// Real-time delivery tracking with geofencing
const location = await this.getCurrentLocation()
await this.updateDeliveryProgress(studentId, location)
// Auto-complete when near student location
const distance = this.calculateDistance(location, student.location)
if (distance < 50) { // 50 meters
await this.promptDeliveryCompletion(studentId)
}
}
}
```
## 🔧 Implementation Roadmap
### Week 1-2: Foundation Infrastructure
- Microservices architecture setup
- Database partitioning and multi-tenancy
- API Gateway and load balancing
- Redis caching layer implementation
### Week 3-4: AI/ML Integration
- TensorFlow.js model deployment
- Student assistance matching engine
- Donor prediction models
- Natural language processing setup
### Week 5-6: Enterprise Integrations
- Salesforce NPSP connector
- QuickBooks API integration
- Email marketing platform sync
- Payment processor enhancements
### Week 7-8: Advanced Features
- Mobile app development
- Real-time collaboration tools
- Advanced reporting suite
- Workflow automation engine
### Week 9-10: Security & Compliance
- SOC 2 Type II implementation
- GDPR compliance framework
- Security audit and penetration testing
- Compliance reporting automation
## 💰 Investment & ROI Analysis
### Development Investment
- **Infrastructure**: $15K-25K (cloud setup, security)
- **Development**: $40K-60K (full-stack team for 10 weeks)
- **AI/ML Models**: $10K-15K (training data, compute)
- **Integration Costs**: $8K-12K (third-party APIs, licenses)
- **Total Investment**: $73K-112K
### Projected ROI (Year 1)
- **Operational Efficiency**: 75% reduction in manual tasks
- **Donation Increase**: 40% improvement in conversion rates
- **Cost Savings**: $45K annually in reduced overhead
- **Revenue Growth**: $150K+ additional donations
- **Net ROI**: 180-250% in first year
### Scalability Benefits
- **Multi-organization Platform**: $50K-100K annual revenue potential
- **Licensing Opportunities**: Additional revenue streams
- **Consulting Services**: Expert implementation support
- **Partnership Revenue**: Integration and referral income
## 🎯 Success Metrics
### Operational KPIs
- **Request Processing Time**: <2 hours average
- **Volunteer Response Rate**: >85%
- **Donor Retention Rate**: >75%
- **System Uptime**: 99.9%
- **Mobile App Rating**: >4.5 stars
### Business Impact KPIs
- **Students Served Growth**: 300% increase capacity
- **Volunteer Engagement**: 60% improvement
- **Donation Efficiency**: 45% better conversion
- **Administrative Overhead**: 70% reduction
- **Compliance Score**: 100% automated compliance
## 🚀 Next Phase Execution
Ready to begin Phase 3 implementation! The recommended starting approach:
1. **Begin with AI Foundation** - Implement the student assistance matching engine
2. **Parallel Infrastructure Setup** - Microservices and database architecture
3. **CRM Integration Priority** - Salesforce connector for immediate impact
4. **Mobile App Development** - Native apps for volunteers and staff
5. **Advanced Analytics** - Real-time intelligence dashboard
This Phase 3 architecture will position Miracles in Motion as the premier nonprofit technology platform, capable of serving as a model for the entire sector while dramatically increasing impact and efficiency.

2174
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -31,12 +31,24 @@
},
"homepage": "https://miraclesinmotion.org",
"dependencies": {
"@tensorflow/tfjs": "^4.22.0",
"bull": "^4.16.5",
"compromise": "^14.14.4",
"date-fns": "^4.1.0",
"framer-motion": "^10.16.16",
"ioredis": "^5.8.0",
"lucide-react": "^0.290.0",
"ml-matrix": "^6.12.1",
"natural": "^8.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"framer-motion": "^10.16.16",
"lucide-react": "^0.290.0"
"redis": "^5.8.3",
"socket.io-client": "^4.8.1",
"uuid": "^13.0.0",
"ws": "^8.18.3"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@typescript-eslint/eslint-plugin": "^6.10.0",
@@ -49,8 +61,7 @@
"gh-pages": "^6.0.0",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"@tailwindcss/typography": "^0.5.10",
"typescript": "^5.2.2",
"vite": "^4.5.0"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,418 @@
// Phase 3: Real-time Processing Pipeline for AI Assistance Matching
import type {
StudentRequest,
MatchResult,
AIUpdate,
AIInsight,
ProcessingPipelineConfig
} from './types'
import { aiEngine } from './StudentAssistanceAI'
// Mock Queue Implementation (in production, use Redis + Bull)
class MockQueue<T> {
private jobs: Array<{ id: string; data: T; priority: number }> = []
private processors: Map<string, (job: { id: string; data: T }) => Promise<void>> = new Map()
async add(jobType: string, data: T, options: { priority: number; attempts?: number; backoff?: string } = { priority: 0 }): Promise<{ id: string }> {
const job = {
id: `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
data,
priority: options.priority
}
this.jobs.push(job)
this.jobs.sort((a, b) => b.priority - a.priority) // Higher priority first
// Process job immediately for demo
setTimeout(() => this.processNextJob(jobType), 100)
return job
}
process(jobType: string, concurrency: number, processor: (job: { id: string; data: T }) => Promise<void>): void {
this.processors.set(jobType, processor)
}
private async processNextJob(jobType: string): Promise<void> {
const processor = this.processors.get(jobType)
if (!processor || this.jobs.length === 0) return
const job = this.jobs.shift()!
try {
await processor(job)
} catch (error) {
console.error(`Error processing job ${job.id}:`, error)
}
}
}
// Notification Service for real-time updates
class NotificationService {
private subscribers: Set<(update: AIUpdate) => void> = new Set()
subscribe(callback: (update: AIUpdate) => void): () => void {
this.subscribers.add(callback)
return () => this.subscribers.delete(callback)
}
notify(update: AIUpdate): void {
this.subscribers.forEach(callback => {
try {
callback(update)
} catch (error) {
console.error('Error in notification callback:', error)
}
})
}
async notifyStudent(studentId: string, assignment: any): Promise<void> {
console.log(`📧 Notifying student ${studentId} about assignment`)
// In production: send email, SMS, or push notification
}
async notifyVolunteer(volunteerId: string, assignment: any): Promise<void> {
console.log(`📧 Notifying volunteer ${volunteerId} about new assignment`)
// In production: send volunteer notification
}
async notifyCoordinators(assignment: any): Promise<void> {
console.log(`📧 Notifying coordinators about new assignment`)
// In production: alert coordination team
}
async updateDonors(estimatedCost: number): Promise<void> {
console.log(`💰 Updating donors about $${estimatedCost} impact opportunity`)
// In production: trigger donor engagement campaign
}
async notifyReviewer(reviewer: any, reviewTask: any, aiInsights: any): Promise<void> {
console.log(`👥 Notifying reviewer ${reviewer.id} about review task with AI confidence: ${aiInsights.aiConfidence}`)
// In production: send detailed review notification with AI recommendations
}
}
// Assignment and Review Management
class AssignmentManager {
private static assignments: Map<string, any> = new Map()
static async createAssignment(assignmentData: any): Promise<any> {
const assignment = {
id: `assign-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
...assignmentData,
createdAt: new Date(),
status: 'pending'
}
this.assignments.set(assignment.id, assignment)
console.log(`✅ Created assignment ${assignment.id}`)
return assignment
}
static async getById(id: string): Promise<any | null> {
return this.assignments.get(id) || null
}
}
class ReviewManager {
private static reviewTasks: Map<string, any> = new Map()
static async createReviewTask(taskData: any): Promise<any> {
const task = {
id: `review-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
...taskData,
createdAt: new Date(),
status: 'pending'
}
this.reviewTasks.set(task.id, task)
console.log(`📋 Created review task ${task.id}`)
return task
}
}
// Main Processing Pipeline
export class RealTimeProcessingPipeline {
private queue: MockQueue<StudentRequest>
private notificationService: NotificationService
private config: ProcessingPipelineConfig
constructor(config: Partial<ProcessingPipelineConfig> = {}) {
this.queue = new MockQueue<StudentRequest>()
this.notificationService = new NotificationService()
this.config = {
autoApprovalThreshold: 0.85,
urgencyWeights: {
'emergency': 1.0,
'high': 0.8,
'medium': 0.5,
'low': 0.2
},
categoryWeights: {
'emergency-housing': 1.0,
'food-assistance': 0.9,
'medical-needs': 0.85,
'clothing': 0.7,
'school-supplies': 0.6,
'transportation': 0.5,
'technology': 0.4,
'extracurricular': 0.3,
'other': 0.4
},
maxProcessingTime: 5000, // 5 seconds
retryAttempts: 3,
notificationEnabled: true,
...config
}
this.setupQueueProcessors()
}
private setupQueueProcessors(): void {
this.queue.process('analyze-request', 5, async (job) => {
const request = job.data
try {
console.log(`🔄 Processing request ${request.id} for ${request.studentName}`)
// AI analysis and matching
const matches = await aiEngine.processRequest(request)
// Auto-approval for high-confidence matches
if (matches.length > 0 && matches[0].confidenceScore >= this.config.autoApprovalThreshold) {
await this.autoApproveRequest(request, matches[0])
} else {
await this.routeForHumanReview(request, matches)
}
// Update real-time dashboard
await this.updateDashboard(request.id, matches)
// Notify subscribers of processing completion
this.notificationService.notify({
type: 'request-processed',
requestId: request.id,
studentName: request.studentName,
status: matches.length > 0 && matches[0].confidenceScore >= this.config.autoApprovalThreshold ? 'auto-approved' : 'under-review',
recommendations: matches,
timestamp: new Date()
})
} catch (error) {
await this.handleProcessingError(request, error as Error)
}
})
}
async submitRequest(request: StudentRequest): Promise<string> {
console.log(`📥 Submitting request from ${request.studentName}: ${request.category}`)
// Add to processing queue with priority based on urgency
const priority = this.calculatePriority(request.urgency)
const job = await this.queue.add('analyze-request', request, {
priority,
attempts: this.config.retryAttempts,
backoff: 'exponential'
})
// Immediate acknowledgment
await this.sendAcknowledgment(request)
return job.id
}
private calculatePriority(urgency: string): number {
return this.config.urgencyWeights[urgency as keyof typeof this.config.urgencyWeights] || 0.5
}
private async sendAcknowledgment(request: StudentRequest): Promise<void> {
console.log(`✉️ Sending acknowledgment to ${request.studentName}`)
// In production: send immediate confirmation email/SMS
}
private async autoApproveRequest(request: StudentRequest, match: MatchResult): Promise<void> {
console.log(`🤖 Auto-approving request ${request.id} with ${(match.confidenceScore * 100).toFixed(1)}% confidence`)
// Create assistance assignment
const assignment = await AssignmentManager.createAssignment({
requestId: request.id,
studentId: request.studentId,
studentName: request.studentName,
resourceId: match.resourceId,
resourceName: match.resourceName,
volunteerId: match.volunteerMatch?.id,
volunteerName: match.volunteerMatch?.volunteerName,
scheduledDate: new Date(Date.now() + 24 * 60 * 60 * 1000), // Tomorrow
estimatedCost: match.estimatedCost,
approvalStatus: 'auto-approved',
confidence: match.confidenceScore,
aiRecommendation: true,
urgency: request.urgency,
category: request.category
})
// Notify all stakeholders
if (this.config.notificationEnabled) {
await Promise.all([
this.notificationService.notifyStudent(request.studentId, assignment),
match.volunteerMatch ? this.notificationService.notifyVolunteer(assignment.volunteerId, assignment) : Promise.resolve(),
this.notificationService.notifyCoordinators(assignment),
this.notificationService.updateDonors(assignment.estimatedCost)
])
}
// Notify real-time subscribers
this.notificationService.notify({
type: 'auto-approval',
requestId: request.id,
studentName: request.studentName,
message: `Request automatically approved with ${(match.confidenceScore * 100).toFixed(1)}% confidence`,
timestamp: new Date()
})
// Track decision for learning
await this.trackDecision(request, match, 'auto-approved')
}
private async routeForHumanReview(request: StudentRequest, matches: MatchResult[]): Promise<void> {
console.log(`👤 Routing request ${request.id} for human review`)
// Determine best reviewer based on request type and matches
const reviewer = await this.selectOptimalReviewer(request, matches)
// Create review assignment
const reviewTask = await ReviewManager.createReviewTask({
requestId: request.id,
assignedTo: reviewer.id,
assignedToName: reviewer.name,
aiRecommendations: matches,
priority: this.calculateReviewPriority(request, matches),
deadline: this.calculateReviewDeadline(request.urgency),
studentName: request.studentName,
category: request.category,
urgency: request.urgency
})
// Notify reviewer with AI insights
if (this.config.notificationEnabled) {
await this.notificationService.notifyReviewer(reviewer, reviewTask, {
aiConfidence: matches[0]?.confidenceScore || 0,
recommendedAction: this.generateRecommendation(matches),
riskFactors: matches[0]?.riskFactors || []
})
}
}
private async selectOptimalReviewer(request: StudentRequest, matches: MatchResult[]) {
// Mock reviewer selection - in production, this would use actual staff data
const reviewers = [
{ id: 'rev1', name: 'Sarah Martinez', specialties: ['clothing', 'emergency-housing'], workload: 5 },
{ id: 'rev2', name: 'John Davis', specialties: ['food-assistance', 'transportation'], workload: 3 },
{ id: 'rev3', name: 'Lisa Chen', specialties: ['school-supplies', 'technology'], workload: 7 },
{ id: 'rev4', name: 'Mike Johnson', specialties: ['medical-needs', 'other'], workload: 4 }
]
// Select reviewer based on specialty and workload
const categoryReviewers = reviewers.filter(r =>
r.specialties.includes(request.category) || r.specialties.includes('other')
)
// Return reviewer with lowest workload
return categoryReviewers.sort((a, b) => a.workload - b.workload)[0] || reviewers[0]
}
private calculateReviewPriority(request: StudentRequest, matches: MatchResult[]): number {
let priority = this.config.urgencyWeights[request.urgency as keyof typeof this.config.urgencyWeights] || 0.5
// Boost priority for high AI confidence but below threshold
if (matches.length > 0) {
const topMatch = matches[0]
if (topMatch.confidenceScore > 0.7 && topMatch.confidenceScore < this.config.autoApprovalThreshold) {
priority += 0.2
}
}
// Boost priority for critical categories
priority += this.config.categoryWeights[request.category as keyof typeof this.config.categoryWeights] || 0
return Math.min(priority, 1.0)
}
private calculateReviewDeadline(urgency: string): Date {
const now = new Date()
switch (urgency) {
case 'emergency':
return new Date(now.getTime() + 30 * 60 * 1000) // 30 minutes
case 'high':
return new Date(now.getTime() + 2 * 60 * 60 * 1000) // 2 hours
case 'medium':
return new Date(now.getTime() + 8 * 60 * 60 * 1000) // 8 hours
case 'low':
default:
return new Date(now.getTime() + 24 * 60 * 60 * 1000) // 24 hours
}
}
private generateRecommendation(matches: MatchResult[]): string {
if (matches.length === 0) return 'No suitable matches found - manual resource allocation needed'
const topMatch = matches[0]
if (topMatch.confidenceScore > 0.8) {
return `Strong AI recommendation: ${topMatch.resourceName} (${(topMatch.confidenceScore * 100).toFixed(1)}% confidence)`
} else if (topMatch.confidenceScore > 0.6) {
return `Moderate AI recommendation: ${topMatch.resourceName} - review for accuracy`
} else {
return `Low confidence match: manual evaluation recommended`
}
}
private async updateDashboard(requestId: string, matches: MatchResult[]): Promise<void> {
console.log(`📊 Updating dashboard for request ${requestId}`)
// In production: update real-time analytics dashboard
}
private async handleProcessingError(request: StudentRequest, error: Error): Promise<void> {
console.error(`❌ Error processing request ${request.id}:`, error.message)
// Notify administrators of processing error
this.notificationService.notify({
type: 'alert',
requestId: request.id,
studentName: request.studentName,
message: `Processing error: ${error.message}`,
timestamp: new Date()
})
// Route to manual processing
await this.routeForHumanReview(request, [])
}
private async trackDecision(request: StudentRequest, match: MatchResult, decision: string): Promise<void> {
console.log(`📈 Tracking decision: ${decision} for request ${request.id}`)
// In production: log decision for ML model training
}
// Public methods for integration
subscribe(callback: (update: AIUpdate) => void): () => void {
return this.notificationService.subscribe(callback)
}
async generateInsights(requests: StudentRequest[]): Promise<AIInsight[]> {
return await aiEngine.generateInsights(requests)
}
getConfig(): ProcessingPipelineConfig {
return { ...this.config }
}
updateConfig(newConfig: Partial<ProcessingPipelineConfig>): void {
this.config = { ...this.config, ...newConfig }
console.log('🔧 Pipeline configuration updated')
}
}
// Export singleton instance
export const processingPipeline = new RealTimeProcessingPipeline()
// Export classes for testing and advanced usage
export { NotificationService, AssignmentManager, ReviewManager }

View File

@@ -0,0 +1,803 @@
// Phase 3: AI-Powered Student Assistance Matching Engine
import * as tf from '@tensorflow/tfjs'
import type {
StudentRequest,
MatchResult,
RequestAnalysis,
AIInsight,
LearningFeedback,
NeedCategory,
ResourceRequirement
} from './types'
// Text Vectorization for NLP Analysis
class TextVectorizer {
private vocabulary: Map<string, number> = new Map()
private vectorSize: number = 100
constructor() {
this.initializeVocabulary()
}
private initializeVocabulary() {
// Common assistance-related vocabulary
const assistanceVocabulary = [
// Clothing
'clothes', 'clothing', 'shirt', 'pants', 'shoes', 'coat', 'jacket', 'uniform', 'dress',
'socks', 'underwear', 'boots', 'sneakers', 'winter', 'warm', 'size', 'outgrown',
// School supplies
'supplies', 'backpack', 'notebook', 'pencil', 'pen', 'binder', 'calculator', 'books',
'textbook', 'paper', 'folders', 'highlighter', 'ruler', 'glue', 'scissors',
// Food assistance
'food', 'hungry', 'lunch', 'breakfast', 'dinner', 'meal', 'snack', 'groceries',
'nutrition', 'eat', 'starving', 'appetite', 'diet', 'allergic',
// Transportation
'transport', 'bus', 'ride', 'car', 'walk', 'distance', 'far', 'pickup', 'drop-off',
'gas', 'vehicle', 'bicycle', 'train', 'subway',
// Emergency/housing
'emergency', 'urgent', 'homeless', 'shelter', 'roof', 'home', 'housing', 'evicted',
'temporary', 'crisis', 'help', 'desperate',
// Medical
'medical', 'doctor', 'medicine', 'sick', 'health', 'prescription', 'hospital',
'glasses', 'dental', 'therapy', 'insurance',
// Technology
'computer', 'laptop', 'tablet', 'phone', 'internet', 'wifi', 'online', 'digital',
'device', 'charger', 'software', 'access',
// Emotional/contextual
'need', 'help', 'family', 'parent', 'mother', 'father', 'guardian', 'sibling',
'student', 'school', 'grade', 'class', 'teacher', 'principal', 'counselor',
'poor', 'afford', 'money', 'financial', 'struggle', 'difficult', 'hardship'
]
assistanceVocabulary.forEach((word, index) => {
this.vocabulary.set(word.toLowerCase(), index)
})
}
async encode(text: string): Promise<number[]> {
const words = this.preprocessText(text)
const vector = new Array(this.vectorSize).fill(0)
// Simple bag-of-words with TF-IDF weighting
const wordCounts = new Map<string, number>()
words.forEach(word => {
wordCounts.set(word, (wordCounts.get(word) || 0) + 1)
})
// Create weighted vector
wordCounts.forEach((count, word) => {
const vocabIndex = this.vocabulary.get(word)
if (vocabIndex !== undefined && vocabIndex < this.vectorSize) {
// Simple TF-IDF approximation
const tf = count / words.length
const idf = Math.log(this.vocabulary.size / (count + 1))
vector[vocabIndex] = tf * idf
}
})
// Simple semantic analysis without external NLP library
// Extract entities and sentiment patterns
// Boost urgency indicators
const urgencyWords = ['urgent', 'emergency', 'immediate', 'asap', 'desperate', 'critical']
const urgencyScore = urgencyWords.reduce((score, word) => {
return score + (text.toLowerCase().includes(word) ? 1 : 0)
}, 0)
// Add urgency as a feature
if (vector.length > this.vectorSize - 10) {
vector[this.vectorSize - 1] = urgencyScore / urgencyWords.length
}
return vector
}
private preprocessText(text: string): string[] {
return text
.toLowerCase()
.replace(/[^\w\s]/g, ' ')
.split(/\s+/)
.filter(word => word.length > 2)
}
}
// Core AI Matching Engine
export class StudentAssistanceAI {
private vectorizer: TextVectorizer
private matchingModel: tf.LayersModel | null = null
private impactModel: tf.LayersModel | null = null
private isInitialized = false
// Mock data for demonstration - in production, this would come from databases
private mockResources = [
{ id: 'r1', name: 'Emergency Clothing Fund', type: 'clothing', capacity: 100, avgCost: 75 },
{ id: 'r2', name: 'School Supply Backpack Program', type: 'supplies', capacity: 50, avgCost: 45 },
{ id: 'r3', name: 'Weekend Food Program', type: 'food', capacity: 200, avgCost: 25 },
{ id: 'r4', name: 'Transportation Assistance', type: 'transport', capacity: 30, avgCost: 40 },
{ id: 'r5', name: 'Technology Access Fund', type: 'technology', capacity: 25, avgCost: 200 }
]
private mockVolunteers = [
{ id: 'v1', name: 'Sarah Johnson', skills: ['clothing', 'shopping'], rating: 4.8, location: { city: 'Austin', state: 'TX' } },
{ id: 'v2', name: 'Mike Chen', skills: ['supplies', 'delivery'], rating: 4.9, location: { city: 'Austin', state: 'TX' } },
{ id: 'v3', name: 'Emily Rodriguez', skills: ['food', 'cooking'], rating: 4.7, location: { city: 'Round Rock', state: 'TX' } },
{ id: 'v4', name: 'David Thompson', skills: ['transport', 'technology'], rating: 4.6, location: { city: 'Cedar Park', state: 'TX' } }
]
constructor() {
this.vectorizer = new TextVectorizer()
this.initializeModels()
}
private async initializeModels() {
try {
// Create simple neural network models for demonstration
// In production, these would be pre-trained models loaded from files
// Matching Model: Input features -> Match probability
this.matchingModel = tf.sequential({
layers: [
tf.layers.dense({ inputShape: [110], units: 64, activation: 'relu' }),
tf.layers.dropout({ rate: 0.2 }),
tf.layers.dense({ units: 32, activation: 'relu' }),
tf.layers.dense({ units: 16, activation: 'relu' }),
tf.layers.dense({ units: 1, activation: 'sigmoid' })
]
})
// Impact Prediction Model: Match features -> Impact score
this.impactModel = tf.sequential({
layers: [
tf.layers.dense({ inputShape: [20], units: 32, activation: 'relu' }),
tf.layers.dense({ units: 16, activation: 'relu' }),
tf.layers.dense({ units: 4, activation: 'linear' }) // [beneficiaries, success_prob, time, sustainability]
]
})
// Compile models
this.matchingModel.compile({
optimizer: tf.train.adam(0.001),
loss: 'binaryCrossentropy',
metrics: ['accuracy']
})
this.impactModel.compile({
optimizer: tf.train.adam(0.001),
loss: 'meanSquaredError',
metrics: ['mae']
})
this.isInitialized = true
console.log('🤖 AI Models initialized successfully')
} catch (error) {
console.error('Failed to initialize AI models:', error)
// Fallback to rule-based system
this.isInitialized = false
}
}
async processRequest(request: StudentRequest): Promise<MatchResult[]> {
console.log(`🧠 Processing request for ${request.studentName}: ${request.description}`)
try {
// 1. Analyze request with NLP
const analysis = await this.analyzeRequest(request)
// 2. Find candidate resources
const candidates = await this.findCandidateResources(analysis)
// 3. Score matches using AI or rule-based fallback
const scoredMatches = this.isInitialized
? await this.aiScoreMatches(candidates, analysis, request)
: await this.ruleBasedScoring(candidates, analysis, request)
// 4. Predict impact for top matches
const enrichedMatches = await this.enrichWithPredictions(scoredMatches)
// 5. Sort by confidence and return top 3
return enrichedMatches
.sort((a, b) => b.confidenceScore - a.confidenceScore)
.slice(0, 3)
} catch (error) {
console.error('Error processing request:', error)
return this.generateFallbackMatches()
}
}
private async analyzeRequest(request: StudentRequest): Promise<RequestAnalysis> {
// NLP analysis of request description
await this.vectorizer.encode(request.description) // Process text for any side effects
// Extract needs using rule-based analysis + NLP
const primaryNeeds = this.extractNeedCategories(request)
const urgencyScore = this.calculateUrgencyScore(request)
const complexityEstimate = this.estimateComplexity(request)
return {
primaryNeeds,
urgencyScore,
complexityEstimate,
resourceRequirements: this.estimateResourceRequirements(primaryNeeds),
estimatedBudget: this.estimateBudget(primaryNeeds, request.description)
}
}
private extractNeedCategories(request: StudentRequest): NeedCategory[] {
const categories: NeedCategory[] = []
const desc = request.description.toLowerCase()
// Clothing detection
if (desc.includes('clothes') || desc.includes('clothing') || desc.includes('shirt') ||
desc.includes('pants') || desc.includes('shoes') || desc.includes('coat')) {
categories.push({
category: 'clothing',
priority: desc.includes('winter') || desc.includes('cold') ? 0.9 : 0.7,
specifications: { season: desc.includes('winter') ? 'winter' : 'general' }
})
}
// School supplies detection
if (desc.includes('supplies') || desc.includes('backpack') || desc.includes('notebook') ||
desc.includes('pencil') || desc.includes('books')) {
categories.push({
category: 'school-supplies',
priority: 0.8,
specifications: { type: 'general' }
})
}
// Food assistance detection
if (desc.includes('food') || desc.includes('hungry') || desc.includes('lunch') ||
desc.includes('meal') || desc.includes('eat')) {
categories.push({
category: 'food-assistance',
priority: desc.includes('hungry') || desc.includes('starving') ? 0.95 : 0.75,
specifications: { urgency: desc.includes('hungry') ? 'high' : 'medium' }
})
}
// Transportation detection
if (desc.includes('transport') || desc.includes('bus') || desc.includes('ride') ||
desc.includes('car') || desc.includes('far')) {
categories.push({
category: 'transportation',
priority: 0.6,
specifications: { type: 'general' }
})
}
// Emergency/housing detection
if (desc.includes('emergency') || desc.includes('urgent') || desc.includes('homeless') ||
desc.includes('shelter') || desc.includes('crisis')) {
categories.push({
category: 'emergency-housing',
priority: 0.98,
specifications: { urgency: 'critical' }
})
}
// Technology detection
if (desc.includes('computer') || desc.includes('laptop') || desc.includes('internet') ||
desc.includes('online') || desc.includes('device')) {
categories.push({
category: 'technology',
priority: 0.65,
specifications: { type: 'educational' }
})
}
// Default to the request category if nothing else matches
if (categories.length === 0) {
categories.push({
category: request.category,
priority: 0.5,
specifications: {}
})
}
return categories.sort((a, b) => b.priority - a.priority)
}
private calculateUrgencyScore(request: StudentRequest): number {
let score = 0
// Base score from urgency level
switch (request.urgency) {
case 'emergency': score += 1.0; break
case 'high': score += 0.8; break
case 'medium': score += 0.5; break
case 'low': score += 0.2; break
}
// Boost for urgency keywords in description
const desc = request.description.toLowerCase()
const urgencyKeywords = ['urgent', 'emergency', 'immediate', 'asap', 'desperate', 'critical', 'help', 'need now']
urgencyKeywords.forEach(keyword => {
if (desc.includes(keyword)) score += 0.1
})
// Boost for deadline
if (request.deadline) {
const daysUntilDeadline = (request.deadline.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
if (daysUntilDeadline < 1) score += 0.3
else if (daysUntilDeadline < 3) score += 0.2
else if (daysUntilDeadline < 7) score += 0.1
}
return Math.min(score, 1.0) // Cap at 1.0
}
private estimateComplexity(request: StudentRequest): number {
let complexity = 0.3 // Base complexity
// Multiple needs increase complexity
const needCount = this.extractNeedCategories(request).length
complexity += needCount * 0.15
// Special constraints increase complexity
if (request.constraints.deliveryMethod === 'delivery') complexity += 0.2
if (request.constraints.privacyLevel === 'anonymous') complexity += 0.1
if (request.constraints.specialRequirements?.length) complexity += 0.2
// Geographic complexity
if (!request.location.city || !request.location.zipCode) complexity += 0.15
return Math.min(complexity, 1.0)
}
private estimateResourceRequirements(needs: NeedCategory[]): ResourceRequirement[] {
return needs.map(need => ({
type: need.category,
quantity: 1,
specifications: need.specifications || {},
estimatedCost: this.estimateCategoryBudget(need.category)
}))
}
private estimateBudget(needs: NeedCategory[], description: string): number {
let totalBudget = 0
needs.forEach(need => {
totalBudget += this.estimateCategoryBudget(need.category) * need.priority
})
// Adjust based on description indicators
const desc = description.toLowerCase()
if (desc.includes('multiple') || desc.includes('several')) totalBudget *= 1.5
if (desc.includes('family') || desc.includes('siblings')) totalBudget *= 2
if (desc.includes('brand new') || desc.includes('quality')) totalBudget *= 1.3
return Math.round(totalBudget)
}
private estimateCategoryBudget(category: string): number {
const budgets: Record<string, number> = {
'clothing': 75,
'school-supplies': 45,
'food-assistance': 25,
'transportation': 40,
'emergency-housing': 200,
'medical-needs': 150,
'technology': 200,
'extracurricular': 60,
'other': 50
}
return budgets[category] || 50
}
private async findCandidateResources(analysis: RequestAnalysis) {
// Find resources that match the primary needs
const candidates = this.mockResources.filter(resource =>
analysis.primaryNeeds.some(need =>
resource.type === need.category ||
this.isCompatibleResourceType(resource.type, need.category)
)
)
// Add volunteer matches
const volunteerCandidates = this.mockVolunteers.filter(volunteer =>
volunteer.skills.some(skill =>
analysis.primaryNeeds.some(need =>
skill.includes(need.category) || need.category.includes(skill)
)
)
)
return candidates.map(resource => ({
...resource,
volunteers: volunteerCandidates.filter(v =>
v.skills.some(skill => skill.includes(resource.type))
)
}))
}
private isCompatibleResourceType(resourceType: string, needCategory: string): boolean {
const compatibility: Record<string, string[]> = {
'clothing': ['clothing'],
'supplies': ['school-supplies'],
'food': ['food-assistance'],
'transport': ['transportation'],
'technology': ['technology'],
'emergency': ['emergency-housing', 'medical-needs']
}
return compatibility[resourceType]?.includes(needCategory) || false
}
private async aiScoreMatches(candidates: any[], analysis: RequestAnalysis, request: StudentRequest): Promise<MatchResult[]> {
if (!this.matchingModel) return this.ruleBasedScoring(candidates, analysis, request)
const matches: MatchResult[] = []
for (const candidate of candidates) {
try {
// Prepare features for ML model
const features = await this.prepareFeaturesForML(candidate, analysis, request)
const featureTensor = tf.tensor2d([features])
// Get confidence score from AI model
const prediction = this.matchingModel.predict(featureTensor) as tf.Tensor
const confidenceData = await prediction.data()
const confidenceScore = confidenceData[0]
// Clean up tensors
featureTensor.dispose()
prediction.dispose()
// Select best volunteer
const bestVolunteer = candidate.volunteers.length > 0
? candidate.volunteers.sort((a: any, b: any) => b.rating - a.rating)[0]
: null
matches.push({
resourceId: candidate.id,
resourceName: candidate.name,
resourceType: candidate.type,
confidenceScore,
estimatedImpact: this.estimateImpact(analysis, candidate),
logisticalComplexity: analysis.complexityEstimate,
volunteerMatch: bestVolunteer ? {
id: bestVolunteer.id,
volunteerId: bestVolunteer.id,
volunteerName: bestVolunteer.name,
skills: bestVolunteer.skills,
availability: [], // Would be populated from real data
location: bestVolunteer.location,
rating: bestVolunteer.rating,
completedAssignments: Math.floor(Math.random() * 50) + 10
} : undefined,
estimatedCost: candidate.avgCost,
fulfillmentTimeline: this.estimateTimeline(analysis.urgencyScore, analysis.complexityEstimate),
reasoningFactors: this.generateReasoningFactors(candidate, analysis),
riskFactors: this.identifyRiskFactors(candidate, analysis)
})
} catch (error) {
console.error('Error in AI scoring:', error)
// Fallback to rule-based for this candidate
const ruleBasedScore = this.calculateRuleBasedScore(candidate, analysis, request)
matches.push(ruleBasedScore)
}
}
return matches
}
private async prepareFeaturesForML(candidate: any, analysis: RequestAnalysis, request: StudentRequest): Promise<number[]> {
// Create feature vector for ML model (110 features to match model input)
const features = new Array(110).fill(0)
// Request analysis features (first 100 from text vectorization)
const textVector = await this.vectorizer.encode(request.description)
for (let i = 0; i < Math.min(textVector.length, 100); i++) {
features[i] = textVector[i]
}
// Resource matching features
features[100] = analysis.urgencyScore
features[101] = analysis.complexityEstimate
features[102] = analysis.estimatedBudget / 1000 // Normalize
features[103] = candidate.capacity / 100 // Normalize
features[104] = candidate.avgCost / 200 // Normalize
features[105] = candidate.volunteers.length / 10 // Normalize
features[106] = candidate.volunteers.length > 0 ? Math.max(...candidate.volunteers.map((v: any) => v.rating)) / 5 : 0
features[107] = analysis.primaryNeeds.length / 5 // Normalize
features[108] = request.constraints.maxBudget ? (candidate.avgCost / request.constraints.maxBudget) : 0.5
features[109] = this.getTimeframeScore(request.constraints.timeframe)
return features
}
private getTimeframeScore(timeframe: string): number {
switch (timeframe) {
case 'immediate': return 1.0
case 'within-week': return 0.7
case 'within-month': return 0.4
case 'flexible': return 0.2
default: return 0.5
}
}
private async ruleBasedScoring(candidates: any[], analysis: RequestAnalysis, request: StudentRequest): Promise<MatchResult[]> {
return candidates.map(candidate => this.calculateRuleBasedScore(candidate, analysis, request))
}
private calculateRuleBasedScore(candidate: any, analysis: RequestAnalysis, request: StudentRequest): MatchResult {
let score = 0.5 // Base score
// Category match bonus
const categoryMatch = analysis.primaryNeeds.some(need =>
candidate.type === need.category || this.isCompatibleResourceType(candidate.type, need.category)
)
if (categoryMatch) score += 0.3
// Capacity and cost considerations
if (candidate.capacity > 10) score += 0.1
if (candidate.avgCost <= analysis.estimatedBudget) score += 0.2
// Volunteer availability bonus
if (candidate.volunteers?.length > 0) {
score += 0.15
const avgRating = candidate.volunteers.reduce((sum: number, v: any) => sum + v.rating, 0) / candidate.volunteers.length
score += (avgRating / 5) * 0.1
}
// Urgency adjustment
score += analysis.urgencyScore * 0.1
// Complexity penalty
score -= analysis.complexityEstimate * 0.05
const bestVolunteer = candidate.volunteers?.length > 0
? candidate.volunteers.sort((a: any, b: any) => b.rating - a.rating)[0]
: null
return {
resourceId: candidate.id,
resourceName: candidate.name,
resourceType: candidate.type,
confidenceScore: Math.min(Math.max(score, 0), 1),
estimatedImpact: this.estimateImpact(analysis, candidate),
logisticalComplexity: analysis.complexityEstimate,
volunteerMatch: bestVolunteer ? {
id: bestVolunteer.id,
volunteerId: bestVolunteer.id,
volunteerName: bestVolunteer.name,
skills: bestVolunteer.skills,
availability: [],
location: bestVolunteer.location,
rating: bestVolunteer.rating,
completedAssignments: Math.floor(Math.random() * 50) + 10
} : undefined,
estimatedCost: candidate.avgCost,
fulfillmentTimeline: this.estimateTimeline(analysis.urgencyScore, analysis.complexityEstimate),
reasoningFactors: this.generateReasoningFactors(candidate, analysis),
riskFactors: this.identifyRiskFactors(candidate, analysis, request)
}
}
private estimateImpact(analysis: RequestAnalysis, candidate: any): number {
let impact = 0.6 // Base impact
// Higher impact for urgent needs
impact += analysis.urgencyScore * 0.2
// Higher impact for critical categories
const criticalCategories = ['emergency-housing', 'food-assistance', 'medical-needs']
if (analysis.primaryNeeds.some(need => criticalCategories.includes(need.category))) {
impact += 0.15
}
// Volunteer quality impact
if (candidate.volunteers?.length > 0) {
const avgRating = candidate.volunteers.reduce((sum: number, v: any) => sum + v.rating, 0) / candidate.volunteers.length
impact += (avgRating / 5) * 0.1
}
return Math.min(impact, 1.0)
}
private estimateTimeline(urgencyScore: number, complexityScore: number): string {
const baseHours = 24 + (complexityScore * 48) - (urgencyScore * 12)
if (baseHours <= 2) return 'Within 2 hours'
if (baseHours <= 24) return 'Same day'
if (baseHours <= 48) return '1-2 days'
if (baseHours <= 168) return '3-7 days'
return '1-2 weeks'
}
private generateReasoningFactors(candidate: any, analysis: RequestAnalysis): string[] {
const factors: string[] = []
factors.push(`Resource specializes in ${candidate.type} assistance`)
if (candidate.capacity > 50) factors.push('High capacity resource available')
if (candidate.avgCost <= analysis.estimatedBudget) factors.push('Cost within estimated budget')
if (candidate.volunteers?.length > 0) {
const avgRating = candidate.volunteers.reduce((sum: number, v: any) => sum + v.rating, 0) / candidate.volunteers.length
factors.push(`${candidate.volunteers.length} volunteers available (avg rating: ${avgRating.toFixed(1)})`)
}
if (analysis.urgencyScore > 0.7) factors.push('High urgency request prioritized')
return factors
}
private identifyRiskFactors(candidate: any, analysis: RequestAnalysis): string[] {
const risks: string[] = []
if (candidate.capacity < 5) risks.push('Limited resource capacity')
if (candidate.avgCost > analysis.estimatedBudget * 1.2) risks.push('Cost exceeds estimated budget')
if (candidate.volunteers?.length === 0) risks.push('No volunteers currently available')
if (analysis.complexityEstimate > 0.7) risks.push('High complexity request')
return risks
}
private async enrichWithPredictions(matches: MatchResult[]): Promise<MatchResult[]> {
// For each match, predict impact using AI model if available
for (const match of matches) {
try {
if (this.impactModel && this.isInitialized) {
const impactFeatures = this.prepareImpactFeatures(match)
const impactTensor = tf.tensor2d([impactFeatures])
const impactPrediction = this.impactModel.predict(impactTensor) as tf.Tensor
const impactData = await impactPrediction.data()
// Update match with AI predictions
match.estimatedImpact = Math.min(Math.max(impactData[1], 0), 1) // success probability
// Clean up tensors
impactTensor.dispose()
impactPrediction.dispose()
}
} catch (error) {
console.error('Error in impact prediction:', error)
// Keep existing impact estimate
}
}
return matches
}
private prepareImpactFeatures(match: MatchResult): number[] {
// Create 20-feature vector for impact prediction
return [
match.confidenceScore,
match.estimatedCost / 200, // Normalize
match.logisticalComplexity,
match.volunteerMatch ? match.volunteerMatch.rating / 5 : 0,
match.volunteerMatch ? match.volunteerMatch.completedAssignments / 50 : 0,
match.resourceType === 'emergency' ? 1 : 0,
match.resourceType === 'food' ? 1 : 0,
match.resourceType === 'clothing' ? 1 : 0,
match.resourceType === 'supplies' ? 1 : 0,
match.resourceType === 'transport' ? 1 : 0,
match.reasoningFactors.length / 5,
match.riskFactors.length / 5,
Math.random(), // Random noise for variation
Math.random(),
Math.random(),
Math.random(),
Math.random(),
Math.random(),
Math.random(),
Math.random()
]
}
private generateFallbackMatches(): MatchResult[] {
// Emergency fallback when AI fails
return [{
resourceId: 'fallback-1',
resourceName: 'General Assistance Fund',
resourceType: 'other',
confidenceScore: 0.5,
estimatedImpact: 0.6,
logisticalComplexity: 0.5,
estimatedCost: 75,
fulfillmentTimeline: '1-2 days',
reasoningFactors: ['Fallback resource for emergency situations'],
riskFactors: ['System processing temporarily unavailable']
}]
}
async generateInsights(requests: StudentRequest[]): Promise<AIInsight[]> {
const insights: AIInsight[] = []
// Pattern analysis
const categoryDistribution = this.analyzeCategoryDistribution(requests)
const urgencyTrends = this.analyzeUrgencyTrends(requests)
// Generate insights based on patterns
if (categoryDistribution.clothing > 0.4) {
insights.push({
id: `insight-${Date.now()}-1`,
type: 'trend',
title: 'High Clothing Demand Detected',
description: `${Math.round(categoryDistribution.clothing * 100)}% of recent requests are for clothing assistance. Consider expanding clothing inventory.`,
confidence: 0.85,
severity: 'medium',
timestamp: new Date(),
actionItems: ['Increase clothing fund allocation', 'Recruit clothing-focused volunteers', 'Partner with local clothing stores']
})
}
if (urgencyTrends.emergencyRate > 0.3) {
insights.push({
id: `insight-${Date.now()}-2`,
type: 'anomaly',
title: 'Increased Emergency Requests',
description: `${Math.round(urgencyTrends.emergencyRate * 100)}% of requests are marked as emergency. This is above normal baseline.`,
confidence: 0.9,
severity: 'high',
timestamp: new Date(),
actionItems: ['Review emergency response protocols', 'Ensure emergency fund availability', 'Alert senior coordinators']
})
}
return insights
}
private analyzeCategoryDistribution(requests: StudentRequest[]) {
const total = requests.length
if (total === 0) return {}
const counts: Record<string, number> = {}
requests.forEach(req => {
counts[req.category] = (counts[req.category] || 0) + 1
})
const distribution: Record<string, number> = {}
Object.entries(counts).forEach(([category, count]) => {
distribution[category] = count / total
})
return distribution
}
private analyzeUrgencyTrends(requests: StudentRequest[]) {
const total = requests.length
if (total === 0) return { emergencyRate: 0 }
const emergencyCount = requests.filter(req => req.urgency === 'emergency').length
return {
emergencyRate: emergencyCount / total
}
}
async learnFromFeedback(feedback: LearningFeedback): Promise<void> {
console.log(`📚 Learning from feedback for request ${feedback.requestId}`)
// In production, this would update training data and trigger model retraining
// For now, we'll log the feedback for analysis
const learningData = {
requestId: feedback.requestId,
outcome: feedback.outcome,
accuracyScore: feedback.outcome === 'successful' ? 1 : 0,
costAccuracy: Math.abs(feedback.actualCost - (feedback.actualCost)) / feedback.actualCost,
timeAccuracy: feedback.actualTimeToComplete,
satisfaction: feedback.satisfactionScore,
timestamp: new Date()
}
// Store for batch processing and model improvement
console.log('Learning data recorded:', learningData)
}
}
// Export the AI engine
export const aiEngine = new StudentAssistanceAI()

191
src/ai/types.ts Normal file
View File

@@ -0,0 +1,191 @@
// Phase 3: AI Types and Interfaces
export interface StudentRequest {
id: string
studentId: string
studentName: string
description: string
category: AssistanceCategory
urgency: UrgencyLevel
location: GeographicLocation
constraints: RequestConstraints
deadline?: Date
submittedAt: Date
estimatedCost?: number
requiredSkills?: string[]
}
export interface MatchResult {
resourceId: string
resourceName: string
resourceType: 'clothing' | 'supplies' | 'food' | 'transport' | 'emergency' | 'other'
confidenceScore: number
estimatedImpact: number
logisticalComplexity: number
volunteerMatch?: VolunteerAssignment
estimatedCost: number
fulfillmentTimeline: string
reasoningFactors: string[]
riskFactors: string[]
}
export interface RequestAnalysis {
primaryNeeds: NeedCategory[]
urgencyScore: number
complexityEstimate: number
resourceRequirements: ResourceRequirement[]
locationConstraints?: GeographicConstraint[]
timeConstraints?: TemporalConstraint[]
requiredSkills?: string[]
estimatedBudget: number
}
export interface AIInsight {
id: string
type: 'anomaly' | 'optimization' | 'trend' | 'prediction' | 'recommendation'
title: string
description: string
confidence: number
severity?: 'low' | 'medium' | 'high' | 'critical'
timestamp: Date
actionItems?: string[]
estimatedImpact?: string
data?: Record<string, any>
}
export interface AIMetrics {
accuracyRate: number
accuracyTrend: number
avgProcessingTime: number
speedTrend: number
autoApprovalRate: number
automationTrend: number
impactPredictionAccuracy: number
impactTrend: number
totalRequestsProcessed: number
successfulMatches: number
}
export interface AIUpdate {
type: 'request-processed' | 'new-insight' | 'auto-approval' | 'model-updated' | 'alert'
requestId?: string
studentName?: string
status?: string
recommendations?: MatchResult[]
insight?: AIInsight
message?: string
timestamp: Date
}
export type AssistanceCategory =
| 'clothing'
| 'school-supplies'
| 'food-assistance'
| 'transportation'
| 'emergency-housing'
| 'medical-needs'
| 'technology'
| 'extracurricular'
| 'other'
export type UrgencyLevel = 'low' | 'medium' | 'high' | 'emergency'
export interface GeographicLocation {
latitude?: number
longitude?: number
address?: string
city: string
state: string
zipCode: string
schoolDistrict?: string
}
export interface RequestConstraints {
maxBudget?: number
timeframe: 'immediate' | 'within-week' | 'within-month' | 'flexible'
deliveryMethod: 'pickup' | 'delivery' | 'mail' | 'school-delivery' | 'any'
privacyLevel: 'anonymous' | 'semi-anonymous' | 'open'
specialRequirements?: string[]
}
export interface VolunteerAssignment {
id: string
volunteerId: string
volunteerName: string
skills: string[]
availability: Date[]
location: GeographicLocation
rating: number
completedAssignments: number
}
export interface NeedCategory {
category: AssistanceCategory
subcategory?: string
priority: number
quantity?: number
specifications?: Record<string, any>
}
export interface ResourceRequirement {
type: string
quantity: number
specifications: Record<string, any>
alternatives?: string[]
estimatedCost: number
}
export interface GeographicConstraint {
maxDistance: number
preferredAreas?: string[]
excludedAreas?: string[]
}
export interface TemporalConstraint {
earliestStart: Date
latestCompletion: Date
preferredTimes?: string[]
blackoutPeriods?: DateRange[]
}
export interface DateRange {
start: Date
end: Date
}
export interface ImpactPrediction {
estimatedBeneficiaries: number
successProbability: number
timeToImpact: number
sustainabilityScore: number
rippleEffects: RippleEffect[]
measurableOutcomes: string[]
}
export interface RippleEffect {
type: 'family' | 'community' | 'academic' | 'social' | 'economic'
description: string
estimatedBeneficiaries: number
confidenceLevel: number
}
export interface LearningFeedback {
requestId: string
matchId: string
outcome: 'successful' | 'partial' | 'failed'
actualCost: number
actualTimeToComplete: number
satisfactionScore: number
issues?: string[]
improvements?: string[]
measuredImpact?: Record<string, number>
}
export interface ProcessingPipelineConfig {
autoApprovalThreshold: number
urgencyWeights: Record<UrgencyLevel, number>
categoryWeights: Record<AssistanceCategory, number>
maxProcessingTime: number
retryAttempts: number
notificationEnabled: boolean
}

View File

@@ -0,0 +1,721 @@
// Phase 3: AI Assistance Portal React Components
import React, { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { formatDistanceToNow } from 'date-fns'
import type {
StudentRequest,
MatchResult,
AIInsight,
AIMetrics,
AIUpdate,
UrgencyLevel,
AssistanceCategory
} from '../ai/types'
import { processingPipeline } from '../ai/ProcessingPipeline'
// Icons (using the existing icon system)
const Brain = ({ className = "w-5 h-5" }: { className?: string }) => (
<svg className={className} fill="currentColor" viewBox="0 0 20 20">
<path d="M17.28 9.28a.75.75 0 00-1.06-1.06l-7.5 7.5a.75.75 0 101.06 1.06l7.5-7.5z" />
</svg>
)
const Cpu = ({ className = "w-4 h-4" }: { className?: string }) => (
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<rect x="4" y="4" width="16" height="16" rx="2" />
<rect x="9" y="9" width="6" height="6" />
<path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3" />
</svg>
)
const Activity = ({ className = "w-4 h-4" }: { className?: string }) => (
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<polyline points="22,12 18,12 15,21 9,3 6,12 2,12" />
</svg>
)
const TrendingUp = ({ className = "w-8 h-8" }: { className?: string }) => (
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18" />
<polyline points="17 6 23 6 23 12" />
</svg>
)
const Users = ({ className = "w-8 h-8" }: { className?: string }) => (
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
)
// Mock data for demonstration
const mockRequests: StudentRequest[] = [
{
id: 'req-1',
studentId: 'std-1',
studentName: 'Maria Rodriguez',
description: 'Need winter coat and boots for my daughter. Size 8 shoes and medium coat. Getting cold and she only has summer clothes.',
category: 'clothing',
urgency: 'high',
location: { city: 'Austin', state: 'TX', zipCode: '78701' },
constraints: { timeframe: 'within-week', deliveryMethod: 'school-delivery', privacyLevel: 'semi-anonymous' },
submittedAt: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2 hours ago
},
{
id: 'req-2',
studentId: 'std-2',
studentName: 'James Thompson',
description: 'My son needs school supplies - notebooks, pencils, calculator for math class. Starting new semester next week.',
category: 'school-supplies',
urgency: 'medium',
location: { city: 'Round Rock', state: 'TX', zipCode: '78664' },
constraints: { timeframe: 'within-week', deliveryMethod: 'pickup', privacyLevel: 'open' },
submittedAt: new Date(Date.now() - 45 * 60 * 1000), // 45 minutes ago
},
{
id: 'req-3',
studentId: 'std-3',
studentName: 'Sarah Kim',
description: 'Emergency - no food at home for kids this weekend. Need groceries or meal assistance ASAP.',
category: 'food-assistance',
urgency: 'emergency',
location: { city: 'Cedar Park', state: 'TX', zipCode: '78613' },
constraints: { timeframe: 'immediate', deliveryMethod: 'delivery', privacyLevel: 'anonymous' },
submittedAt: new Date(Date.now() - 20 * 60 * 1000), // 20 minutes ago
}
]
interface AIAssistancePortalProps {
userRole: 'student' | 'coordinator' | 'admin'
}
export function AIAssistancePortal({ userRole }: AIAssistancePortalProps) {
const [requests, setRequests] = useState<StudentRequest[]>(mockRequests)
const [aiInsights, setAIInsights] = useState<AIInsight[]>([])
const [processing, setProcessing] = useState(false)
const [selectedRequest, setSelectedRequest] = useState<string | null>(null)
useEffect(() => {
// Subscribe to real-time AI updates
const unsubscribe = processingPipeline.subscribe(handleRealTimeUpdate)
// Generate initial insights
generateInsights()
return unsubscribe
}, [])
const handleRealTimeUpdate = (update: AIUpdate) => {
console.log('🔄 Real-time update received:', update)
switch (update.type) {
case 'request-processed':
setRequests(prev => prev.map(r =>
r.id === update.requestId
? { ...r, status: update.status, aiRecommendations: update.recommendations }
: r
))
break
case 'new-insight':
if (update.insight) {
setAIInsights(prev => [update.insight!, ...prev.slice(0, 4)])
}
break
case 'auto-approval':
// Show success notification
console.log('✅ Auto-approval notification:', update.message)
break
}
}
const generateInsights = async () => {
try {
const insights = await processingPipeline.generateInsights(requests)
setAIInsights(insights)
} catch (error) {
console.error('Error generating insights:', error)
}
}
const handleApproval = async (requestId: string, recommendation: MatchResult) => {
setProcessing(true)
try {
console.log(`✅ Approving request ${requestId} with recommendation:`, recommendation)
// In production: make API call to approve
setRequests(prev => prev.map(r =>
r.id === requestId ? { ...r, status: 'approved' } : r
))
} catch (error) {
console.error('Error approving request:', error)
} finally {
setProcessing(false)
}
}
const handleModification = async (requestId: string) => {
console.log(`✏️ Modifying request ${requestId}`)
setSelectedRequest(requestId)
}
const submitNewRequest = async (request: Omit<StudentRequest, 'id' | 'submittedAt'>) => {
setProcessing(true)
try {
const newRequest: StudentRequest = {
...request,
id: `req-${Date.now()}`,
submittedAt: new Date()
}
// Submit to AI processing pipeline
await processingPipeline.submitRequest(newRequest)
setRequests(prev => [newRequest, ...prev])
} catch (error) {
console.error('Error submitting request:', error)
} finally {
setProcessing(false)
}
}
return (
<div className="ai-assistance-portal grid grid-cols-1 lg:grid-cols-3 gap-8 p-6">
{/* AI Insights Panel */}
<motion.div
className="insights-panel bg-white dark:bg-gray-800 rounded-2xl border border-gray-200 dark:border-gray-700 p-6"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2 text-gray-900 dark:text-gray-100">
<Brain className="w-5 h-5 text-purple-500" />
AI Insights
</h3>
<AnimatePresence mode="popLayout">
{aiInsights.map((insight) => (
<motion.div
key={insight.id}
className="insight-card p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg mb-3 border border-purple-100 dark:border-purple-800"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
whileHover={{ scale: 1.02 }}
>
<div className="flex items-start gap-3">
<div className={`w-2 h-2 rounded-full mt-2 ${getInsightColor(insight.type)}`} />
<div className="flex-1">
<p className="font-medium text-sm text-gray-900 dark:text-gray-100">{insight.title}</p>
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1 line-clamp-2">
{insight.description}
</p>
{insight.confidence && (
<div className="mt-2 flex items-center gap-2">
<div className="w-16 bg-gray-200 dark:bg-gray-600 rounded-full h-1">
<div
className="bg-purple-500 h-1 rounded-full transition-all duration-300"
style={{ width: `${insight.confidence * 100}%` }}
/>
</div>
<span className="text-xs font-medium text-gray-700 dark:text-gray-300">
{Math.round(insight.confidence * 100)}%
</span>
</div>
)}
</div>
</div>
</motion.div>
))}
</AnimatePresence>
{aiInsights.length === 0 && (
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
<Brain className="w-12 h-12 mx-auto mb-3 opacity-50" />
<p className="text-sm">No insights available yet</p>
</div>
)}
</motion.div>
{/* Request Processing Interface */}
<div className="request-processing lg:col-span-2">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
Smart Request Processing
</h3>
<motion.button
onClick={generateInsights}
className="btn-secondary flex items-center gap-2 text-sm"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
disabled={processing}
>
<Activity className="w-4 h-4" />
{processing ? 'Processing...' : 'Refresh'}
</motion.button>
</div>
<div className="space-y-4">
{requests.map((request) => (
<RequestCard
key={request.id}
request={request}
onApprove={handleApproval}
onModify={handleModification}
showAIRecommendations={userRole !== 'student'}
isSelected={selectedRequest === request.id}
/>
))}
</div>
{userRole === 'student' && (
<NewRequestForm onSubmit={submitNewRequest} />
)}
</div>
{/* Performance Metrics */}
<div className="lg:col-span-3">
<AIPerformanceMetrics />
</div>
</div>
)
}
interface RequestCardProps {
request: StudentRequest & { status?: string; aiRecommendations?: MatchResult[] }
onApprove: (requestId: string, recommendation: MatchResult) => void
onModify: (requestId: string) => void
showAIRecommendations: boolean
isSelected: boolean
}
function RequestCard({ request, onApprove, onModify, showAIRecommendations, isSelected }: RequestCardProps) {
return (
<motion.div
className={`request-card p-6 border-2 rounded-xl mb-4 transition-all ${
isSelected
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800'
}`}
whileHover={{ y: -2, boxShadow: "0 8px 25px rgba(0,0,0,0.1)" }}
layout
>
<div className="flex justify-between items-start mb-4">
<div className="flex-1">
<h4 className="font-semibold text-gray-900 dark:text-gray-100 mb-2">
{request.studentName}
</h4>
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2 line-clamp-3">
{request.description}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
Submitted {formatDistanceToNow(request.submittedAt)} ago
</p>
</div>
<div className="flex flex-col items-end gap-2 ml-4">
<UrgencyBadge urgency={request.urgency} />
<CategoryBadge category={request.category} />
{request.status && <StatusBadge status={request.status} />}
</div>
</div>
{showAIRecommendations && request.aiRecommendations && (
<motion.div
className="ai-recommendations bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 p-4 rounded-xl mb-4 border border-blue-200 dark:border-blue-800"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
transition={{ duration: 0.3 }}
>
<div className="flex items-center gap-3 mb-3">
<Cpu className="w-5 h-5 text-blue-500" />
<span className="text-sm font-semibold text-blue-700 dark:text-blue-300">
AI Recommendation
</span>
<ConfidenceIndicator confidence={request.aiRecommendations[0].confidenceScore} />
</div>
<div className="space-y-3">
{request.aiRecommendations.slice(0, 2).map((rec, index) => (
<div key={index} className="flex justify-between items-center">
<div className="flex-1">
<p className="font-medium text-sm text-gray-900 dark:text-gray-100">
{rec.resourceName}
</p>
<p className="text-xs text-gray-600 dark:text-gray-400">
{rec.reasoningFactors[0]}
</p>
</div>
<div className="flex items-center gap-4 text-sm">
<span className="text-green-600 dark:text-green-400 font-medium">
${rec.estimatedCost}
</span>
<span className="text-blue-600 dark:text-blue-400">
{rec.fulfillmentTimeline}
</span>
</div>
</div>
))}
</div>
<div className="mt-4 flex gap-2">
<motion.button
onClick={() => onApprove(request.id, request.aiRecommendations![0])}
className="btn-primary text-xs px-4 py-2 flex items-center gap-2"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Approve AI Recommendation
</motion.button>
<button
onClick={() => onModify(request.id)}
className="btn-secondary text-xs px-4 py-2"
>
Modify
</button>
</div>
</motion.div>
)}
<div className="flex justify-between items-center">
<div className="flex gap-2">
<LocationBadge location={request.location.city} />
</div>
<div className="flex gap-2">
{!showAIRecommendations && (
<span className="text-xs text-gray-500 dark:text-gray-400">
Processing...
</span>
)}
</div>
</div>
</motion.div>
)
}
function UrgencyBadge({ urgency }: { urgency: UrgencyLevel }) {
const colors = {
emergency: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300',
high: 'bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300',
medium: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300',
low: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300'
}
return (
<span className={`px-2 py-1 rounded-full text-xs font-medium ${colors[urgency]}`}>
{urgency.toUpperCase()}
</span>
)
}
function CategoryBadge({ category }: { category: AssistanceCategory }) {
return (
<span className="px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
{category.replace('-', ' ').toUpperCase()}
</span>
)
}
function StatusBadge({ status }: { status: string }) {
const colors = {
'approved': 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300',
'auto-approved': 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300',
'under-review': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300',
'pending': 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'
}
return (
<span className={`px-2 py-1 rounded-full text-xs font-medium ${colors[status as keyof typeof colors] || colors.pending}`}>
{status.replace('-', ' ').toUpperCase()}
</span>
)
}
function LocationBadge({ location }: { location: string }) {
return (
<span className="px-2 py-1 rounded-full text-xs bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400">
📍 {location}
</span>
)
}
function ConfidenceIndicator({ confidence }: { confidence: number }) {
const getColor = (conf: number) => {
if (conf >= 0.8) return 'text-green-500'
if (conf >= 0.6) return 'text-yellow-500'
return 'text-red-500'
}
return (
<div className="flex items-center gap-2">
<div className="w-20 bg-gray-200 dark:bg-gray-600 rounded-full h-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-300"
style={{ width: `${confidence * 100}%` }}
/>
</div>
<span className={`text-xs font-bold ${getColor(confidence)}`}>
{Math.round(confidence * 100)}%
</span>
</div>
)
}
function AIPerformanceMetrics() {
const [metrics, setMetrics] = useState<AIMetrics>({
accuracyRate: 0.87,
accuracyTrend: 2.3,
avgProcessingTime: 1.8,
speedTrend: -0.5,
autoApprovalRate: 0.68,
automationTrend: 5.2,
impactPredictionAccuracy: 0.82,
impactTrend: 1.8,
totalRequestsProcessed: 247,
successfulMatches: 213
})
return (
<div className="ai-performance-metrics mt-8">
<h4 className="text-lg font-semibold mb-6 text-gray-900 dark:text-gray-100 flex items-center gap-2">
<Activity className="w-5 h-5 text-blue-500" />
AI Performance Dashboard
</h4>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
<MetricCard
title="Accuracy Rate"
value={`${(metrics.accuracyRate * 100).toFixed(1)}%`}
trend={metrics.accuracyTrend}
color="green"
icon={<TrendingUp className="w-6 h-6" />}
/>
<MetricCard
title="Avg Processing Time"
value={`${metrics.avgProcessingTime}s`}
trend={metrics.speedTrend}
color="blue"
icon={<Activity className="w-6 h-6" />}
/>
<MetricCard
title="Auto-Approval Rate"
value={`${(metrics.autoApprovalRate * 100).toFixed(1)}%`}
trend={metrics.automationTrend}
color="purple"
icon={<Cpu className="w-6 h-6" />}
/>
<MetricCard
title="Impact Accuracy"
value={`${(metrics.impactPredictionAccuracy * 100).toFixed(1)}%`}
trend={metrics.impactTrend}
color="orange"
icon={<Users className="w-6 h-6" />}
/>
</div>
<div className="mt-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
<div className="flex justify-between items-center text-sm">
<span className="text-gray-600 dark:text-gray-400">Total Requests Processed</span>
<span className="font-semibold text-gray-900 dark:text-gray-100">{metrics.totalRequestsProcessed}</span>
</div>
<div className="flex justify-between items-center text-sm mt-2">
<span className="text-gray-600 dark:text-gray-400">Successful Matches</span>
<span className="font-semibold text-green-600 dark:text-green-400">{metrics.successfulMatches}</span>
</div>
</div>
</div>
)
}
interface MetricCardProps {
title: string
value: string
trend: number
color: 'green' | 'blue' | 'purple' | 'orange'
icon: React.ReactNode
}
function MetricCard({ title, value, trend, color, icon }: MetricCardProps) {
const colorClasses = {
green: 'bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800',
blue: 'bg-blue-50 border-blue-200 dark:bg-blue-900/20 dark:border-blue-800',
purple: 'bg-purple-50 border-purple-200 dark:bg-purple-900/20 dark:border-purple-800',
orange: 'bg-orange-50 border-orange-200 dark:bg-orange-900/20 dark:border-orange-800'
}
const iconColors = {
green: 'text-green-500',
blue: 'text-blue-500',
purple: 'text-purple-500',
orange: 'text-orange-500'
}
return (
<motion.div
className={`metric-card p-4 rounded-xl border ${colorClasses[color]}`}
whileHover={{ scale: 1.02, y: -2 }}
>
<div className="flex items-center justify-between mb-3">
<div className={iconColors[color]}>{icon}</div>
<div className={`text-xs px-2 py-1 rounded-full ${
trend > 0
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300'
: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
}`}>
{trend > 0 ? '↗' : '↘'} {Math.abs(trend)}%
</div>
</div>
<div>
<p className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-1">{value}</p>
<p className="text-sm text-gray-600 dark:text-gray-400">{title}</p>
</div>
</motion.div>
)
}
function NewRequestForm({ onSubmit }: { onSubmit: (request: Omit<StudentRequest, 'id' | 'submittedAt'>) => void }) {
const [formData, setFormData] = useState({
studentName: '',
description: '',
category: 'clothing' as AssistanceCategory,
urgency: 'medium' as UrgencyLevel,
city: '',
state: 'TX',
zipCode: ''
})
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onSubmit({
studentId: `std-${Date.now()}`,
studentName: formData.studentName,
description: formData.description,
category: formData.category,
urgency: formData.urgency,
location: {
city: formData.city,
state: formData.state,
zipCode: formData.zipCode
},
constraints: {
timeframe: 'within-week',
deliveryMethod: 'any',
privacyLevel: 'semi-anonymous'
}
})
// Reset form
setFormData({
studentName: '',
description: '',
category: 'clothing',
urgency: 'medium',
city: '',
state: 'TX',
zipCode: ''
})
}
return (
<motion.div
className="new-request-form mt-8 p-6 bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
<h4 className="text-md font-semibold mb-4 text-gray-900 dark:text-gray-100">Submit New Request</h4>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<input
type="text"
placeholder="Student Name"
value={formData.studentName}
onChange={(e) => setFormData(prev => ({ ...prev, studentName: e.target.value }))}
className="input-field"
required
/>
<select
value={formData.category}
onChange={(e) => setFormData(prev => ({ ...prev, category: e.target.value as AssistanceCategory }))}
className="input-field"
>
<option value="clothing">Clothing</option>
<option value="school-supplies">School Supplies</option>
<option value="food-assistance">Food Assistance</option>
<option value="transportation">Transportation</option>
<option value="emergency-housing">Emergency Housing</option>
<option value="medical-needs">Medical Needs</option>
<option value="technology">Technology</option>
<option value="other">Other</option>
</select>
</div>
<textarea
placeholder="Describe the assistance needed..."
value={formData.description}
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
className="input-field h-24 resize-none"
required
/>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<select
value={formData.urgency}
onChange={(e) => setFormData(prev => ({ ...prev, urgency: e.target.value as UrgencyLevel }))}
className="input-field"
>
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
<option value="emergency">Emergency</option>
</select>
<input
type="text"
placeholder="City"
value={formData.city}
onChange={(e) => setFormData(prev => ({ ...prev, city: e.target.value }))}
className="input-field"
required
/>
<input
type="text"
placeholder="ZIP Code"
value={formData.zipCode}
onChange={(e) => setFormData(prev => ({ ...prev, zipCode: e.target.value }))}
className="input-field"
required
/>
</div>
<motion.button
type="submit"
className="btn-primary w-full"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
Submit Request for AI Analysis
</motion.button>
</form>
</motion.div>
)
}
function getInsightColor(type: string): string {
switch (type) {
case 'anomaly': return 'bg-red-400'
case 'optimization': return 'bg-green-400'
case 'trend': return 'bg-blue-400'
case 'prediction': return 'bg-purple-400'
case 'recommendation': return 'bg-yellow-400'
default: return 'bg-gray-400'
}
}
export default AIAssistancePortal