feat: Replace hardcoded default profile image with a dynamic placeholder and remove cache-busting from presigned URLs.
This commit is contained in:
@@ -64,12 +64,10 @@ interface ProfileCardProps {
|
||||
function ProfileCard({ profile, resolvedAvatarUrl }: ProfileCardProps) {
|
||||
const [showFullBio, setShowFullBio] = useState(false);
|
||||
const [showAllTags, setShowAllTags] = useState(false);
|
||||
const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg';
|
||||
|
||||
const getProfileImageUrl = () => {
|
||||
const getProfileImageUrl = (): string | null => {
|
||||
if (resolvedAvatarUrl) return resolvedAvatarUrl;
|
||||
if (profile.avatar?.startsWith('/')) return profile.avatar;
|
||||
return defaultImage;
|
||||
return null;
|
||||
};
|
||||
|
||||
// Format member since date
|
||||
@@ -237,17 +235,29 @@ function ProfileCard({ profile, resolvedAvatarUrl }: ProfileCardProps) {
|
||||
<div className="bg-white rounded-[20px] p-5 flex gap-5 shadow-[0px_10px_20px_rgba(217,217,217,0.5)]">
|
||||
{/* Profile Image & View Profile Button */}
|
||||
<div className="flex-shrink-0 flex flex-col items-center">
|
||||
<div className="relative w-[200px] h-[200px] rounded-[15px] overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={getProfileImageUrl()}
|
||||
alt={`${profile.firstName} ${profile.lastName}`}
|
||||
className="w-full h-full object-cover"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = defaultImage;
|
||||
}}
|
||||
/>
|
||||
<div className="relative w-[200px] h-[200px] rounded-[15px] overflow-hidden bg-[#e8e8e8]">
|
||||
{getProfileImageUrl() ? (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img
|
||||
src={getProfileImageUrl()!}
|
||||
alt={`${profile.firstName} ${profile.lastName}`}
|
||||
className="w-full h-full object-cover"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<Image
|
||||
src="/assets/icons/user-placeholder-icon.svg"
|
||||
alt={`${profile.firstName} ${profile.lastName}`}
|
||||
width={64}
|
||||
height={64}
|
||||
className="opacity-40"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Link href={`/user/profile/${profile.id}`} className="mt-4">
|
||||
<button className="w-[113px] py-2.5 bg-[#e58625] hover:bg-[#d47720] text-[#00293d] font-fractul font-bold text-[14px] rounded-[15px] transition-colors">
|
||||
@@ -469,19 +479,15 @@ function ProfilesPageContent() {
|
||||
|
||||
const response = await agentsService.searchAgents(params);
|
||||
|
||||
// Resolve all avatar presigned URLs in parallel before rendering
|
||||
// Resolve all avatar URLs via presigned-download-url API
|
||||
const urlMap: Record<string, string> = {};
|
||||
const avatarPromises = response.data.map(async (agent) => {
|
||||
if (agent.avatar && !agent.avatar.startsWith('http') && !agent.avatar.startsWith('/')) {
|
||||
try {
|
||||
const presignedUrl = await uploadService.getPresignedDownloadUrl(agent.avatar);
|
||||
const agentWithTimestamp = agent as unknown as { updatedAt?: string };
|
||||
const cacheBuster = agentWithTimestamp.updatedAt
|
||||
? new Date(agentWithTimestamp.updatedAt).getTime()
|
||||
: Date.now();
|
||||
urlMap[agent.id] = `${presignedUrl}&_t=${cacheBuster}`;
|
||||
urlMap[agent.id] = presignedUrl;
|
||||
} catch {
|
||||
// Skip failed URLs, card will show default image
|
||||
// Skip failed URLs, card will show placeholder
|
||||
}
|
||||
} else if (agent.avatar) {
|
||||
urlMap[agent.id] = agent.avatar;
|
||||
|
||||
Reference in New Issue
Block a user