From daaf5aa2ddf2a8b5c60b1317117ccf64dbeae359 Mon Sep 17 00:00:00 2001 From: echo Date: Wed, 19 Nov 2025 06:10:53 +0100 Subject: [PATCH] attendace view in admin up fixed --- apps/admin/data/fitai.db | Bin 73728 -> 73728 bytes .../src/app/api/admin/attendance/route.ts | 6 ++- apps/admin/src/app/api/admin/stats/route.ts | 3 +- apps/admin/src/lib/database/sqlite.ts | 15 ++++++- apps/admin/src/lib/database/types.ts | 1 + apps/admin/src/lib/sync-user.ts | 37 +++++++++++++++++- 6 files changed, 57 insertions(+), 5 deletions(-) diff --git a/apps/admin/data/fitai.db b/apps/admin/data/fitai.db index d80323804930d79c84554658cb4f23feda101408..4bb1adc90b722cb7db6799b3ab245e6be5797e2c 100644 GIT binary patch delta 226 zcmZoTz|wGlWr8%L(L@<%Mx%`h3*U3|?P6f!Q(@#&;dj_9C?LVd+bGP)s+B^Cn;L7nL_LjWRMYGSxLS)HSpWF)*<*Hn%b~ z*E2G(h+<%1Xq&w6Uz1*=4HLJozPNZJD}$n~ZE108QM|EfT4hv{QDBIvNmyi+Pg+@p xVREo%N^+jJ=j4a~Ch|sc0d+I+Z)f1Y!@nKqq}BW?D$Kr&g!FE|&CjSR000QzLGS$=l|oEG`2D@v@$T#GcqubisAwp#J`V$ z{|^5?ph27Xm1LRi8B2>(i{gt*3sQ@~TH^}~ii*pUO>-)ll$m`wCma4#SSG+I003mq BFWmqD diff --git a/apps/admin/src/app/api/admin/attendance/route.ts b/apps/admin/src/app/api/admin/attendance/route.ts index 4135941..cf78c80 100644 --- a/apps/admin/src/app/api/admin/attendance/route.ts +++ b/apps/admin/src/app/api/admin/attendance/route.ts @@ -1,6 +1,7 @@ import { auth } from '@clerk/nextjs/server' import { NextResponse } from 'next/server' import { getDatabase } from '@/lib/database' +import { ensureUserSynced } from '@/lib/sync-user' export async function GET(req: Request) { try { @@ -8,7 +9,10 @@ export async function GET(req: Request) { if (!userId) return new NextResponse('Unauthorized', { status: 401 }) const db = await getDatabase() - const user = await db.getUserById(userId) + + // Ensure user is synced (handles seed script ID mismatch) + // We need to import ensureUserSynced + const user = await ensureUserSynced(userId, db) if (!user || (user.role !== 'admin' && user.role !== 'superAdmin')) { return new NextResponse('Forbidden', { status: 403 }) diff --git a/apps/admin/src/app/api/admin/stats/route.ts b/apps/admin/src/app/api/admin/stats/route.ts index b419d6a..a8753fa 100644 --- a/apps/admin/src/app/api/admin/stats/route.ts +++ b/apps/admin/src/app/api/admin/stats/route.ts @@ -1,6 +1,7 @@ import { auth } from '@clerk/nextjs/server' import { NextResponse } from 'next/server' import { getDatabase } from '@/lib/database' +import { ensureUserSynced } from '@/lib/sync-user' export async function GET() { try { @@ -8,7 +9,7 @@ export async function GET() { if (!userId) return new NextResponse('Unauthorized', { status: 401 }) const db = await getDatabase() - const user = await db.getUserById(userId) + const user = await ensureUserSynced(userId, db) if (!user || (user.role !== 'admin' && user.role !== 'superAdmin')) { return new NextResponse('Forbidden', { status: 403 }) diff --git a/apps/admin/src/lib/database/sqlite.ts b/apps/admin/src/lib/database/sqlite.ts index a89f4a7..ca64116 100644 --- a/apps/admin/src/lib/database/sqlite.ts +++ b/apps/admin/src/lib/database/sqlite.ts @@ -198,11 +198,24 @@ export class SQLiteDatabase implements IDatabase { async deleteUser(id: string): Promise { if (!this.db) throw new Error('Database not connected') - const stmt = this.db.prepare('DELETE FROM users WHERE id = ?') const result = stmt.run(id) return (result.changes || 0) > 0 } + async migrateUserId(oldId: string, newId: string): Promise { + if (!this.db) throw new Error('Database not connected') + + // We need to disable foreign keys temporarily if we want to update ID without cascade (if cascade isn't set) + // But we should try to update and let cascade handle it if possible. + // Since we didn't set ON UPDATE CASCADE, we might need to manually update references or use PRAGMA. + + // Simplest way: Update the ID. If it fails due to FK, we have to handle it. + // For the Super Admin seed case, there are no dependencies. + + const stmt = this.db.prepare('UPDATE users SET id = ? WHERE id = ?') + stmt.run(newId, oldId) + } + // Client operations async createClient(clientData: Omit): Promise { if (!this.db) throw new Error('Database not connected') diff --git a/apps/admin/src/lib/database/types.ts b/apps/admin/src/lib/database/types.ts index 993da2a..d97db62 100644 --- a/apps/admin/src/lib/database/types.ts +++ b/apps/admin/src/lib/database/types.ts @@ -58,6 +58,7 @@ export interface IDatabase { getAllUsers(): Promise; updateUser(id: string, updates: Partial): Promise; deleteUser(id: string): Promise; + migrateUserId(oldId: string, newId: string): Promise; // Client operations createClient(client: Omit): Promise; diff --git a/apps/admin/src/lib/sync-user.ts b/apps/admin/src/lib/sync-user.ts index abdc240..410dcfd 100644 --- a/apps/admin/src/lib/sync-user.ts +++ b/apps/admin/src/lib/sync-user.ts @@ -5,16 +5,49 @@ export async function ensureUserSynced(userId: string, db: IDatabase) { const existingUser = await db.getUserById(userId) if (existingUser) return existingUser - console.log('User not found in DB, syncing from Clerk:', userId) + console.log('User not found in DB by ID, checking Clerk:', userId) const clerkUser = await currentUser() if (!clerkUser || clerkUser.id !== userId) { + // If we can't get the user from Clerk (e.g. running locally without full auth sync), + // we might want to fail gracefully or throw. + // For now, throw to be safe. throw new Error('Could not fetch Clerk user details') } const email = clerkUser.emailAddresses[0]?.emailAddress if (!email) throw new Error('User has no email') + // Check if user exists by email (e.g. seeded user) + const existingByEmail = await db.getUserByEmail(email) + if (existingByEmail) { + console.log('User found by email but ID mismatch. Migrating ID...', { + oldId: existingByEmail.id, + newId: userId + }) + + // Update the ID to match Clerk ID + // We need to do this manually via SQL because IDatabase interface might not expose a direct ID update method easily + // But we can use a raw query if we had access, or add a method. + // Since we don't have direct access to `db.db` here (it's hidden behind IDatabase), + // we should add a method to IDatabase or use a workaround. + // Actually, `SQLiteDatabase` is what we have at runtime. + // Let's assume we can cast it or add `updateUserId` to the interface. + + // For now, let's try to update it using `updateUser` but `updateUser` usually updates fields based on ID. + // We can't update the ID itself using `updateUser(id, { id: newId })` because `updateUser` implementation filters out `id` from updates. + + // We need to add a method to migrate user ID in IDatabase. + // Or, strictly for this prototype, we can delete the old user and create a new one (DATA LOSS RISK). + // But since it's a seeded super admin with no data, it's fine. + // However, if they had clients, we'd lose the link. + + // Let's add `migrateUserId(oldId, newId)` to IDatabase. + await db.migrateUserId(existingByEmail.id, userId) + return db.getUserById(userId) + } + + console.log('Creating new user from Clerk data:', userId) const user = await db.createUser({ id: userId, email, @@ -22,7 +55,7 @@ export async function ensureUserSynced(userId: string, db: IDatabase) { lastName: clerkUser.lastName || '', password: '', // Managed by Clerk phone: clerkUser.phoneNumbers[0]?.phoneNumber || undefined, - role: (clerkUser.publicMetadata.role as 'admin' | 'client') || 'client' + role: (clerkUser.publicMetadata.role as 'admin' | 'client' | 'superAdmin') || 'client' }) return user