diff --git a/frontend/app/hooks/useUsers.ts b/frontend/app/hooks/useUsers.ts
new file mode 100644
index 00000000..d7133fc5
--- /dev/null
+++ b/frontend/app/hooks/useUsers.ts
@@ -0,0 +1,17 @@
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { getUsers, updateUserRole } from '../lib/api/usersApi';
+
+export function useUsers() {
+ return useQuery({ queryKey: ['users'], queryFn: getUsers });
+}
+
+export function useUpdateUserRole() {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: ({ id, role }: { id: string; role: string }) =>
+ updateUserRole(id, role),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['users'] });
+ },
+ });
+}
diff --git a/frontend/app/users/page.tsx b/frontend/app/users/page.tsx
new file mode 100644
index 00000000..287b41f0
--- /dev/null
+++ b/frontend/app/users/page.tsx
@@ -0,0 +1,108 @@
+'use client';
+
+import React, { useState } from 'react';
+import { useUsers, useUpdateUserRole } from '../hooks/useUsers';
+import { Avatar } from '../../components/ui/Avatar';
+
+export default function UsersPage() {
+ const { data: users = [], isLoading } = useUsers();
+ const updateRole = useUpdateUserRole();
+
+ const [search, setSearch] = useState('');
+ const [roleFilter, setRoleFilter] = useState('');
+
+ if (isLoading) return
Loading...
;
+
+ const filteredUsers = users.filter((u: any) =>
+ (u.name.toLowerCase().includes(search.toLowerCase()) ||
+ u.email.toLowerCase().includes(search.toLowerCase())) &&
+ (roleFilter ? u.role === roleFilter : true)
+ );
+
+ return (
+
+
Users Management
+
+
+ setSearch(e.target.value)}
+ className="border p-2"
+ />
+
+
+
+
+
+
+ | Avatar + Name |
+ Email |
+ Role |
+ Joined |
+
+
+
+ {filteredUsers.map((user: any) => (
+
+ |
+
+ {user.name}{' '}
+ {user.isCurrentUser && (
+
+ You
+
+ )}
+ |
+ {user.email} |
+
+
+ |
+ {new Date(user.joinedAt).toLocaleDateString()} |
+
+ ))}
+
+
+
+
+
Role Legend
+
+ -
+ Admin — full access
+
+ -
+ Manager — manage teams
+
+ -
+ Staff — standard user
+
+
+
+
+ );
+}
diff --git a/frontend/components/ui/Avatar.tsx b/frontend/components/ui/Avatar.tsx
new file mode 100644
index 00000000..e03dc214
--- /dev/null
+++ b/frontend/components/ui/Avatar.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+
+export function Avatar({
+ firstName,
+ lastName,
+ isCurrentUser,
+}: {
+ firstName: string;
+ lastName: string;
+ isCurrentUser?: boolean;
+}) {
+ const initials = `${firstName[0]}${lastName[0]}`.toUpperCase();
+ const bgColor = isCurrentUser ? 'bg-gray-800' : 'bg-gray-400';
+
+ return (
+
+ {initials}
+
+ );
+}
diff --git a/frontend/lib/api/usersApi.ts b/frontend/lib/api/usersApi.ts
new file mode 100644
index 00000000..71b9bfe8
--- /dev/null
+++ b/frontend/lib/api/usersApi.ts
@@ -0,0 +1,11 @@
+import api from './client';
+
+export async function getUsers() {
+ const res = await api.get('/api/users');
+ return res.data;
+}
+
+export async function updateUserRole(id: string, role: string) {
+ const res = await api.patch(`/api/users/${id}/role`, { role });
+ return res.data;
+}