108 lines
4.1 KiB
React
108 lines
4.1 KiB
React
import React from 'react'
|
|
import { formatProfileDate, formatProfileNumber } from '../../../lib/profileFormatters'
|
|
|
|
function formatFollowedAt(value) {
|
|
if (!value) return null
|
|
|
|
try {
|
|
return formatProfileDate(value, { year: 'numeric', month: 'short', day: 'numeric' })
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export default function TabFollowers({ listing, username, followerCount }) {
|
|
const items = Array.isArray(listing?.data) ? listing.data : []
|
|
const total = Number(listing?.total ?? followerCount ?? 0)
|
|
const currentPage = Number(listing?.current_page ?? 1)
|
|
const lastPage = Number(listing?.last_page ?? 1)
|
|
const prevUrl = listing?.prev_page_url ?? null
|
|
const nextUrl = listing?.next_page_url ?? null
|
|
|
|
return (
|
|
<div
|
|
id="tabpanel-followers"
|
|
role="tabpanel"
|
|
aria-labelledby="tab-followers"
|
|
className="pt-6"
|
|
>
|
|
<h2 className="mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-slate-500">
|
|
<i className="fa-solid fa-users text-sky-400 fa-fw" />
|
|
Followers
|
|
<span className="rounded bg-white/5 px-1.5 py-0.5 text-[10px] font-medium normal-case tracking-normal text-slate-400">
|
|
{formatProfileNumber(total)}
|
|
</span>
|
|
</h2>
|
|
|
|
{items.length === 0 ? (
|
|
<p className="rounded-2xl border border-white/8 bg-white/[0.03] px-4 py-8 text-center text-sm text-slate-400">
|
|
{username ? `${username} has no public followers yet.` : 'No public followers yet.'}
|
|
</p>
|
|
) : (
|
|
<ul className="grid gap-3 sm:grid-cols-2">
|
|
{items.map((follower) => {
|
|
const handle = follower.username || follower.display_name || follower.name
|
|
const followedAt = formatFollowedAt(follower.followed_at)
|
|
|
|
return (
|
|
<li key={follower.id}>
|
|
<a
|
|
href={follower.profile_url}
|
|
className="flex items-center gap-3 rounded-2xl border border-white/8 bg-white/[0.03] px-3 py-3 transition-colors hover:border-sky-300/25 hover:bg-white/[0.06]"
|
|
>
|
|
{follower.avatar_url ? (
|
|
<img
|
|
src={follower.avatar_url}
|
|
alt=""
|
|
className="h-12 w-12 rounded-xl object-cover"
|
|
/>
|
|
) : (
|
|
<span className="flex h-12 w-12 items-center justify-center rounded-xl bg-white/5 text-slate-500">
|
|
<i className="fa-solid fa-user" />
|
|
</span>
|
|
)}
|
|
<span className="min-w-0">
|
|
<span className="block truncate text-sm font-semibold text-white">
|
|
{handle}
|
|
</span>
|
|
{follower.username ? (
|
|
<span className="block truncate text-xs text-slate-400">@{follower.username}</span>
|
|
) : null}
|
|
<span className="mt-1 block text-[11px] text-slate-500">
|
|
{[follower.rank, followedAt ? `Followed ${followedAt}` : null]
|
|
.filter(Boolean)
|
|
.join(' · ')}
|
|
</span>
|
|
</span>
|
|
</a>
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
)}
|
|
|
|
{lastPage > 1 ? (
|
|
<nav className="mt-6 flex items-center justify-center gap-3 text-sm" aria-label="Followers pagination">
|
|
{prevUrl ? (
|
|
<a href={prevUrl} className="rounded-xl border border-white/10 px-3 py-2 text-slate-300 hover:bg-white/5">
|
|
Previous
|
|
</a>
|
|
) : (
|
|
<span className="rounded-xl border border-white/5 px-3 py-2 text-slate-600">Previous</span>
|
|
)}
|
|
<span className="text-slate-500">
|
|
Page {currentPage} of {lastPage}
|
|
</span>
|
|
{nextUrl ? (
|
|
<a href={nextUrl} className="rounded-xl border border-white/10 px-3 py-2 text-slate-300 hover:bg-white/5">
|
|
Next
|
|
</a>
|
|
) : (
|
|
<span className="rounded-xl border border-white/5 px-3 py-2 text-slate-600">Next</span>
|
|
)}
|
|
</nav>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|