notification system implemented

need refinements
This commit is contained in:
dimitar committed 2026-03-11 06:07:16 +01:00
1 parent 612259f020
commit 1143f8ca02
33 files changed
+2221 -222

No files matched your search

+110 -29
View File
@@ -28,7 +28,7 @@ interface AttendanceStats {
thisMonth: number;
recentCheckIns: Array<{
id: string;
checkInTime: string;
checkInTime: string; // ISO string
checkOutTime: string | null;
type: string;
duration?: number; // in minutes
@@ -134,8 +134,8 @@ export async function GET(request: NextRequest) {
)
.all(userId) as Array<{
id: string;
checkInTime: string;
checkOutTime: string | null;
checkInTime: string | number; // Can be ISO string or Unix timestamp
checkOutTime: string | number | null;
type: string;
}>;
@@ -144,13 +144,33 @@ export async function GET(request: NextRequest) {
// Get recent check-ins (last 10)
const recentCheckIns = attendanceRecords.slice(0, 10).map((record) => {
let duration: number | undefined;
// Convert Unix timestamps to milliseconds for calculation
const checkInMs =
typeof record.checkInTime === "number"
? record.checkInTime * 1000
: new Date(record.checkInTime).getTime();
if (record.checkOutTime) {
const checkIn = new Date(record.checkInTime).getTime();
const checkOut = new Date(record.checkOutTime).getTime();
duration = Math.round((checkOut - checkIn) / (1000 * 60)); // minutes
const checkOutMs =
typeof record.checkOutTime === "number"
? record.checkOutTime * 1000
: new Date(record.checkOutTime).getTime();
duration = Math.round((checkOutMs - checkInMs) / (1000 * 60)); // minutes
}
// Return with ISO string timestamps for consistency
return {
...record,
id: record.id,
checkInTime: new Date(checkInMs).toISOString(),
checkOutTime: record.checkOutTime
? new Date(
typeof record.checkOutTime === "number"
? record.checkOutTime * 1000
: record.checkOutTime,
).toISOString()
: null,
type: record.type,
duration,
};
});
@@ -161,15 +181,25 @@ export async function GET(request: NextRequest) {
startOfWeek.setDate(now.getDate() - now.getDay()); // Sunday
startOfWeek.setHours(0, 0, 0, 0);
const thisWeekCheckIns = attendanceRecords.filter(
(r) => new Date(r.checkInTime) >= startOfWeek,
).length;
const thisWeekCheckIns = attendanceRecords.filter((r) => {
// checkInTime is Unix timestamp in seconds, convert to milliseconds
const checkInDate =
typeof r.checkInTime === "number"
? new Date(r.checkInTime * 1000)
: new Date(r.checkInTime);
return checkInDate >= startOfWeek;
}).length;
// Calculate this month's check-ins
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const thisMonthCheckIns = attendanceRecords.filter(
(r) => new Date(r.checkInTime) >= startOfMonth,
).length;
const thisMonthCheckIns = attendanceRecords.filter((r) => {
// checkInTime is Unix timestamp in seconds, convert to milliseconds
const checkInDate =
typeof r.checkInTime === "number"
? new Date(r.checkInTime * 1000)
: new Date(r.checkInTime);
return checkInDate >= startOfMonth;
}).length;
// Calculate current streak (consecutive days with check-ins)
let currentStreak = 0;
@@ -177,14 +207,24 @@ export async function GET(request: NextRequest) {
let tempStreak = 0;
let lastDate: Date | null = null;
const sortedRecords = [...attendanceRecords].sort(
(a, b) =>
new Date(b.checkInTime).getTime() - new Date(a.checkInTime).getTime(),
);
const sortedRecords = [...attendanceRecords].sort((a, b) => {
const dateA =
typeof a.checkInTime === "number"
? new Date(a.checkInTime * 1000)
: new Date(a.checkInTime);
const dateB =
typeof b.checkInTime === "number"
? new Date(b.checkInTime * 1000)
: new Date(b.checkInTime);
return dateB.getTime() - dateA.getTime();
});
const uniqueDays = new Set<string>();
sortedRecords.forEach((record) => {
const date = new Date(record.checkInTime);
const date =
typeof record.checkInTime === "number"
? new Date(record.checkInTime * 1000)
: new Date(record.checkInTime);
const dateStr = date.toISOString().split("T")[0];
uniqueDays.add(dateStr);
});
@@ -257,10 +297,32 @@ export async function GET(request: NextRequest) {
weekEnd.setDate(weekStart.getDate() + 7);
const weekCheckIns = attendanceRecords.filter((r) => {
const checkInDate = new Date(r.checkInTime);
// checkInTime is Unix timestamp in seconds, convert to milliseconds
const checkInDate =
typeof r.checkInTime === "number"
? new Date(r.checkInTime * 1000)
: new Date(r.checkInTime);
return checkInDate >= weekStart && checkInDate < weekEnd;
}).length;
log.debug("Weekly calculation", {
weekIndex: i,
weekStart: weekStart.toISOString(),
weekEnd: weekEnd.toISOString(),
weekCheckIns,
sampleRecord: attendanceRecords[0]
? {
checkInTime: attendanceRecords[0].checkInTime,
converted:
typeof attendanceRecords[0].checkInTime === "number"
? new Date(
attendanceRecords[0].checkInTime * 1000,
).toISOString()
: attendanceRecords[0].checkInTime,
}
: null,
});
const weekGoalsCompleted = db
.prepare(
`SELECT COUNT(*) as count
@@ -308,14 +370,42 @@ export async function GET(request: NextRequest) {
userId,
totalGoals: goalStats.total,
totalCheckIns,
weeklyTrendLength: weeklyTrend.length,
weeklyTrendSample: weeklyTrend[0],
});
// Return statistics in standardized format that matches mobile app expectations
return NextResponse.json({
success: true,
data: {
statistics: {
userId,
...statistics,
goals: {
totalGoals: goalStats.total,
activeGoals: goalStats.active,
completedGoals: goalStats.completed,
averageProgress: goalStats.avgProgress,
goalsByType: Object.entries(goalStats.byType).map(
([goalType, count]) => ({
goalType,
count,
}),
),
},
attendance: {
totalCheckIns: attendanceStats.totalCheckIns,
currentStreak: attendanceStats.currentStreak,
longestStreak: attendanceStats.longestStreak,
checkInsThisWeek: attendanceStats.thisWeek,
checkInsThisMonth: attendanceStats.thisMonth,
recentCheckIns: attendanceStats.recentCheckIns,
},
weeklyTrend: weeklyTrend.map((week) => ({
weekLabel: week.week,
checkIns: week.checkIns,
goalsCompleted: week.goalsCompleted,
averageProgress: week.avgProgress,
})),
},
},
meta: {
@@ -323,15 +413,6 @@ export async function GET(request: NextRequest) {
requestId: `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
},
});
return NextResponse.json({
success: true,
data: { statistics },
meta: {
timestamp: new Date().toISOString(),
requestId: `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
},
});
} catch (error) {
log.error("Failed to fetch statistics", error);
return NextResponse.json(