fix(routine): resolove routine blocker logic bug and white screen
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
feat(gamification): add streak bonuses, tooltip, and improved history details fix(ep): resolve double counting ep bug ui: update app icon and translations
This commit is contained in:
+45
-47
@@ -984,7 +984,7 @@ ${activeTasksWithLabels.slice(0, 5).map(t => `- [${t.priority}] [${t.label}] ${t
|
||||
// Award XP for creating a task
|
||||
if (req.user) {
|
||||
const source = req.body.parentTaskId ? 'create_subtask' : 'create_task';
|
||||
await gamificationService.awardXP((req.user as User).id, source);
|
||||
await gamificationService.awardXP((req.user as User).id, source, undefined, { taskId: task.id, taskTitle: task.title });
|
||||
}
|
||||
|
||||
await storage.createAuditLog({
|
||||
@@ -1017,10 +1017,10 @@ ${activeTasksWithLabels.slice(0, 5).map(t => `- [${t.priority}] [${t.label}] ${t
|
||||
if (updates.data.status === 'done' && previousTask.status !== 'done') {
|
||||
const isLate = previousTask.dueDate && new Date(previousTask.dueDate) < new Date();
|
||||
const source = isLate ? 'complete_task_late' : 'complete_task';
|
||||
await gamificationService.awardXP((req.user as User).id, source);
|
||||
await gamificationService.awardXP((req.user as User).id, source, undefined, { taskId: previousTask.id, taskTitle: previousTask.title });
|
||||
} else if (Object.keys(updates.data).length > 0) { // Only award if there are actual updates
|
||||
// Small points for any other update (title, description, etc)
|
||||
await gamificationService.awardXP((req.user as User).id, 'update_task');
|
||||
await gamificationService.awardXP((req.user as User).id, 'update_task', undefined, { taskId: previousTask.id, taskTitle: previousTask.title });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,56 +1131,33 @@ ${activeTasksWithLabels.slice(0, 5).map(t => `- [${t.priority}] [${t.label}] ${t
|
||||
|
||||
// Analytics API
|
||||
app.get("/api/analytics/weekly", async (req, res) => {
|
||||
// Return last 7 days. Key is 0-6 (Sun-Sat) or ISO date.
|
||||
// For simplicity, let's return day index relative to today or just standard day index (0=Sun)
|
||||
// To make it look "last 7 days" we can return relative indices
|
||||
const keys = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
// Better: Send localizable keys.
|
||||
// Day format: "day_1" (Mon) ... "day_7" (Sun) or just short codes the frontend can map
|
||||
|
||||
// We will send standard JS Day indices adjusted: 1 (Mon) - 7 (Sun) for "ISO Week" style or just 0-6
|
||||
// Let's send a `labelKey` that the frontend can translate.
|
||||
const data = [
|
||||
{ labelKey: 'mon', xp: Math.floor(Math.random() * 500) },
|
||||
{ labelKey: 'tue', xp: Math.floor(Math.random() * 500) },
|
||||
{ labelKey: 'wed', xp: Math.floor(Math.random() * 500) },
|
||||
{ labelKey: 'thu', xp: Math.floor(Math.random() * 500) },
|
||||
{ labelKey: 'fri', xp: Math.floor(Math.random() * 500) },
|
||||
{ labelKey: 'sat', xp: Math.floor(Math.random() * 500) },
|
||||
{ labelKey: 'sun', xp: Math.floor(Math.random() * 500) },
|
||||
];
|
||||
res.json(data);
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
try {
|
||||
const data = await gamificationService.getWeeklyAnalytics((req.user as User).id);
|
||||
res.json(data);
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: "Failed to fetch analytics" });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/analytics/yearly", async (req, res) => {
|
||||
const data = [
|
||||
{ labelKey: 'jan', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'feb', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'mar', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'apr', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'may', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'jun', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'jul', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'aug', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'sep', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'oct', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'nov', xp: Math.floor(Math.random() * 2000) },
|
||||
{ labelKey: 'dec', xp: Math.floor(Math.random() * 2000) },
|
||||
];
|
||||
res.json(data);
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
try {
|
||||
const data = await gamificationService.getYearlyAnalytics((req.user as User).id);
|
||||
res.json(data);
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: "Failed to fetch analytics" });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/analytics/monthly", async (req, res) => {
|
||||
// Return last 4-5 weeks with actual Calendar Week numbers
|
||||
// Mocking for now: Assume current week is ~50
|
||||
const currentWeek = 50;
|
||||
const data = [
|
||||
{ labelKey: (currentWeek - 3).toString(), xp: Math.floor(Math.random() * 800) },
|
||||
{ labelKey: (currentWeek - 2).toString(), xp: Math.floor(Math.random() * 800) },
|
||||
{ labelKey: (currentWeek - 1).toString(), xp: Math.floor(Math.random() * 800) },
|
||||
{ labelKey: currentWeek.toString(), xp: Math.floor(Math.random() * 800) },
|
||||
];
|
||||
res.json(data);
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
try {
|
||||
const data = await gamificationService.getMonthlyAnalytics((req.user as User).id);
|
||||
res.json(data);
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: "Failed to fetch analytics" });
|
||||
}
|
||||
});
|
||||
|
||||
// Gamification Logic Wrapper
|
||||
@@ -1349,6 +1326,27 @@ ${activeTasksWithLabels.slice(0, 5).map(t => `- [${t.priority}] [${t.label}] ${t
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/user/routine/:type/complete", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
const type = req.params.type;
|
||||
if (type !== 'morning' && type !== 'evening') return res.status(400).json({ error: "Invalid routine type" });
|
||||
|
||||
try {
|
||||
const updates: any = {};
|
||||
const now = new Date();
|
||||
if (type === 'morning') {
|
||||
updates.lastMorningRoutine = now;
|
||||
} else {
|
||||
updates.lastEveningRoutine = now;
|
||||
}
|
||||
|
||||
const updatedUser = await storage.updateUser((req.user as User).id, updates);
|
||||
res.json(updatedUser);
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: "Failed to complete routine" });
|
||||
}
|
||||
});
|
||||
|
||||
app.patch("/api/user/password", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user