import { pb } from "@/lib/pocketbase"; export interface User { id: string; created: string; updated: string; username: string; email: string; emailVisibility: boolean; verified: boolean; full_name?: string; avatar?: string; role?: string; isActive?: boolean; status?: string; // Added this field to support the backend status } export interface CreateUserData { username: string; email: string; password: string; passwordConfirm: string; full_name?: string; avatar?: string; role?: string; isActive?: boolean; emailVisibility?: boolean; } export interface UpdateUserData { username?: string; email?: string; emailVisibility?: boolean; full_name?: string; avatar?: string; role?: string; isActive?: boolean; } // Helper function to convert PocketBase record to User type const convertToUserType = (record: any, role: string = "admin"): User => { return { id: record.id, created: record.created, updated: record.updated, username: record.username, email: record.email, emailVisibility: record.emailVisibility, verified: record.verified, full_name: record.full_name, avatar: record.avatar, role: role, isActive: record.isActive, status: record.status, }; }; export const userService = { async getUsers(): Promise { try { // console.log("Calling getUsers API"); // Get both regular users and superadmins const regularUsers = await pb.collection('users').getList(1, 50, { sort: 'created', }); let superadminUsers: any = []; try { // Try to get superadmin users if exists superadminUsers = await pb.collection('_superusers').getList(1, 50, { sort: 'created', }); // console.log("Successfully fetched superadmin users:", superadminUsers); } catch (error) { // console.log("No superadmin collection or access rights:", error); } // Combine both user types and mark superadmins const allUsers = [ ...regularUsers.items.map((user: any) => convertToUserType(user, user.role || "admin")), ...superadminUsers.items.map((user: any) => convertToUserType(user, "superadmin")) ]; // console.log("Combined users list:", allUsers); return allUsers; } catch (error) { // console.error("Failed to fetch users:", error); return null; } }, async getUser(id: string): Promise { try { // console.log(`Fetching user with ID: ${id}`); // Try fetching from regular users first try { const user = await pb.collection('users').getOne(id); // console.log("User fetch result (regular user):", user); return convertToUserType(user, user.role || "admin"); } catch (error) { // console.log("User not found in regular users, trying superadmin collection"); } // If not found, try in superadmins try { const user = await pb.collection('_superusers').getOne(id); // console.log("User fetch result (superadmin):", user); return convertToUserType(user, "superadmin"); } catch (error) { // console.log("User not found in superadmin collection either"); return null; } } catch (error) { // console.error(`Failed to fetch user ${id}:`, error); return null; } }, async updateUser(id: string, data: UpdateUserData): Promise { try { // Create a clean update object - remove undefined and empty string values const cleanData: Record = {}; Object.entries(data).forEach(([key, value]) => { // Skip undefined values and empty strings for non-required fields if (value !== undefined && !(typeof value === 'string' && value.trim() === '')) { cleanData[key] = value; } }); // console.log("Updating user with clean data:", cleanData); // If there's nothing to update, return the current user if (Object.keys(cleanData).length === 0) { // console.log("No changes to update"); const currentUser = await this.getUser(id); return currentUser; } // Special handling for role changes between admin and superadmin const roleChange = cleanData.role !== undefined; const targetRole = roleChange ? cleanData.role : null; // Remove role from regular update if it's being changed if (roleChange) { delete cleanData.role; } // Handle email updates with proper error handling const hasEmailChange = cleanData.email !== undefined; const emailToUpdate = hasEmailChange ? cleanData.email : null; let updatedUser: User | null = null; // First, determine if this is currently a regular user or superadmin let isCurrentlySuperadmin = false; try { await pb.collection('_superusers').getOne(id); isCurrentlySuperadmin = true; } catch (error) { // Not in superadmin collection } // If we're changing the role and need to move the user between collections if (roleChange && ((isCurrentlySuperadmin && targetRole !== "superadmin") || (!isCurrentlySuperadmin && targetRole === "superadmin"))) { // Get the full user data before changing collections const currentUser = await this.getUser(id); if (!currentUser) { throw new Error("User not found during role change"); } // Prepare user data for transfer const transferData = { username: currentUser.username, email: currentUser.email, emailVisibility: currentUser.emailVisibility || true, full_name: currentUser.full_name || "", avatar: currentUser.avatar || "", isActive: currentUser.isActive !== false, ...cleanData }; try { if (targetRole === "superadmin") { // Create in superadmin collection const newSuperUser = await pb.collection('_superusers').create(transferData); // Delete from regular users await pb.collection('users').delete(id); updatedUser = convertToUserType(newSuperUser, "superadmin"); } else { // Create in regular users collection const newRegularUser = await pb.collection('users').create(transferData); // Delete from superadmin await pb.collection('_superusers').delete(id); updatedUser = convertToUserType(newRegularUser, "admin"); } // console.log("User transferred between collections due to role change"); } catch (error) { // console.error("Failed to transfer user between collections:", error); throw new Error("Failed to change user role: " + (error instanceof Error ? error.message : "Unknown error")); } } else { // Regular update without changing collections if (Object.keys(cleanData).length > 0) { // console.log("Final update payload to PocketBase:", cleanData); try { // Use the appropriate collection const collection = isCurrentlySuperadmin ? '_superusers' : 'users'; const updatedRecord = await pb.collection(collection).update(id, cleanData); updatedUser = convertToUserType(updatedRecord, isCurrentlySuperadmin ? "superadmin" : "admin"); // console.log("PocketBase update response:", updatedUser); // If email was updated successfully, show success message if (hasEmailChange) { // console.log("Email updated successfully to:", emailToUpdate); } } catch (error) { // console.error("Error updating user:", error); // Provide more specific error messages for email issues if (hasEmailChange && error instanceof Error) { if (error.message.includes("email")) { throw new Error("Email update failed. The email address may already be in use or invalid."); } } throw error; } } else { // If no fields to update, get the current user updatedUser = await this.getUser(id); } } return updatedUser; } catch (error) { // console.error("Failed to update user:", error); throw error; // Re-throw to handle in the component } }, async deleteUser(id: string): Promise { try { // Try to delete from regular users first try { await pb.collection('users').delete(id); return true; } catch (error) { // console.log("User not found in regular users, trying superadmin collection"); } // If not found, try deleting from superadmin collection try { await pb.collection('_superusers').delete(id); return true; } catch (error) { // console.error("Failed to delete user from either collection:", error); return false; } } catch (error) { // console.error("Failed to delete user:", error); return false; } }, async createUser(data: CreateUserData): Promise { try { // Create a clean data object without avatar field if it's a URL // PocketBase requires actual file uploads for avatar, not URLs const cleanData = { ...data }; // Remove avatar if it's a URL (we'll handle this differently in the future) if (cleanData.avatar && typeof cleanData.avatar === 'string') { // Check if it's an external URL (not a file reference) if (cleanData.avatar.startsWith('http') || cleanData.avatar.startsWith('/upload/') || cleanData.avatar.includes('api.dicebear.com')) { // console.log("Removing avatar URL for new user creation:", cleanData.avatar); delete cleanData.avatar; } } // Determine which collection to use based on the role const isSuperAdmin = cleanData.role === "superadmin"; const collection = isSuperAdmin ? '_superusers' : 'users'; /// console.log(`Creating new user in ${collection} collection with data:`, { // ...cleanData, // password: "[REDACTED]", // passwordConfirm: "[REDACTED]" // }); // Create the user in the appropriate collection const result = await pb.collection(collection).create(cleanData); return convertToUserType(result, isSuperAdmin ? "superadmin" : "admin"); } catch (error) { // console.error("Failed to create user:", error); throw error; } } };