import { ref, computed } from 'vue' import { useAuthStore } from '~/stores/auth' // Types export interface User { id: number email: string role: 'superadmin' | 'admin' | 'moderator' | 'sales_agent' scope_level: 'Global' | 'Country' | 'Region' | 'City' | 'District' status: 'active' | 'inactive' created_at: string updated_at?: string last_login?: string organization_id?: number region_code?: string country_code?: string } export interface UpdateUserRoleRequest { role: User['role'] scope_level: User['scope_level'] scope_id?: number region_code?: string country_code?: string } export interface UserManagementState { users: User[] loading: boolean error: string | null } // Geographical scope definitions for mock data const geographicalScopes = [ // Hungary hierarchy { level: 'Country' as const, code: 'HU', name: 'Hungary', region_code: null }, { level: 'Region' as const, code: 'HU-PE', name: 'Pest County', country_code: 'HU' }, { level: 'City' as const, code: 'HU-BU', name: 'Budapest', country_code: 'HU', region_code: 'HU-PE' }, { level: 'District' as const, code: 'HU-BU-05', name: 'District V', country_code: 'HU', region_code: 'HU-BU' }, // Germany hierarchy { level: 'Country' as const, code: 'DE', name: 'Germany', region_code: null }, { level: 'Region' as const, code: 'DE-BE', name: 'Berlin', country_code: 'DE' }, { level: 'City' as const, code: 'DE-BER', name: 'Berlin', country_code: 'DE', region_code: 'DE-BE' }, // UK hierarchy { level: 'Country' as const, code: 'GB', name: 'United Kingdom', region_code: null }, { level: 'Region' as const, code: 'GB-LON', name: 'London', country_code: 'GB' }, { level: 'City' as const, code: 'GB-LND', name: 'London', country_code: 'GB', region_code: 'GB-LON' }, ] // Mock data generator with consistent geographical scopes const generateMockUsers = (count: number = 25): User[] => { const roles: User['role'][] = ['superadmin', 'admin', 'moderator', 'sales_agent'] const statuses: User['status'][] = ['active', 'inactive'] const domains = ['servicefinder.com', 'example.com', 'partner.com', 'customer.org'] const firstNames = ['John', 'Jane', 'Robert', 'Emily', 'Michael', 'Sarah', 'David', 'Lisa', 'James', 'Maria'] const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez'] const users: User[] = [] // Predefined users with specific geographical scopes for testing const predefinedUsers: Partial[] = [ // Global superadmin { email: 'superadmin@servicefinder.com', role: 'superadmin', scope_level: 'Global', country_code: undefined, region_code: undefined }, // Hungary admin { email: 'admin.hu@servicefinder.com', role: 'admin', scope_level: 'Country', country_code: 'HU', region_code: undefined }, // Pest County moderator { email: 'moderator.pest@servicefinder.com', role: 'moderator', scope_level: 'Region', country_code: 'HU', region_code: 'HU-PE' }, // Budapest sales agent { email: 'sales.budapest@servicefinder.com', role: 'sales_agent', scope_level: 'City', country_code: 'HU', region_code: 'HU-BU' }, // District V sales agent { email: 'agent.district5@servicefinder.com', role: 'sales_agent', scope_level: 'District', country_code: 'HU', region_code: 'HU-BU-05' }, // Germany admin { email: 'admin.de@servicefinder.com', role: 'admin', scope_level: 'Country', country_code: 'DE', region_code: undefined }, // Berlin moderator { email: 'moderator.berlin@servicefinder.com', role: 'moderator', scope_level: 'City', country_code: 'DE', region_code: 'DE-BE' }, // UK admin { email: 'admin.uk@servicefinder.com', role: 'admin', scope_level: 'Country', country_code: 'GB', region_code: undefined }, // London sales agent { email: 'sales.london@servicefinder.com', role: 'sales_agent', scope_level: 'City', country_code: 'GB', region_code: 'GB-LON' }, ] // Add predefined users predefinedUsers.forEach((userData, index) => { users.push({ id: index + 1, email: userData.email!, role: userData.role!, scope_level: userData.scope_level!, status: 'active', created_at: `2026-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`, updated_at: `2026-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`, last_login: `2026-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`, organization_id: Math.floor(Math.random() * 10) + 1, country_code: userData.country_code, region_code: userData.region_code, }) }) // Generate remaining random users for (let i = users.length + 1; i <= count; i++) { const firstName = firstNames[Math.floor(Math.random() * firstNames.length)] const lastName = lastNames[Math.floor(Math.random() * lastNames.length)] const domain = domains[Math.floor(Math.random() * domains.length)] const role = roles[Math.floor(Math.random() * roles.length)] const status = statuses[Math.floor(Math.random() * statuses.length)] // Select a random geographical scope const scope = geographicalScopes[Math.floor(Math.random() * geographicalScopes.length)] users.push({ id: i, email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@${domain}`, role, scope_level: scope.level, status, created_at: `2026-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`, updated_at: `2026-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`, last_login: `2026-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`, organization_id: Math.floor(Math.random() * 10) + 1, country_code: scope.country_code || undefined, region_code: scope.region_code || undefined, }) } return users } // API Service (Mock implementation) class UserManagementApiService { private mockUsers: User[] = generateMockUsers(15) private delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) // Get all users (with optional filtering) async getUsers(options?: { role?: User['role'] scope_level?: User['scope_level'] status?: User['status'] search?: string country_code?: string region_code?: string geographical_scope?: 'Global' | 'Hungary' | 'Pest County' | 'Budapest' | 'District V' }): Promise { await this.delay(500) // Simulate network delay let filteredUsers = [...this.mockUsers] if (options?.role) { filteredUsers = filteredUsers.filter(user => user.role === options.role) } if (options?.scope_level) { filteredUsers = filteredUsers.filter(user => user.scope_level === options.scope_level) } if (options?.status) { filteredUsers = filteredUsers.filter(user => user.status === options.status) } if (options?.country_code) { filteredUsers = filteredUsers.filter(user => user.country_code === options.country_code || user.scope_level === 'Global' ) } if (options?.region_code) { filteredUsers = filteredUsers.filter(user => user.region_code === options.region_code || user.scope_level === 'Global' || (user.scope_level === 'Country' && user.country_code === options.country_code) ) } // Geographical scope filtering (simplified for demo) if (options?.geographical_scope) { switch (options.geographical_scope) { case 'Global': // All users break case 'Hungary': filteredUsers = filteredUsers.filter(user => user.country_code === 'HU' || user.scope_level === 'Global' ) break case 'Pest County': filteredUsers = filteredUsers.filter(user => user.region_code === 'HU-PE' || user.country_code === 'HU' || user.scope_level === 'Global' ) break case 'Budapest': filteredUsers = filteredUsers.filter(user => user.region_code === 'HU-BU' || user.region_code === 'HU-PE' || user.country_code === 'HU' || user.scope_level === 'Global' ) break case 'District V': filteredUsers = filteredUsers.filter(user => user.region_code === 'HU-BU-05' || user.region_code === 'HU-BU' || user.region_code === 'HU-PE' || user.country_code === 'HU' || user.scope_level === 'Global' ) break } } if (options?.search) { const searchLower = options.search.toLowerCase() filteredUsers = filteredUsers.filter(user => user.email.toLowerCase().includes(searchLower) || user.role.toLowerCase().includes(searchLower) || user.scope_level.toLowerCase().includes(searchLower) || (user.country_code && user.country_code.toLowerCase().includes(searchLower)) || (user.region_code && user.region_code.toLowerCase().includes(searchLower)) ) } return filteredUsers } // Get single user by ID async getUserById(id: number): Promise { await this.delay(300) return this.mockUsers.find(user => user.id === id) || null } // Update user role and scope async updateUserRole(id: number, data: UpdateUserRoleRequest): Promise { await this.delay(800) // Simulate slower update const userIndex = this.mockUsers.findIndex(user => user.id === id) if (userIndex === -1) { throw new Error(`User with ID ${id} not found`) } // Check permissions (in a real app, this would be server-side) const authStore = useAuthStore() const currentUserRole = authStore.getUserRole // Superadmin can update anyone // Admin cannot update superadmin or other admins if (currentUserRole === 'admin') { const targetUser = this.mockUsers[userIndex] if (targetUser.role === 'superadmin' || (targetUser.role === 'admin' && targetUser.id !== authStore.getUserId)) { throw new Error('Admin cannot update superadmin or other admin users') } } // Update the user const updatedUser: User = { ...this.mockUsers[userIndex], ...data, updated_at: new Date().toISOString().split('T')[0], } this.mockUsers[userIndex] = updatedUser return updatedUser } // Toggle user status async toggleUserStatus(id: number): Promise { await this.delay(500) const userIndex = this.mockUsers.findIndex(user => user.id === id) if (userIndex === -1) { throw new Error(`User with ID ${id} not found`) } const currentStatus = this.mockUsers[userIndex].status const newStatus: User['status'] = currentStatus === 'active' ? 'inactive' : 'active' this.mockUsers[userIndex] = { ...this.mockUsers[userIndex], status: newStatus, updated_at: new Date().toISOString().split('T')[0], } return this.mockUsers[userIndex] } // Create new user (mock) async createUser(email: string, role: User['role'], scope_level: User['scope_level']): Promise { await this.delay(1000) const newUser: User = { id: Math.max(...this.mockUsers.map(u => u.id)) + 1, email, role, scope_level, status: 'active', created_at: new Date().toISOString().split('T')[0], updated_at: new Date().toISOString().split('T')[0], } this.mockUsers.push(newUser) return newUser } // Delete user (mock - just deactivate) async deleteUser(id: number): Promise { await this.delay(700) const userIndex = this.mockUsers.findIndex(user => user.id === id) if (userIndex === -1) { throw new Error(`User with ID ${id} not found`) } // Instead of deleting, mark as inactive this.mockUsers[userIndex] = { ...this.mockUsers[userIndex], status: 'inactive', updated_at: new Date().toISOString().split('T')[0], } } } // Composable export const useUserManagement = () => { const state = ref({ users: [], loading: false, error: null, }) const apiService = new UserManagementApiService() // Computed const activeUsers = computed(() => state.value.users.filter(user => user.status === 'active')) const inactiveUsers = computed(() => state.value.users.filter(user => user.status === 'inactive')) const superadminUsers = computed(() => state.value.users.filter(user => user.role === 'superadmin')) const adminUsers = computed(() => state.value.users.filter(user => user.role === 'admin')) // Actions const fetchUsers = async (options?: { role?: User['role'] scope_level?: User['scope_level'] status?: User['status'] search?: string country_code?: string region_code?: string geographical_scope?: 'Global' | 'Hungary' | 'Pest County' | 'Budapest' | 'District V' }) => { state.value.loading = true state.value.error = null try { const users = await apiService.getUsers(options) state.value.users = users } catch (error) { state.value.error = error instanceof Error ? error.message : 'Failed to fetch users' console.error('Error fetching users:', error) } finally { state.value.loading = false } } const updateUserRole = async (id: number, data: UpdateUserRoleRequest) => { state.value.loading = true state.value.error = null try { const updatedUser = await apiService.updateUserRole(id, data) // Update local state const userIndex = state.value.users.findIndex(user => user.id === id) if (userIndex !== -1) { state.value.users[userIndex] = updatedUser } return updatedUser } catch (error) { state.value.error = error instanceof Error ? error.message : 'Failed to update user role' console.error('Error updating user role:', error) throw error } finally { state.value.loading = false } } const toggleUserStatus = async (id: number) => { state.value.loading = true state.value.error = null try { const updatedUser = await apiService.toggleUserStatus(id) // Update local state const userIndex = state.value.users.findIndex(user => user.id === id) if (userIndex !== -1) { state.value.users[userIndex] = updatedUser } return updatedUser } catch (error) { state.value.error = error instanceof Error ? error.message : 'Failed to toggle user status' console.error('Error toggling user status:', error) throw error } finally { state.value.loading = false } } const createUser = async (email: string, role: User['role'], scope_level: User['scope_level']) => { state.value.loading = true state.value.error = null try { const newUser = await apiService.createUser(email, role, scope_level) state.value.users.push(newUser) return newUser } catch (error) { state.value.error = error instanceof Error ? error.message : 'Failed to create user' console.error('Error creating user:', error) throw error } finally { state.value.loading = false } } const deleteUser = async (id: number) => { state.value.loading = true state.value.error = null try { await apiService.deleteUser(id) // Update local state (mark as inactive) const userIndex = state.value.users.findIndex(user => user.id === id) if (userIndex !== -1) { state.value.users[userIndex] = { ...state.value.users[userIndex], status: 'inactive', updated_at: new Date().toISOString().split('T')[0], } } } catch (error) { state.value.error = error instanceof Error ? error.message : 'Failed to delete user' console.error('Error deleting user:', error) throw error } finally { state.value.loading = false } } // Initialize with some data const initialize = () => { fetchUsers() } // Helper function to get geographical scopes for UI const getGeographicalScopes = () => { return [ { value: 'Global', label: 'Global', icon: 'mdi-earth', description: 'All users worldwide' }, { value: 'Hungary', label: 'Hungary', icon: 'mdi-flag', description: 'Users in Hungary' }, { value: 'Pest County', label: 'Pest County', icon: 'mdi-map-marker-radius', description: 'Users in Pest County' }, { value: 'Budapest', label: 'Budapest', icon: 'mdi-city', description: 'Users in Budapest' }, { value: 'District V', label: 'District V', icon: 'mdi-map-marker', description: 'Users in District V' }, ] } return { // State state: computed(() => state.value), users: computed(() => state.value.users), loading: computed(() => state.value.loading), error: computed(() => state.value.error), // Computed activeUsers, inactiveUsers, superadminUsers, adminUsers, // Actions fetchUsers, updateUserRole, toggleUserStatus, createUser, deleteUser, initialize, // Helper functions getUserById: (id: number) => state.value.users.find(user => user.id === id), filterByRole: (role: User['role']) => state.value.users.filter(user => user.role === role), filterByScope: (scope_level: User['scope_level']) => state.value.users.filter(user => user.scope_level === scope_level), getGeographicalScopes, } } export default useUserManagement