Add Prayer Reminder, Dhikr Counter, Qibla Finder, Halal Monitor updates
- Prayer reminder: /prayer page with 5 daily times, countdown, alAdhan API - Dhikr counter: /dhikr page with 3 presets, custom counter, haptics, DB tracking - Qibla finder: /qibla page with compass SVG, DeviceOrientation, bearing to Kaaba - Halal Monitor: Leaflet + Google Places, 26 KL markers, geolocation, distance sort - BottomNav: added clock icon for prayer - Prisma: schema updates for DhikrSession, DhikrDay, DailyStreak, XpTransaction - Geo utility module - Weighted Traefik routing: Contabo 99% / Synology 1% cold standby
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { requireAuth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
|
||||
function todayStr(): string {
|
||||
const d = new Date();
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
function getDhikrType(raw: string): string {
|
||||
const allowed = ["subhanallah", "alhamdulillah", "allahuakbar", "custom"];
|
||||
return allowed.includes(raw) ? raw : "custom";
|
||||
}
|
||||
|
||||
// ── GET /api/dhikr/stats — Today's dhikr stats ────────────────────────────
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const jwt = await requireAuth(request);
|
||||
if (!jwt) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const today = todayStr();
|
||||
|
||||
let dayStats = await prisma.dhikrDay.findUnique({
|
||||
where: { userId_date: { userId: jwt.userId, date: today } },
|
||||
});
|
||||
|
||||
// Recent sessions (last 20)
|
||||
const recentSessions = await prisma.dhikrSession.findMany({
|
||||
where: { userId: jwt.userId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
take: 20,
|
||||
select: { id: true, type: true, count: true, createdAt: true },
|
||||
});
|
||||
|
||||
if (!dayStats) {
|
||||
dayStats = await prisma.dhikrDay.create({
|
||||
data: { userId: jwt.userId, date: today },
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ dayStats, recentSessions });
|
||||
}
|
||||
|
||||
// ── POST /api/dhikr/stats — Log a dhikr session ────────────────────────────
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const jwt = await requireAuth(request);
|
||||
if (!jwt) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
let body: { type?: string; count?: number; target?: number; completed?: boolean };
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
||||
}
|
||||
|
||||
const type = getDhikrType(body.type || "custom");
|
||||
const count = Math.min(Math.max(body.count || 1, 1), 9999);
|
||||
const target = body.target || 33;
|
||||
const completed = body.completed !== false;
|
||||
const today = todayStr();
|
||||
|
||||
// Create session record
|
||||
const session = await prisma.dhikrSession.create({
|
||||
data: { userId: jwt.userId, type, count, target, completed },
|
||||
});
|
||||
|
||||
// Upsert today's aggregate
|
||||
const dayStats = await prisma.dhikrDay.upsert({
|
||||
where: { userId_date: { userId: jwt.userId, date: today } },
|
||||
create: {
|
||||
userId: jwt.userId,
|
||||
date: today,
|
||||
[type]: count,
|
||||
totalCount: count,
|
||||
sessionCount: 1,
|
||||
},
|
||||
update: {
|
||||
totalCount: { increment: count },
|
||||
sessionCount: { increment: 1 },
|
||||
[type]: { increment: count },
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ session, dayStats });
|
||||
}
|
||||
Reference in New Issue
Block a user