diff --git a/frontend/src/components/common/Avatar.jsx b/frontend/src/components/common/Avatar.jsx
new file mode 100644
index 0000000..10c95cd
--- /dev/null
+++ b/frontend/src/components/common/Avatar.jsx
@@ -0,0 +1,71 @@
+import React from 'react';
+
+const STATUS_COLORS = {
+ online: 'bg-green-500',
+ offline: 'bg-gray-400',
+ busy: 'bg-yellow-500',
+};
+
+const Avatar = ({
+ src,
+ username = '',
+ alt,
+ size = 32, // number (px) or string with Tailwind classes
+ className = '', // applied to
+ containerClassName = '', // applied to wrapper
+ rounded = true,
+ ring = false,
+ ringColor = 'ring-white',
+ status = null, // 'online' | 'offline' | 'busy' | null
+ title,
+ onClick,
+}) => {
+ const [imgSrc, setImgSrc] = React.useState(src || '');
+
+ const isNumericSize = typeof size === 'number';
+ const style = isNumericSize ? { width: size, height: size } : undefined;
+ const sizeClass = isNumericSize ? '' : size; // e.g. 'w-8 h-8'
+
+ const fallback = `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(
+ username || 'user'
+ )}`;
+
+ const handleError = (e) => {
+ if (e.currentTarget.src !== fallback) {
+ setImgSrc(fallback);
+ }
+ };
+
+ const img = (
+
+ );
+
+ // If no status, no need for wrapper; keep DOM minimal
+ if (!status) {
+ return img;
+ }
+
+ const dotColor = STATUS_COLORS[status] || STATUS_COLORS.offline;
+
+ return (
+
+ {img}
+
+
+ );
+};
+
+export default Avatar;
diff --git a/frontend/src/components/layout/Navbar.jsx b/frontend/src/components/layout/Navbar.jsx
index e64e3b9..c47fc6b 100644
--- a/frontend/src/components/layout/Navbar.jsx
+++ b/frontend/src/components/layout/Navbar.jsx
@@ -1,6 +1,7 @@
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
import { Video, LogOut, User, History, Users } from 'lucide-react';
+import Avatar from '../common/Avatar';
import { useState, useEffect } from 'react';
import { matchesAPI } from '../../services/api';
import { connectSocket, disconnectSocket, getSocket } from '../../services/socket';
@@ -99,11 +100,7 @@ const Navbar = () => {
-

+
{user.username}
diff --git a/frontend/src/pages/EventChatPage.jsx b/frontend/src/pages/EventChatPage.jsx
index 2dce747..384134b 100644
--- a/frontend/src/pages/EventChatPage.jsx
+++ b/frontend/src/pages/EventChatPage.jsx
@@ -6,6 +6,7 @@ import { Send, UserPlus, Loader2, LogOut, AlertTriangle, QrCode, Edit2, Filter,
import { connectSocket, disconnectSocket, getSocket } from '../services/socket';
import { eventsAPI, heatsAPI, matchesAPI } from '../services/api';
import HeatsBanner from '../components/heats/HeatsBanner';
+import Avatar from '../components/common/Avatar';
const EventChatPage = () => {
const { slug } = useParams();
@@ -536,20 +537,13 @@ const EventChatPage = () => {
className="flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg"
>
-
-

- {/* Online/Offline indicator */}
-
-
+
)}
{messages.map((message) => {
- const isOwnMessage = message.userId === user.id;
+ const isOwnMessage = (message.userId ?? message.user_id) === user.id;
return (
-
diff --git a/frontend/src/pages/MatchChatPage.jsx b/frontend/src/pages/MatchChatPage.jsx
index ec130e0..279fb4c 100644
--- a/frontend/src/pages/MatchChatPage.jsx
+++ b/frontend/src/pages/MatchChatPage.jsx
@@ -8,6 +8,7 @@ import { connectSocket, getSocket } from '../services/socket';
import { useWebRTC } from '../hooks/useWebRTC';
import { detectWebRTCSupport } from '../utils/webrtcDetection';
import WebRTCWarning from '../components/WebRTCWarning';
+import Avatar from '../components/common/Avatar';
const MatchChatPage = () => {
const { slug } = useParams();
@@ -286,10 +287,12 @@ const MatchChatPage = () => {
-
@@ -355,17 +358,18 @@ const MatchChatPage = () => {
)}
{messages.map((message) => {
- const isOwnMessage = message.userId === user.id;
+ const isOwnMessage = (message.userId ?? message.user_id) === user.id;
return (
-
diff --git a/frontend/src/pages/ProfilePage.jsx b/frontend/src/pages/ProfilePage.jsx
index a276531..aeacda9 100644
--- a/frontend/src/pages/ProfilePage.jsx
+++ b/frontend/src/pages/ProfilePage.jsx
@@ -4,6 +4,7 @@ import { authAPI } from '../services/api';
import Layout from '../components/layout/Layout';
import { User, Mail, Lock, Save, AlertCircle, CheckCircle, Loader2, Hash, Youtube, Instagram, Facebook, MapPin, Globe } from 'lucide-react';
import { COUNTRIES } from '../data/countries';
+import Avatar from '../components/common/Avatar';
const ProfilePage = () => {
const { user, updateUser } = useAuth();
@@ -138,10 +139,13 @@ const ProfilePage = () => {
{/* Header */}
-
diff --git a/frontend/src/pages/PublicProfilePage.jsx b/frontend/src/pages/PublicProfilePage.jsx
index 3b6b5ed..ab6161e 100644
--- a/frontend/src/pages/PublicProfilePage.jsx
+++ b/frontend/src/pages/PublicProfilePage.jsx
@@ -3,6 +3,7 @@ import { useParams, Link } from 'react-router-dom';
import { authAPI, ratingsAPI } from '../services/api';
import Layout from '../components/layout/Layout';
import { User, MapPin, Globe, Hash, Youtube, Instagram, Facebook, Award, Users, Star, Calendar, Loader2, AlertCircle, ThumbsUp } from 'lucide-react';
+import Avatar from '../components/common/Avatar';
const PublicProfilePage = () => {
const { username } = useParams();
@@ -86,10 +87,12 @@ const PublicProfilePage = () => {
{/* Profile Header */}
-
@@ -263,10 +266,12 @@ const PublicProfilePage = () => {
{/* Rater Info */}
-