Files
rikkei_simple_care/management/src/components/StudentAvatar.tsx
PhuocNTB 2bb856f2fd
All checks were successful
Deploy on Master Change / deploy (push) Successful in 1m19s
fix ui
2026-07-07 06:52:33 +07:00

71 lines
2.0 KiB
TypeScript

import React, { useEffect, useState } from 'react';
function normalizeAvatarUrl(raw?: string | null): string {
const trimmed = raw?.trim() || '';
if (!trimmed) return '';
if (trimmed.startsWith('//')) return `https:${trimmed}`;
return trimmed;
}
interface StudentAvatarProps {
fullName: string;
avatar?: string | null;
isOnline?: boolean;
size?: number;
showStatusBadge?: boolean;
}
export const StudentOnlineBadge: React.FC<{
isOnline: boolean;
size?: 'sm' | 'md';
}> = ({ isOnline, size = 'sm' }) => (
<span className={`student-online-badge ${isOnline ? 'online' : 'offline'} student-online-badge--${size}`}>
<span className="student-online-badge-dot" aria-hidden />
{isOnline ? 'Online' : 'Offline'}
</span>
);
export const StudentAvatar: React.FC<StudentAvatarProps> = ({
fullName,
avatar,
isOnline = false,
size = 48,
showStatusBadge = true,
}) => {
const [imgFailed, setImgFailed] = useState(false);
const avatarUrl = normalizeAvatarUrl(avatar);
const initial = fullName ? fullName.trim().charAt(0).toUpperCase() : 'S';
const showImage = !!avatarUrl && !imgFailed;
useEffect(() => {
setImgFailed(false);
}, [avatarUrl]);
return (
<div className="student-avatar-wrap" style={{ width: size, height: size }}>
<div
className={`student-workspace-avatar ${isOnline ? 'online' : 'offline'} ${showImage ? 'has-image' : ''}`}
style={{ width: size, height: size, fontSize: size * 0.38 }}
>
{showImage ? (
<img
src={avatarUrl}
alt={fullName}
className="student-avatar-img"
referrerPolicy="no-referrer"
onError={() => setImgFailed(true)}
/>
) : (
initial
)}
</div>
{showStatusBadge && (
<span
className={`student-avatar-status-ring ${isOnline ? 'online' : 'offline'}`}
title={isOnline ? 'Đang online' : 'Đang offline'}
/>
)}
</div>
);
};