Index: frontend/src/pages/AllUsers.tsx
===================================================================
--- frontend/src/pages/AllUsers.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
+++ frontend/src/pages/AllUsers.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
@@ -0,0 +1,88 @@
+import React, { useEffect, useState } from "react";
+import { useNavigate } from "react-router-dom";
+import axiosInstance from "../api/axiosInstance";
+
+interface User {
+	id: number;
+	username: string;
+	fullName: string;
+}
+
+const AllUsers = () => {
+	const [users, setUsers] = useState<User[]>([]);
+	const [searchedUser, setSearchedUser] = useState<string>("");
+	const [error, setError] = useState<string | null>(null);
+	const navigate = useNavigate();
+
+	useEffect(() => {
+		const fetchUsers = async () => {
+			const response = axiosInstance.get("/users/all");
+			setUsers((await response).data);
+		};
+		fetchUsers();
+	}, []);
+
+	const handleUserClick = (userId: number) => {
+		navigate(`/users/${userId}`);
+	};
+
+	const handleSearch = async (e: React.FormEvent) => {
+		e.preventDefault();
+		try {
+			const response = await axiosInstance.get(`/users/search`, {
+				params: { name: searchedUser },
+			});
+			setUsers(response.data);
+		} catch (err: any) {
+			const errorMessage = err.response?.data?.error || "Search failed";
+			setError(errorMessage);
+		}
+	};
+
+	if (users.length == 0) return <div className="p-6">Loading...</div>;
+	if (error) {
+		return (
+			<div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg">
+				<h2 className="font-bold">Error</h2>
+				<p>{error}</p>
+			</div>
+		);
+	}
+
+	return (
+		<div className="container mx-auto p-6">
+			<h1 className="text-3xl font-bold mb-6">All Users</h1>
+			<form onSubmit={handleSearch} className="flex gap-2 mb-10">
+				<input
+					type="text"
+					value={searchedUser}
+					onChange={(e) => setSearchedUser(e.target.value)}
+					className="border p-2 rounded w-full"
+					placeholder="Search for a user..."
+				/>
+				<button
+					type="submit"
+					className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 transition-colors"
+				>
+					Search
+				</button>
+			</form>
+
+			<div className="grid gap-4">
+				{users.map((u) => (
+					<div
+						key={u.id}
+						onClick={() => handleUserClick(u.id)}
+						className="bg-white shadow-md rounded-lg p-4 hover:shadow-lg hover:bg-gray-50 cursor-pointer transition-all"
+					>
+						<h2 className="text-xl font-semibold">{u.fullName}</h2>
+						<p className="text-gray-600">@{u.username}</p>
+						<p className="text-gray-600">{u.id}</p>
+					</div>
+				))}
+			</div>
+		</div>
+	);
+};
+
+export default AllUsers;
Index: frontend/src/pages/LandingPage.tsx
===================================================================
--- frontend/src/pages/LandingPage.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
+++ frontend/src/pages/LandingPage.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
@@ -0,0 +1,5 @@
+const LandingPage = () => {
+	return <div>landing page</div>;
+};
+
+export default LandingPage;
Index: frontend/src/pages/Login.tsx
===================================================================
--- frontend/src/pages/Login.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
+++ frontend/src/pages/Login.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
@@ -0,0 +1,71 @@
+import { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import axiosInstance, { scheduleTokenRefresh } from "../api/axiosInstance";
+import { useAuth } from "../context/authContext";
+import type { User } from "../utils/types";
+
+const Login = () => {
+	const [username, setUsername] = useState("");
+	const [password, setPassword] = useState("");
+	const [error, setError] = useState<string | null>(null);
+	const { setUser } = useAuth();
+	const navigate = useNavigate();
+
+	const handleLogin = async (e: React.FormEvent) => {
+		e.preventDefault();
+		try {
+			const response = await axiosInstance.post<{
+				user: User;
+				tokenExpiresIn: number;
+			}>("/auth/login", { username, password });
+			scheduleTokenRefresh(response.data.tokenExpiresIn);
+			setUser(response.data.user);
+			navigate("/");
+		} catch (error) {
+			setError("Login failed. Please check your credentials.");
+		}
+	};
+	return (
+		<div className="flex flex-col items-center justify-center min-h-[90vh] bg-gray-100">
+			<h2 className="text-2xl mb-4">Login</h2>
+			<form
+				onSubmit={handleLogin}
+				className="bg-white p-6 rounded shadow-md w-80"
+			>
+				{error && <p className="text-red-500 mb-4">{error}</p>}
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="username">
+						Username
+					</label>
+					<input
+						type="text"
+						id="username"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={username}
+						onChange={(e) => setUsername(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="password">
+						Password
+					</label>
+					<input
+						type="password"
+						id="password"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={password}
+						onChange={(e) => setPassword(e.target.value)}
+					/>
+				</div>
+				<button
+					type="submit"
+					className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors duration-200 w-full cursor-pointer"
+				>
+					Login
+				</button>
+			</form>
+		</div>
+	);
+};
+
+export default Login;
Index: frontend/src/pages/Nav.tsx
===================================================================
--- frontend/src/pages/Nav.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
+++ frontend/src/pages/Nav.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
@@ -0,0 +1,85 @@
+import { Link } from "react-router-dom";
+import axiosInstance, { baseURL } from "../api/axiosInstance";
+import Logo from "../assets/logo-finkwave.png";
+import { useAuth } from "../context/authContext";
+
+const Nav = () => {
+	const { user, setUser, isAuthLoading } = useAuth();
+
+	const handleLogout = async (e: React.MouseEvent<HTMLButtonElement>) => {
+		e.preventDefault();
+		try {
+			await axiosInstance.post("/auth/logout");
+			setUser(undefined);
+		} catch (error) {
+			console.error("Logout failed:", error);
+		}
+	};
+
+	return (
+		<div className="bg-gray-800 p-4 flex justify-between items-center">
+			<Link to="/" className="text-white text-lg font-semibold">
+				<img src={Logo} alt="Finkwave Logo" className="h-12 w-auto" />
+			</Link>
+
+			<div className="flex items-center space-x-4">
+				{!isAuthLoading && (
+					<div className="flex items-center space-x-3">
+						{user ? (
+							<div className="flex items-center space-x-3">
+								<div className="flex items-center space-x-2 bg-slate-700 rounded-lg px-3 py-2">
+									{user.profilePhoto ? (
+										<img
+											src={`${baseURL}/${user.profilePhoto}`}
+											alt={`${user.username}'s profile`}
+											className="w-8 h-8 rounded-full object-cover"
+										/>
+									) : (
+										<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
+											<span className="text-white text-sm font-semibold">
+												{user.username.charAt(0).toUpperCase()}
+											</span>
+										</div>
+									)}
+
+									<div className="text-white">
+										<p className="text-sm font-medium">{user.username}</p>
+										<p className="text-xs text-gray-300 capitalize">
+											{user.role}
+										</p>
+									</div>
+								</div>
+								<button
+									onClick={handleLogout}
+									className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm 
+                                    font-medium transition-colors duration-200 flex items-center space-x-1 cursor-pointer"
+								>
+									Logout
+								</button>
+							</div>
+						) : (
+							<div className="flex items-center space-x-4">
+								<Link
+									to="/login"
+									className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm 
+                                    font-medium transition-colors duration-200 cursor-pointer"
+								>
+									Login
+								</Link>
+								<Link
+									to="/register"
+									className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg text-sm
+                                    font-medium transition-colors duration-200 cursor-pointer"
+								>
+									Register
+								</Link>
+							</div>
+						)}
+					</div>
+				)}
+			</div>
+		</div>
+	);
+};
+
+export default Nav;
Index: frontend/src/pages/Register.tsx
===================================================================
--- frontend/src/pages/Register.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
+++ frontend/src/pages/Register.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
@@ -0,0 +1,160 @@
+import { useEffect, useState } from "react";
+import { useNavigate } from "react-router";
+import axiosInstance, { scheduleTokenRefresh } from "../api/axiosInstance";
+import { useAuth } from "../context/authContext";
+import type { User } from "../utils/types";
+
+const Register = () => {
+	const { setUser } = useAuth();
+	const [username, setUsername] = useState("");
+	const [password, setPassword] = useState("");
+	const [fullname, setFullname] = useState("");
+	const [email, setEmail] = useState("");
+	const [profilePhotoFile, setProfilePhotoFile] = useState<File | null>(null);
+	const [previewProfilePhoto, setPreviewProfilePhoto] = useState<string | null>(
+		null,
+	);
+
+	const navigate = useNavigate();
+
+	useEffect(() => {
+		return () => {
+			if (previewProfilePhoto) {
+				URL.revokeObjectURL(previewProfilePhoto);
+			}
+		};
+	}, [previewProfilePhoto]);
+
+	const handleRegister = async (e: React.FormEvent) => {
+		e.preventDefault();
+		// todo: add proper error handling
+		if (profilePhotoFile && profilePhotoFile.size > 5 * 1024 * 1024) {
+			alert("Max file size is 5MB");
+			return;
+		}
+
+		if (profilePhotoFile && !profilePhotoFile.type.startsWith("image/")) {
+			alert("Only images allowed");
+			return;
+		}
+		const formData = new FormData();
+		if (profilePhotoFile) formData.append("profilePhoto", profilePhotoFile);
+		formData.append("username", username);
+		formData.append("fullname", fullname);
+		formData.append("email", email);
+		formData.append("password", password);
+
+		try {
+			const response = await axiosInstance.post<{
+				user: User;
+				tokenExpiresIn: number;
+			}>("/auth/register", formData);
+			scheduleTokenRefresh(response.data.tokenExpiresIn);
+			setUser(response.data.user);
+			navigate("/");
+		} catch (error) {
+			console.error("Registration failed:", error);
+		}
+	};
+
+	return (
+		<div className="flex flex-col items-center justify-center min-h-[90vh] bg-gray-100">
+			<h2 className="text-2xl mb-4">Register</h2>
+			<form className="bg-white p-6 rounded shadow-md w-80">
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="username">
+						Username
+					</label>
+					<input
+						type="text"
+						id="username"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={username}
+						onChange={(e) => setUsername(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="password">
+						Password
+					</label>
+					<input
+						type="password"
+						id="password"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={password}
+						onChange={(e) => setPassword(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="fullName">
+						Full Name
+					</label>
+					<input
+						type="text"
+						id="fullName"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={fullname}
+						onChange={(e) => setFullname(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="email">
+						Email
+					</label>
+					<input
+						type="email"
+						id="email"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={email}
+						onChange={(e) => setEmail(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2">Profile Photo</label>
+					<div className="flex items-center gap-3">
+						<label
+							htmlFor="profilePhoto"
+							className="px-4 py-2 bg-blue-500 text-white rounded cursor-pointer hover:bg-blue-600 transition-colors text-sm font-medium"
+						>
+							Choose File
+						</label>
+						<span className="text-gray-600 text-sm">
+							{profilePhotoFile ? profilePhotoFile.name : "No file chosen"}
+						</span>
+					</div>
+					<input
+						type="file"
+						accept="image/*"
+						id="profilePhoto"
+						className="hidden"
+						onChange={(e) => {
+							if (e.target.files && e.target.files[0]) {
+								setProfilePhotoFile(e.target.files[0]);
+								setPreviewProfilePhoto(URL.createObjectURL(e.target.files[0]));
+							}
+						}}
+					/>
+				</div>
+				{previewProfilePhoto && (
+					<div className="mb-4">
+						<p className="block text-gray-700 mb-2">Profile Photo Preview:</p>
+						<img
+							src={previewProfilePhoto}
+							alt="Profile Preview"
+							className="w-20 h-20 object-cover rounded-full"
+						/>
+					</div>
+				)}
+				<button
+					type="submit"
+					className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 cursor-pointer"
+					onClick={handleRegister}
+				>
+					Register
+				</button>
+			</form>
+		</div>
+	);
+};
+
+export default Register;
Index: frontend/src/pages/UserDetailView.tsx
===================================================================
--- frontend/src/pages/UserDetailView.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
+++ frontend/src/pages/UserDetailView.tsx	(revision 4e5cf92d976588170fa715007a49de90e673172a)
@@ -0,0 +1,121 @@
+import { useEffect, useState } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import axiosInstance from "../api/axiosInstance";
+import ArtistView from "../components/userProfile/ArtistView";
+import ListenerView from "../components/userProfile/ListenerView";
+import type {
+	ArtistContributionDTO,
+	MusicalEntityDTO,
+	Playlist,
+} from "../utils/types";
+
+interface User {
+	id: number;
+	username: string;
+	fullName: string;
+	userType: string;
+	followers: number;
+	following: number;
+	isFollowedByCurrentUser: boolean;
+
+	musicalEntities?: {
+		contributions: ArtistContributionDTO[];
+	};
+
+	likes?: {
+		likedEntities: MusicalEntityDTO[];
+	};
+
+	createdPlaylists?: Playlist[];
+}
+
+const UserDetail = () => {
+	// user refers to the selected user NOT to the user from context
+	const { userId } = useParams();
+	const navigate = useNavigate();
+	const [user, setUser] = useState<User | null>(null);
+	const [error, setError] = useState<string | null>(null);
+
+	useEffect(() => {
+		const fetchUser = async () => {
+			setError(null);
+			try {
+				const response = await axiosInstance.get(`/users/${userId}`);
+
+				setUser(response.data);
+			} catch (err: any) {
+				const errorMessage =
+					err.response?.data?.error || "Failed to fetch user";
+				setError(errorMessage);
+			}
+		};
+		fetchUser();
+	}, [userId]);
+
+	if (error) {
+		return (
+			<div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg">
+				<h2 className="font-bold">Error</h2>
+				<p>{error}</p>
+			</div>
+		);
+	}
+
+	if (!user) return <div className="p-6">Loading...</div>;
+
+	return (
+		<div className="container mx-auto p-6">
+			<button
+				onClick={() => navigate(-1)}
+				className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
+			>
+				← Back
+			</button>
+
+			<div className="bg-white shadow-lg rounded-lg p-8">
+				<div className="flex items-start gap-6 mb-8">
+					<div className="shrink-0">
+						<div className="w-32 h-32 rounded-full bg-linear-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white text-4xl font-bold shadow-lg">
+							{user.fullName.charAt(0).toUpperCase()}
+						</div>
+					</div>
+
+					<div className="flex-1">
+						<h1 className="text-4xl font-bold mb-2">{user.fullName}</h1>
+						<span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium mb-4">
+							{user.userType}
+						</span>
+
+						<div className="flex gap-6 mb-4 text-gray-700">
+							<div className="flex flex-col">
+								<span className="text-2xl font-bold">{user.followers}</span>
+								<span className="text-sm text-gray-500">Followers</span>
+							</div>
+							<div className="flex flex-col">
+								<span className="text-2xl font-bold">{user.following}</span>
+								<span className="text-sm text-gray-500">Following</span>
+							</div>
+						</div>
+
+						<button className="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg shadow-md transition-colors duration-200 cursor-pointer">
+							{user.isFollowedByCurrentUser ? "Unfollow" : "Follow"}
+						</button>
+					</div>
+				</div>
+
+				{user.userType === "ARTIST" && user.musicalEntities?.contributions && (
+					<ArtistView contributions={user.musicalEntities.contributions} />
+				)}
+
+				{user.userType === "LISTENER" && user.likes?.likedEntities && (
+					<ListenerView
+						likedEntities={user.likes.likedEntities}
+						playlists={user.createdPlaylists}
+					/>
+				)}
+			</div>
+		</div>
+	);
+};
+
+export default UserDetail;
