Spaces:
Paused
Paused
| import { Router } from 'express'; | |
| import { PalRepository } from './palRepository.js'; | |
| import { PalEventInput, PalBoardAction } from '@widget-tdc/mcp-types'; | |
| export const palRouter = Router(); | |
| const palRepo = new PalRepository(); | |
| // Record an event | |
| palRouter.post('/event', async (req, res) => { | |
| try { | |
| const event: PalEventInput = req.body; | |
| if (!event.userId || !event.orgId || !event.eventType) { | |
| return res.status(400).json({ | |
| error: 'Missing required fields: userId, orgId, eventType', | |
| }); | |
| } | |
| const eventId = await palRepo.recordEvent(event); | |
| res.json({ | |
| success: true, | |
| eventId, | |
| }); | |
| } catch (error: any) { | |
| console.error('PAL event error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message, | |
| }); | |
| } | |
| }); | |
| // Get recommendations | |
| palRouter.get('/recommendations', async (req, res) => { | |
| try { | |
| const { userId, orgId } = req.query; | |
| if (!userId || !orgId) { | |
| return res.status(400).json({ | |
| error: 'Missing required query params: userId, orgId', | |
| }); | |
| } | |
| // Get user profile | |
| let profile = await palRepo.getUserProfile(userId as string, orgId as string); | |
| if (!profile) { | |
| // Create default profile | |
| await palRepo.createUserProfile(userId as string, orgId as string); | |
| profile = await palRepo.getUserProfile(userId as string, orgId as string); | |
| } | |
| // Get focus windows | |
| const focusWindows = await palRepo.getFocusWindows(userId as string, orgId as string); | |
| // Get recent events | |
| const recentEvents = await palRepo.getRecentEvents(userId as string, orgId as string, 10); | |
| // Get stress level distribution | |
| const stressDistribution = await palRepo.getStressLevelDistribution(userId as string, orgId as string, 24); | |
| // Simple heuristic: if high stress events in last 24h, suggest muting | |
| const highStressEntry = stressDistribution.find((d: any) => d.detected_stress_level >= 7); | |
| const highStressCount = highStressEntry?.count || 0; | |
| const boardAdjustments: PalBoardAction[] = []; | |
| const reminders: string[] = []; | |
| if (highStressCount > 3) { | |
| boardAdjustments.push({ | |
| actionType: 'mute_notifications', | |
| message: 'You seem stressed. Would you like to mute notifications for the next hour?', | |
| }); | |
| } | |
| // Check current time against focus windows | |
| const now = new Date(); | |
| const currentWeekday = now.getDay() || 7; // Sunday = 7 | |
| const currentHour = now.getHours(); | |
| const currentFocusWindow = focusWindows.find((fw: any) => | |
| fw.weekday === currentWeekday && | |
| fw.start_hour <= currentHour && | |
| fw.end_hour > currentHour | |
| ); | |
| if (currentFocusWindow) { | |
| boardAdjustments.push({ | |
| actionType: 'isolate_widget_view', | |
| message: 'You\'re in a focus window. Showing only essential widgets.', | |
| }); | |
| } | |
| // Add contextual reminders based on recent events | |
| const meetingEvents = recentEvents.filter((e: any) => e.event_type === 'meeting'); | |
| if (meetingEvents.length > 5) { | |
| reminders.push('You have many meetings scheduled. Consider blocking focus time.'); | |
| } | |
| res.json({ | |
| success: true, | |
| userId, | |
| orgId, | |
| boardAdjustments, | |
| reminders, | |
| focusWindow: currentFocusWindow || null, | |
| profile: { | |
| preferenceTone: profile?.preference_tone || 'neutral', | |
| }, | |
| }); | |
| } catch (error: any) { | |
| console.error('PAL recommendations error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message, | |
| }); | |
| } | |
| }); | |
| // Get user profile | |
| palRouter.get('/profile', async (req, res) => { | |
| try { | |
| const { userId, orgId } = req.query; | |
| if (!userId || !orgId) { | |
| return res.status(400).json({ | |
| error: 'Missing required query params: userId, orgId', | |
| }); | |
| } | |
| let profile = await palRepo.getUserProfile(userId as string, orgId as string); | |
| if (!profile) { | |
| // Create default profile | |
| await palRepo.createUserProfile(userId as string, orgId as string); | |
| profile = await palRepo.getUserProfile(userId as string, orgId as string); | |
| } | |
| res.json({ | |
| success: true, | |
| profile, | |
| }); | |
| } catch (error: any) { | |
| console.error('Get profile error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message, | |
| }); | |
| } | |
| }); | |
| // Update user profile | |
| palRouter.put('/profile', async (req, res) => { | |
| try { | |
| const { userId, orgId, preferenceTone } = req.body; | |
| if (!userId || !orgId || !preferenceTone) { | |
| return res.status(400).json({ | |
| error: 'Missing required fields: userId, orgId, preferenceTone', | |
| }); | |
| } | |
| await palRepo.updateUserProfile(userId, orgId, preferenceTone); | |
| res.json({ | |
| success: true, | |
| }); | |
| } catch (error: any) { | |
| console.error('Update profile error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message, | |
| }); | |
| } | |
| }); | |
| // Add focus window | |
| palRouter.post('/focus-window', async (req, res) => { | |
| try { | |
| const { userId, orgId, weekday, startHour, endHour } = req.body; | |
| if (!userId || !orgId || weekday === undefined || startHour === undefined || endHour === undefined) { | |
| return res.status(400).json({ | |
| error: 'Missing required fields: userId, orgId, weekday, startHour, endHour', | |
| }); | |
| } | |
| const windowId = await palRepo.addFocusWindow(userId, orgId, weekday, startHour, endHour); | |
| res.json({ | |
| success: true, | |
| windowId, | |
| }); | |
| } catch (error: any) { | |
| console.error('Add focus window error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message, | |
| }); | |
| } | |
| }); | |