import type { Express } from "express"; import { createServer, type Server } from "http"; import { storage } from "./storage"; import { insertSessionSchema } from "@shared/schema"; import { z } from "zod"; export async function registerRoutes(app: Express): Promise { // Create session app.post("/api/sessions", async (req, res) => { try { const sessionData = insertSessionSchema.parse(req.body); const session = await storage.createSession(sessionData); // Update device activity await storage.updateDeviceProfileActivity(sessionData.deviceId); res.status(201).json({ ok: true, id: session.id }); } catch (error) { if (error instanceof z.ZodError) { return res.status(400).json({ message: "Invalid session data", errors: error.errors }); } res.status(500).json({ message: "Failed to create session" }); } }); // Get suggestions for device app.get("/api/suggestions", async (req, res) => { try { const deviceId = req.query.deviceId as string; if (!deviceId) { return res.status(400).json({ message: "Device ID is required" }); } // Get recent sessions (last 7 days) const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); const recentSessions = await storage.getSessionsInDateRange(deviceId, sevenDaysAgo, new Date()); const suggestions = generateSuggestions(recentSessions); res.json({ suggestions }); } catch (error) { res.status(500).json({ message: "Failed to get suggestions" }); } }); // Get stats summary app.get("/api/stats/summary", async (req, res) => { try { const deviceId = req.query.deviceId as string; if (!deviceId) { return res.status(400).json({ message: "Device ID is required" }); } const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const last7dSessions = await storage.getSessionsInDateRange(deviceId, sevenDaysAgo, new Date()); const last30dSessions = await storage.getSessionsInDateRange(deviceId, thirtyDaysAgo, new Date()); const last7d = calculateStats(last7dSessions.filter(s => s.type === 'focus')); const last30d = calculateStats(last30dSessions.filter(s => s.type === 'focus')); res.json({ last7d, last30d }); } catch (error) { res.status(500).json({ message: "Failed to get stats" }); } }); const httpServer = createServer(app); return httpServer; } function generateSuggestions(sessions: any[]) { const focusSessions = sessions.filter(s => s.type === 'focus'); if (focusSessions.length === 0) { return [ { minutes: 25, reason: "Classic Pomodoro technique - great for starting out." }, { minutes: 15, reason: "Short sprints help build focus habits." }, { minutes: 40, reason: "Extended sessions for deep work." } ]; } const completedSessions = focusSessions.filter(s => s.completed); const completionRate = completedSessions.length / focusSessions.length; // Analyze successful durations const successfulDurations = completedSessions.map(s => s.intendedMinutes); const avgSuccessfulDuration = successfulDurations.reduce((a, b) => a + b, 0) / successfulDurations.length; // Analyze time of day patterns const now = new Date(); const currentHour = now.getHours(); const timeOfDaySuccess = getTimeOfDaySuccess(completedSessions, currentHour); const suggestions = []; // Primary suggestion based on completion rate if (completionRate >= 0.8) { suggestions.push({ minutes: Math.round(avgSuccessfulDuration), reason: `Your 7-day completion rate is ${Math.round(completionRate * 100)}% at ${Math.round(avgSuccessfulDuration)}m.` }); } else { suggestions.push({ minutes: 25, reason: "Classic 25min sessions have proven effective for most users." }); } // Time-based suggestion if (timeOfDaySuccess.avgDuration > 0) { suggestions.push({ minutes: Math.round(timeOfDaySuccess.avgDuration), reason: `${getTimeOfDayLabel(currentHour)}: best focus avg ${Math.round(timeOfDaySuccess.avgDuration - 3)}–${Math.round(timeOfDaySuccess.avgDuration + 3)}m.` }); } // Interruption-based suggestion const avgInterruptions = focusSessions.reduce((sum, s) => sum + s.interruptions, 0) / focusSessions.length; if (avgInterruptions > 1) { suggestions.push({ minutes: 15, reason: "High interruption periods benefit from short sprints." }); } else { suggestions.push({ minutes: 40, reason: "Low interruption environment - try longer deep work sessions." }); } return suggestions.slice(0, 3); } function calculateStats(focusSessions: any[]) { const completed = focusSessions.filter(s => s.completed); const completionRate = focusSessions.length > 0 ? completed.length / focusSessions.length : 0; const avgFocusMin = completed.length > 0 ? completed.reduce((sum, s) => sum + s.intendedMinutes, 0) / completed.length : 0; return { sessions: focusSessions.length, completionRate: Math.round(completionRate * 100) / 100, avgFocusMin: Math.round(avgFocusMin) }; } function getTimeOfDaySuccess(sessions: any[], currentHour: number) { const timeRangeSessions = sessions.filter(s => { const sessionHour = new Date(s.startedAt).getHours(); return Math.abs(sessionHour - currentHour) <= 2; }); if (timeRangeSessions.length === 0) { return { avgDuration: 0 }; } const avgDuration = timeRangeSessions.reduce((sum, s) => sum + s.intendedMinutes, 0) / timeRangeSessions.length; return { avgDuration }; } function getTimeOfDayLabel(hour: number) { if (hour >= 6 && hour < 12) return "Mornings"; if (hour >= 12 && hour < 17) return "Afternoons"; if (hour >= 17 && hour < 21) return "Evenings"; return "Late hours"; }