Files
pomodoro/server/routes.ts
ingecarlosgutie 80595e7002 Set up project configuration and base UI components
Initializes the project with necessary configurations, including Replit settings and a .gitignore file. It also introduces foundational UI components for the application, such as buttons, dialogs, and layout elements, likely for the Pomodoro timer application.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 59a5ae27-3c71-459b-b42f-fe14121bf9c3
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3007b6f6-d03b-45e1-9ed1-7ce8de18ea24/59a5ae27-3c71-459b-b42f-fe14121bf9c3/Uupe4F4
2025-08-31 20:11:52 +00:00

172 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Server> {
// 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";
}