This commit is contained in:
test
2026-08-30 11:06:07 +02:00
parent e7c878525d
commit 5c80402aed
2 changed files with 228 additions and 59 deletions
+3
View File
@@ -2467,12 +2467,15 @@
"resources/js/hooks/upload/useVisionTags.js": [], "resources/js/hooks/upload/useVisionTags.js": [],
"resources/js/hooks/useWebShare.js": [], "resources/js/hooks/useWebShare.js": [],
"resources/js/lib/academyAnalytics.js": [], "resources/js/lib/academyAnalytics.js": [],
"resources/js/lib/profileFormatters.js": [],
"resources/js/lib/security/botFingerprint.js": [], "resources/js/lib/security/botFingerprint.js": [],
"resources/js/lib/uploadAnalytics.js": [], "resources/js/lib/uploadAnalytics.js": [],
"resources/js/lib/uploadEndpoints.js": [], "resources/js/lib/uploadEndpoints.js": [],
"resources/js/lib/uploadNotices.js": [], "resources/js/lib/uploadNotices.js": [],
"resources/js/lib/uploadUtils.js": [], "resources/js/lib/uploadUtils.js": [],
"resources/js/lib/useDeferredSimilarAi.js": [], "resources/js/lib/useDeferredSimilarAi.js": [],
"resources/js/lib/useLazyProfileFavourites.js": [],
"resources/js/lib/useLazyProfileFeaturedArtworks.js": [],
"resources/js/lib/useNavContext.js": [], "resources/js/lib/useNavContext.js": [],
"resources/js/lib/worldAnalytics.js": [], "resources/js/lib/worldAnalytics.js": [],
"resources/js/ssr.jsx": [], "resources/js/ssr.jsx": [],
+225 -59
View File
@@ -42455,6 +42455,21 @@ function NovaConfirmDialog({
document.body document.body
); );
} }
const PROFILE_LOCALE = "en-US";
const PROFILE_TIME_ZONE = "UTC";
function formatProfileNumber(value) {
const numeric = Number(value ?? 0);
return Number.isFinite(numeric) ? new Intl.NumberFormat(PROFILE_LOCALE).format(numeric) : "0";
}
function formatProfileDate(value, options = {}) {
if (!value) return null;
const date = value instanceof Date ? value : new Date(value);
if (Number.isNaN(date.getTime())) return null;
return new Intl.DateTimeFormat(PROFILE_LOCALE, {
...options,
timeZone: PROFILE_TIME_ZONE
}).format(date);
}
function FollowButton({ function FollowButton({
username, username,
initialFollowing = false, initialFollowing = false,
@@ -42523,7 +42538,7 @@ function FollowButton({
}, },
/* @__PURE__ */ React.createElement("i", { className: `fa-solid fa-fw ${loading ? "fa-circle-notch fa-spin" : following ? hovered ? "fa-user-minus" : "fa-user-check" : "fa-user-plus"}` }), /* @__PURE__ */ React.createElement("i", { className: `fa-solid fa-fw ${loading ? "fa-circle-notch fa-spin" : following ? hovered ? "fa-user-minus" : "fa-user-check" : "fa-user-plus"}` }),
/* @__PURE__ */ React.createElement("span", null, following ? hovered ? "Unfollow" : "Following" : "Follow"), /* @__PURE__ */ React.createElement("span", null, following ? hovered ? "Unfollow" : "Following" : "Follow"),
showCount ? /* @__PURE__ */ React.createElement("span", { className: "text-xs opacity-70" }, count.toLocaleString()) : null showCount ? /* @__PURE__ */ React.createElement("span", { className: "text-xs opacity-70" }, formatProfileNumber(count)) : null
), /* @__PURE__ */ React.createElement( ), /* @__PURE__ */ React.createElement(
NovaConfirmDialog, NovaConfirmDialog,
{ {
@@ -76057,13 +76072,7 @@ async function requestJson$p(url, { method = "GET", body: body2 } = {}) {
} }
function formatUpdated(value) { function formatUpdated(value) {
if (!value) return "Updated recently"; if (!value) return "Updated recently";
const date = new Date(value); return formatProfileDate(value, { month: "short", day: "numeric", year: "numeric" }) || "Updated recently";
if (Number.isNaN(date.getTime())) return "Updated recently";
return new Intl.DateTimeFormat(void 0, {
month: "short",
day: "numeric",
year: "numeric"
}).format(date);
} }
function StatPill$1({ icon, label, value }) { function StatPill$1({ icon, label, value }) {
return /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1.5 rounded-full border border-white/10 bg-white/[0.05] px-2.5 py-1 text-[11px] font-medium text-slate-300" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${icon} text-[10px] text-slate-400` }), /* @__PURE__ */ React.createElement("span", { className: "text-white" }, value), /* @__PURE__ */ React.createElement("span", null, label)); return /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1.5 rounded-full border border-white/10 bg-white/[0.05] px-2.5 py-1 text-[11px] font-medium text-slate-300" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${icon} text-[10px] text-slate-400` }), /* @__PURE__ */ React.createElement("span", { className: "text-white" }, value), /* @__PURE__ */ React.createElement("span", null, label));
@@ -76140,7 +76149,7 @@ function CollectionCard({ collection, isOwner, onDelete, onToggleFeature, onMove
className: `group relative overflow-hidden rounded-[28px] border border-white/10 bg-white/[0.04] shadow-[0_22px_60px_rgba(2,6,23,0.22)] transition-all duration-200 hover:-translate-y-1 hover:border-sky-300/30 hover:bg-white/[0.06] ${busy ? "opacity-70" : ""} lg:max-w-[360px] lg:mx-auto` className: `group relative overflow-hidden rounded-[28px] border border-white/10 bg-white/[0.04] shadow-[0_22px_60px_rgba(2,6,23,0.22)] transition-all duration-200 hover:-translate-y-1 hover:border-sky-300/30 hover:bg-white/[0.06] ${busy ? "opacity-70" : ""} lg:max-w-[360px] lg:mx-auto`
}, },
/* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.12),transparent_34%),radial-gradient(circle_at_bottom_right,rgba(249,115,22,0.12),transparent_28%)] opacity-0 transition duration-300 group-hover:opacity-100" }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.12),transparent_34%),radial-gradient(circle_at_bottom_right,rgba(249,115,22,0.12),transparent_28%)] opacity-0 transition duration-300 group-hover:opacity-100" }),
/* @__PURE__ */ React.createElement("div", { className: "relative" }, /* @__PURE__ */ React.createElement(CoverMedia, { collection, isOwner }), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement("div", { className: "mb-3 flex flex-wrap items-center gap-2" }, collection?.is_featured ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-amber-300/25 bg-amber-300/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Featured") : null, collection?.mode === "smart" ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-sky-300/25 bg-sky-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, "Smart") : null, !isOwner && collection?.program_key ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-lime-300/25 bg-lime-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-lime-100" }, "Program · ", collection.program_key) : null, !isOwner && collection?.partner_label ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-white/10 bg-white/[0.05] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-200" }, "Partner · ", collection.partner_label) : null, !isOwner && collection?.sponsorship_label ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-amber-300/20 bg-amber-300/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Sponsor · ", collection.sponsorship_label) : null, isOwner ? /* @__PURE__ */ React.createElement(CollectionVisibilityBadge, { visibility: collection?.visibility }) : null), /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("h3", { className: "truncate text-lg font-semibold tracking-[-0.03em] text-white" }, collection?.title), collection?.subtitle ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 truncate text-sm text-slate-300" }, collection.subtitle) : null, collection?.owner?.name ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 truncate text-sm text-slate-400" }, "Curated by ", collection.owner.name, collection?.owner?.username ? ` • @${collection.owner.username}` : "") : null, /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, (collection?.artworks_count ?? 0).toLocaleString(), " artworks"))), collection?.description_excerpt ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 line-clamp-2 text-sm leading-relaxed text-slate-300" }, collection.description_excerpt) : collection?.smart_summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 line-clamp-2 text-sm leading-relaxed text-slate-300" }, collection.smart_summary) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(StatPill$1, { icon: "fa-heart", label: "likes", value: (collection?.likes_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatPill$1, { icon: "fa-bell", label: "followers", value: (collection?.followers_count ?? 0).toLocaleString() }), collection?.collaborators_count > 1 ? /* @__PURE__ */ React.createElement(StatPill$1, { icon: "fa-user-group", label: "curators", value: (collection?.collaborators_count ?? 0).toLocaleString() }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex items-center justify-between gap-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, collection?.is_featured ? "Featured" : "Updated", " ", formatUpdated(collection?.featured_at || collection?.updated_at)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2", onClick: stop }, !isOwner && (collection?.save_url || collection?.unsave_url || collection?.login_url) ? /* @__PURE__ */ React.createElement( /* @__PURE__ */ React.createElement("div", { className: "relative" }, /* @__PURE__ */ React.createElement(CoverMedia, { collection, isOwner }), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement("div", { className: "mb-3 flex flex-wrap items-center gap-2" }, collection?.is_featured ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-amber-300/25 bg-amber-300/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Featured") : null, collection?.mode === "smart" ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-sky-300/25 bg-sky-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, "Smart") : null, !isOwner && collection?.program_key ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-lime-300/25 bg-lime-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-lime-100" }, "Program · ", collection.program_key) : null, !isOwner && collection?.partner_label ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-white/10 bg-white/[0.05] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-200" }, "Partner · ", collection.partner_label) : null, !isOwner && collection?.sponsorship_label ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center rounded-full border border-amber-300/20 bg-amber-300/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Sponsor · ", collection.sponsorship_label) : null, isOwner ? /* @__PURE__ */ React.createElement(CollectionVisibilityBadge, { visibility: collection?.visibility }) : null), /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("h3", { className: "truncate text-lg font-semibold tracking-[-0.03em] text-white" }, collection?.title), collection?.subtitle ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 truncate text-sm text-slate-300" }, collection.subtitle) : null, collection?.owner?.name ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 truncate text-sm text-slate-400" }, "Curated by ", collection.owner.name, collection?.owner?.username ? ` • @${collection.owner.username}` : "") : null, /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, formatProfileNumber(collection?.artworks_count ?? 0), " artworks"))), collection?.description_excerpt ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 line-clamp-2 text-sm leading-relaxed text-slate-300" }, collection.description_excerpt) : collection?.smart_summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 line-clamp-2 text-sm leading-relaxed text-slate-300" }, collection.smart_summary) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(StatPill$1, { icon: "fa-heart", label: "likes", value: formatProfileNumber(collection?.likes_count ?? 0) }), /* @__PURE__ */ React.createElement(StatPill$1, { icon: "fa-bell", label: "followers", value: formatProfileNumber(collection?.followers_count ?? 0) }), collection?.collaborators_count > 1 ? /* @__PURE__ */ React.createElement(StatPill$1, { icon: "fa-user-group", label: "curators", value: formatProfileNumber(collection?.collaborators_count ?? 0) }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex items-center justify-between gap-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, collection?.is_featured ? "Featured" : "Updated", " ", formatUpdated(collection?.featured_at || collection?.updated_at)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2", onClick: stop }, !isOwner && (collection?.save_url || collection?.unsave_url || collection?.login_url) ? /* @__PURE__ */ React.createElement(
"button", "button",
{ {
type: "button", type: "button",
@@ -94913,7 +94922,7 @@ function ProfileCoverEditor({
))))); )))));
} }
function formatXp(value) { function formatXp(value) {
return new Intl.NumberFormat().format(Number(value || 0)); return formatProfileNumber(value);
} }
function clampPercent(value) { function clampPercent(value) {
const numeric = Number(value || 0); const numeric = Number(value || 0);
@@ -94977,8 +94986,7 @@ function shinyFlagUrl(countryCode, filesCdnUrl = "") {
return base ? `${base}${relativePath}` : relativePath; return base ? `${base}${relativePath}` : relativePath;
} }
function formatCompactNumber$1(value) { function formatCompactNumber$1(value) {
const numeric = Number(value ?? 0); return formatProfileNumber(value);
return numeric.toLocaleString("en-US");
} }
function ProfileHero({ user, profile, isOwner, viewerIsFollowing, followerCount, recentFollowers = [], followContext = null, heroBgUrl, countryName, leaderboardRank, extraActions = null }) { function ProfileHero({ user, profile, isOwner, viewerIsFollowing, followerCount, recentFollowers = [], followContext = null, heroBgUrl, countryName, leaderboardRank, extraActions = null }) {
const { props } = X$1(); const { props } = X$1();
@@ -94990,7 +94998,7 @@ function ProfileHero({ user, profile, isOwner, viewerIsFollowing, followerCount,
const flagUrl = shinyFlagUrl(profile?.country_code, props?.cdn?.files_url); const flagUrl = shinyFlagUrl(profile?.country_code, props?.cdn?.files_url);
const uname = user.username || user.name || "Unknown"; const uname = user.username || user.name || "Unknown";
const displayName = user.name || uname; const displayName = user.name || uname;
const joinDate = user.created_at ? new Date(user.created_at).toLocaleDateString("en-US", { month: "long", year: "numeric" }) : null; const joinDate = user.created_at ? formatProfileDate(user.created_at, { month: "long", year: "numeric" }) : null;
const progressPercent = Math.round(Number(user?.progress_percent ?? 0)); const progressPercent = Math.round(Number(user?.progress_percent ?? 0));
const heroStats = [ const heroStats = [
{ label: "Followers", value: formatCompactNumber$1(count) }, { label: "Followers", value: formatCompactNumber$1(count) },
@@ -95752,7 +95760,7 @@ function slugify(value) {
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
} }
function formatNumber$9(value) { function formatNumber$9(value) {
return Number(value ?? 0).toLocaleString(); return formatProfileNumber(value);
} }
function sortByPublishedAt(items) { function sortByPublishedAt(items) {
return [...items].sort((left, right) => { return [...items].sort((left, right) => {
@@ -95873,7 +95881,7 @@ function FullGalleryCta({ galleryUrl, username }) {
"Browse full gallery" "Browse full gallery"
))); )));
} }
function TabArtworks({ artworks, featuredArtworks, username, galleryUrl }) { function TabArtworks({ artworks, featuredArtworks, featuredLoading = false, featuredError = false, onRetryFeatured, username, galleryUrl }) {
const initialItems = artworks?.data ?? artworks ?? []; const initialItems = artworks?.data ?? artworks ?? [];
const trendingItems = useArtworkPreview(username, "trending"); const trendingItems = useArtworkPreview(username, "trending");
const popularItems = useArtworkPreview(username, "views"); const popularItems = useArtworkPreview(username, "views");
@@ -95890,6 +95898,7 @@ function TabArtworks({ artworks, featuredArtworks, username, galleryUrl }) {
"aria-labelledby": "tab-artworks", "aria-labelledby": "tab-artworks",
className: "mx-auto max-w-7xl px-4 pt-2 pb-10 md:px-6" className: "mx-auto max-w-7xl px-4 pt-2 pb-10 md:px-6"
}, },
featuredLoading ? /* @__PURE__ */ React.createElement("div", { className: "mb-8 rounded-[28px] border border-white/10 bg-white/[0.03] px-5 py-4 text-sm text-slate-400", role: "status" }, "Loading featured artworks…") : featuredError ? /* @__PURE__ */ React.createElement("div", { className: "mb-8 rounded-[28px] border border-rose-400/20 bg-rose-400/10 px-5 py-4 text-sm text-rose-100", role: "alert" }, "Featured artworks could not be loaded.", onRetryFeatured ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "ml-3 underline", onClick: onRetryFeatured }, "Retry") : null) : null,
/* @__PURE__ */ React.createElement(FeaturedShowcase, { featuredArtworks: featuredArtworks ?? [] }), /* @__PURE__ */ React.createElement(FeaturedShowcase, { featuredArtworks: featuredArtworks ?? [] }),
/* @__PURE__ */ React.createElement( /* @__PURE__ */ React.createElement(
PreviewRail, PreviewRail,
@@ -95974,7 +95983,7 @@ function AchievementCard({ achievement, className = "" }) {
style: { width: `${unlocked ? 100 : percent}%` } style: { width: `${unlocked ? 100 : percent}%` }
} }
)), )),
achievement?.unlocked_at ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs text-slate-500" }, "Unlocked ", new Date(achievement.unlocked_at).toLocaleDateString()) : null achievement?.unlocked_at ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs text-slate-500" }, "Unlocked ", formatProfileDate(achievement.unlocked_at, { year: "numeric", month: "short", day: "numeric" })) : null
); );
} }
function AchievementsList({ unlocked = [], locked = [], limitLocked }) { function AchievementsList({ unlocked = [], locked = [], limitLocked }) {
@@ -96013,11 +96022,7 @@ function TabAchievements({ achievements, worldRewards, worldHistory, onTabChange
function formatDate$a(value) { function formatDate$a(value) {
if (!value) return null; if (!value) return null;
try { try {
return new Intl.DateTimeFormat("en", { return formatProfileDate(value, { month: "short", day: "numeric", year: "numeric" });
month: "short",
day: "numeric",
year: "numeric"
}).format(new Date(value));
} catch { } catch {
return null; return null;
} }
@@ -96025,7 +96030,7 @@ function formatDate$a(value) {
function formatYear(value) { function formatYear(value) {
if (!value) return null; if (!value) return null;
try { try {
return new Intl.DateTimeFormat("en", { year: "numeric" }).format(new Date(value)); return formatProfileDate(value, { year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -96156,8 +96161,8 @@ function YearlyProductivityChart({ recaps }) {
className: `absolute inset-y-0 left-0 rounded-full ${colors.bar} opacity-80 transition-all duration-500 group-hover:opacity-100`, className: `absolute inset-y-0 left-0 rounded-full ${colors.bar} opacity-80 transition-all duration-500 group-hover:opacity-100`,
style: { width: `${(pct * 100).toFixed(1)}%` } style: { width: `${(pct * 100).toFixed(1)}%` }
} }
), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-y-0 left-0 flex w-full items-center px-3 opacity-0 transition-opacity group-hover:opacity-100" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold text-white drop-shadow-sm" }, uploads, " upload", uploads !== 1 ? "s" : "", (item.metrics?.views ?? 0) > 0 ? ` · ${Number(item.metrics.views).toLocaleString()} views` : "", (item.metrics?.downloads ?? 0) > 0 ? ` · ${Number(item.metrics.downloads).toLocaleString()} dl` : ""))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React.createElement("span", { className: `text-[13px] font-bold tabular-nums ${isBest ? "text-white" : "text-slate-300"}` }, uploads), isBest && /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-400/25 bg-amber-400/10 px-1.5 py-0.5 text-[9px] font-bold uppercase tracking-wider text-amber-300" }, "best"), (item.metrics?.featured_count ?? 0) > 0 && !isBest && /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-400/20 bg-sky-400/10 px-1.5 py-0.5 text-[9px] font-bold uppercase tracking-wider text-sky-300" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-star text-[8px]" }), " ", item.metrics.featured_count))); ), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-y-0 left-0 flex w-full items-center px-3 opacity-0 transition-opacity group-hover:opacity-100" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold text-white drop-shadow-sm" }, uploads, " upload", uploads !== 1 ? "s" : "", (item.metrics?.views ?? 0) > 0 ? ` · ${formatProfileNumber(item.metrics.views)} views` : "", (item.metrics?.downloads ?? 0) > 0 ? ` · ${formatProfileNumber(item.metrics.downloads)} dl` : ""))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React.createElement("span", { className: `text-[13px] font-bold tabular-nums ${isBest ? "text-white" : "text-slate-300"}` }, uploads), isBest && /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-400/25 bg-amber-400/10 px-1.5 py-0.5 text-[9px] font-bold uppercase tracking-wider text-amber-300" }, "best"), (item.metrics?.featured_count ?? 0) > 0 && !isBest && /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-400/20 bg-sky-400/10 px-1.5 py-0.5 text-[9px] font-bold uppercase tracking-wider text-sky-300" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-star text-[8px]" }), " ", item.metrics.featured_count)));
})), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-4 border-t border-white/5 pt-4 text-[12px] text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, sorted.length), " active years"), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, sorted.reduce((s2, r) => s2 + r.metrics.uploads_count, 0).toLocaleString()), " total uploads"), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, Number(sorted.reduce((s2, r) => s2 + (r.metrics.views ?? 0), 0)).toLocaleString()), " total views"))); })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-4 border-t border-white/5 pt-4 text-[12px] text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, sorted.length), " active years"), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, formatProfileNumber(sorted.reduce((s2, r) => s2 + r.metrics.uploads_count, 0))), " total uploads"), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, formatProfileNumber(sorted.reduce((s2, r) => s2 + (r.metrics.views ?? 0), 0))), " total views")));
} }
const RELATION_LABELS = { const RELATION_LABELS = {
remake_of: "Remake", remake_of: "Remake",
@@ -96243,7 +96248,7 @@ function CreatorJourneySection({ journey, username }) {
comeback: "bg-amber-400/12 text-amber-200", comeback: "bg-amber-400/12 text-amber-200",
quiet: "bg-slate-700 text-slate-400" quiet: "bg-slate-700 text-slate-400"
}; };
return /* @__PURE__ */ React.createElement("article", { key: item.id, className: "rounded-2xl border border-white/10 bg-white/[0.04] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold uppercase tracking-[0.2em] text-slate-400" }, item.value), status2 && /* @__PURE__ */ React.createElement("span", { className: `rounded-full px-2 py-0.5 text-[10px] font-semibold capitalize ${statusColors[status2] ?? statusColors.steady}` }, status2)), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, item.headline)), /* @__PURE__ */ React.createElement("div", { className: "flex h-11 w-11 items-center justify-center rounded-2xl bg-amber-400/12 text-amber-200" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-column" }))), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-relaxed text-slate-300" }, item.summary), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement(StatPill, { label: "Views", value: (item.metrics?.views ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatPill, { label: "Downloads", value: (item.metrics?.downloads ?? 0).toLocaleString() }), (item.metrics?.featured_count ?? 0) > 0 && /* @__PURE__ */ React.createElement(StatPill, { label: "Featured", value: item.metrics.featured_count }), item.metrics?.top_category && /* @__PURE__ */ React.createElement(StatPill, { label: "Top category", value: item.metrics.top_category }))); return /* @__PURE__ */ React.createElement("article", { key: item.id, className: "rounded-2xl border border-white/10 bg-white/[0.04] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold uppercase tracking-[0.2em] text-slate-400" }, item.value), status2 && /* @__PURE__ */ React.createElement("span", { className: `rounded-full px-2 py-0.5 text-[10px] font-semibold capitalize ${statusColors[status2] ?? statusColors.steady}` }, status2)), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, item.headline)), /* @__PURE__ */ React.createElement("div", { className: "flex h-11 w-11 items-center justify-center rounded-2xl bg-amber-400/12 text-amber-200" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-column" }))), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-relaxed text-slate-300" }, item.summary), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement(StatPill, { label: "Views", value: formatProfileNumber(item.metrics?.views ?? 0) }), /* @__PURE__ */ React.createElement(StatPill, { label: "Downloads", value: formatProfileNumber(item.metrics?.downloads ?? 0) }), (item.metrics?.featured_count ?? 0) > 0 && /* @__PURE__ */ React.createElement(StatPill, { label: "Featured", value: item.metrics.featured_count }), item.metrics?.top_category && /* @__PURE__ */ React.createElement(StatPill, { label: "Top category", value: item.metrics.top_category })));
})))), /* @__PURE__ */ React.createElement(StreaksSection, { streaks }), /* @__PURE__ */ React.createElement(YearlyProductivityChart, { recaps: allRecaps }), /* @__PURE__ */ React.createElement(EvolutionSection, { evolution })); })))), /* @__PURE__ */ React.createElement(StreaksSection, { streaks }), /* @__PURE__ */ React.createElement(YearlyProductivityChart, { recaps: allRecaps }), /* @__PURE__ */ React.createElement(EvolutionSection, { evolution }));
} }
const SOCIAL_ICONS = { const SOCIAL_ICONS = {
@@ -96304,7 +96309,7 @@ function getContentTypeIcon(label) {
return CONTENT_TYPE_ICONS[key] ?? null; return CONTENT_TYPE_ICONS[key] ?? null;
} }
function formatNumber$8(value) { function formatNumber$8(value) {
return Number(value ?? 0).toLocaleString(); return formatProfileNumber(value);
} }
function formatRelativeDate(value) { function formatRelativeDate(value) {
if (!value) return null; if (!value) return null;
@@ -96314,7 +96319,7 @@ function formatRelativeDate(value) {
const now = /* @__PURE__ */ new Date(); const now = /* @__PURE__ */ new Date();
const diffSeconds = Math.round((date.getTime() - now.getTime()) / 1e3); const diffSeconds = Math.round((date.getTime() - now.getTime()) / 1e3);
const absSeconds = Math.abs(diffSeconds); const absSeconds = Math.abs(diffSeconds);
const formatter = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); const formatter = new Intl.RelativeTimeFormat("en-US", { numeric: "auto" });
if (absSeconds < 3600) { if (absSeconds < 3600) {
return formatter.format(Math.round(diffSeconds / 60), "minute"); return formatter.format(Math.round(diffSeconds / 60), "minute");
} }
@@ -96337,7 +96342,7 @@ function formatShortDate$2(value) {
try { try {
const date = new Date(value); const date = new Date(value);
if (Number.isNaN(date.getTime())) return null; if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }); return formatProfileDate(date, { month: "short", day: "numeric", year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -96353,7 +96358,7 @@ function formatContributionDate(value) {
try { try {
const date = new Date(value); const date = new Date(value);
if (Number.isNaN(date.getTime())) return null; if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }); return formatProfileDate(date, { month: "short", day: "numeric", year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -96408,11 +96413,10 @@ function TabAbout({ user, profile, stats, achievements, worldRewards, artworks,
const about = profile?.about; const about = profile?.about;
const website = profile?.website; const website = profile?.website;
const flagUrl = shinyFlagUrl(profile?.country_code, props?.cdn?.files_url); const flagUrl = shinyFlagUrl(profile?.country_code, props?.cdn?.files_url);
const joinDate = user.created_at ? new Date(user.created_at).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" }) : null; const joinDate = user.created_at ? formatProfileDate(user.created_at, { month: "long", day: "numeric", year: "numeric" }) : null;
const lastVisit = user.last_visit_at ? (() => { const lastVisit = user.last_visit_at ? (() => {
try { try {
const d2 = new Date(user.last_visit_at); return formatProfileDate(user.last_visit_at, { month: "short", day: "numeric", year: "numeric" });
return d2.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -96421,7 +96425,7 @@ function TabAbout({ user, profile, stats, achievements, worldRewards, artworks,
const genderLabel = genderMap[profile?.gender?.toUpperCase()] ?? null; const genderLabel = genderMap[profile?.gender?.toUpperCase()] ?? null;
const birthDate = profile?.birthdate ? (() => { const birthDate = profile?.birthdate ? (() => {
try { try {
return new Date(profile.birthdate).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" }); return formatProfileDate(profile.birthdate, { month: "long", day: "numeric", year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -96483,7 +96487,7 @@ function TabAbout({ user, profile, stats, achievements, worldRewards, artworks,
href: entry.group?.profile_url || "#", href: entry.group?.profile_url || "#",
className: "rounded-[24px] border border-white/8 bg-white/[0.03] p-4 transition-colors hover:border-white/14 hover:bg-white/[0.06]" className: "rounded-[24px] border border-white/8 bg-white/[0.03] p-4 transition-colors hover:border-white/14 hover:bg-white/[0.06]"
}, },
/* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-3" }, entry.group?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: entry.group.avatar_url, alt: entry.group?.name, className: "h-12 w-12 rounded-2xl object-cover ring-1 ring-white/10" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-people-group" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-sm font-semibold text-white" }, entry.group?.name), entry.role ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-black/20 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, String(entry.role).replaceAll("_", " ")) : null, entry.trusted_indicator ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-emerald-300/20 bg-emerald-300/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-emerald-100" }, "Trusted") : null), entry.group?.headline ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, truncateText$1(entry.group.headline, 100)) : null, entry.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, entry.summary) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-slate-500" }, /* @__PURE__ */ React.createElement("span", null, Number(entry.counts?.credited_artworks || 0).toLocaleString(), " credited artworks"), /* @__PURE__ */ React.createElement("span", null, Number(entry.counts?.releases || 0).toLocaleString(), " releases"), /* @__PURE__ */ React.createElement("span", null, Number(entry.counts?.projects || 0).toLocaleString(), " projects"), entry.joined_at ? /* @__PURE__ */ React.createElement("span", null, "Joined ", formatContributionDate(entry.joined_at)) : null), Array.isArray(entry.role_labels) && entry.role_labels.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, entry.role_labels.map((label) => /* @__PURE__ */ React.createElement("span", { key: `${entry.group?.slug}-${label}`, className: "rounded-full border border-sky-300/15 bg-sky-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-sky-100" }, label))) : null, Array.isArray(entry.recent_release_titles) && entry.recent_release_titles.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-xs text-slate-400" }, "Recent releases: ", entry.recent_release_titles.join(" • ")) : null)) /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-3" }, entry.group?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: entry.group.avatar_url, alt: entry.group?.name, className: "h-12 w-12 rounded-2xl object-cover ring-1 ring-white/10" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-people-group" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-sm font-semibold text-white" }, entry.group?.name), entry.role ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-black/20 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, String(entry.role).replaceAll("_", " ")) : null, entry.trusted_indicator ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-emerald-300/20 bg-emerald-300/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-emerald-100" }, "Trusted") : null), entry.group?.headline ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, truncateText$1(entry.group.headline, 100)) : null, entry.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, entry.summary) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-slate-500" }, /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts?.credited_artworks || 0), " credited artworks"), /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts?.releases || 0), " releases"), /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts?.projects || 0), " projects"), entry.joined_at ? /* @__PURE__ */ React.createElement("span", null, "Joined ", formatContributionDate(entry.joined_at)) : null), Array.isArray(entry.role_labels) && entry.role_labels.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, entry.role_labels.map((label) => /* @__PURE__ */ React.createElement("span", { key: `${entry.group?.slug}-${label}`, className: "rounded-full border border-sky-300/15 bg-sky-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-sky-100" }, label))) : null, Array.isArray(entry.recent_release_titles) && entry.recent_release_titles.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-xs text-slate-400" }, "Recent releases: ", entry.recent_release_titles.join(" • ")) : null))
)))) : null, followers.length > 0 ? /* @__PURE__ */ React.createElement(SectionCard$2, { icon: "fa-solid fa-user-group", eyebrow: "Community", title: "Recent followers" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2" }, followers.slice(0, 6).map((follower) => /* @__PURE__ */ React.createElement( )))) : null, followers.length > 0 ? /* @__PURE__ */ React.createElement(SectionCard$2, { icon: "fa-solid fa-user-group", eyebrow: "Community", title: "Recent followers" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2" }, followers.slice(0, 6).map((follower) => /* @__PURE__ */ React.createElement(
"a", "a",
{ {
@@ -96587,7 +96591,7 @@ function TabAbout({ user, profile, stats, achievements, worldRewards, artworks,
); );
} }
function KpiCard$1({ icon, label, value, color: color2 = "text-sky-400" }) { function KpiCard$1({ icon, label, value, color: color2 = "text-sky-400" }) {
return /* @__PURE__ */ React.createElement("div", { className: "bg-white/4 ring-1 ring-white/10 rounded-2xl p-5 shadow-xl shadow-black/20 backdrop-blur flex items-center gap-4" }, /* @__PURE__ */ React.createElement("div", { className: `w-12 h-12 rounded-xl bg-white/5 flex items-center justify-center shrink-0 ${color2}` }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${icon} text-xl` })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-2xl font-bold text-white tabular-nums" }, Number(value ?? 0).toLocaleString()), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500 mt-0.5 uppercase tracking-wider" }, label))); return /* @__PURE__ */ React.createElement("div", { className: "bg-white/4 ring-1 ring-white/10 rounded-2xl p-5 shadow-xl shadow-black/20 backdrop-blur flex items-center gap-4" }, /* @__PURE__ */ React.createElement("div", { className: `w-12 h-12 rounded-xl bg-white/5 flex items-center justify-center shrink-0 ${color2}` }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${icon} text-xl` })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-2xl font-bold text-white tabular-nums" }, formatProfileNumber(value)), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500 mt-0.5 uppercase tracking-wider" }, label)));
} }
function TabStats({ stats, followerCount, followAnalytics }) { function TabStats({ stats, followerCount, followAnalytics }) {
const medalTotals = stats?.medal_totals ?? null; const medalTotals = stats?.medal_totals ?? null;
@@ -96616,18 +96620,18 @@ function TabStats({ stats, followerCount, followAnalytics }) {
"aria-labelledby": "tab-stats", "aria-labelledby": "tab-stats",
className: "pt-6" className: "pt-6"
}, },
!hasStats ? /* @__PURE__ */ React.createElement("div", { className: "bg-white/4 ring-1 ring-white/10 rounded-2xl p-10 text-center shadow-xl shadow-black/20" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-bar text-3xl text-slate-600 mb-3 block" }), /* @__PURE__ */ React.createElement("p", { className: "text-slate-400 font-medium" }, "No stats available yet"), /* @__PURE__ */ React.createElement("p", { className: "text-slate-600 text-sm mt-1" }, "Stats will appear once there is activity on this profile.")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("h2", { className: "text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-bar text-green-400 fa-fw" }), "Lifetime Statistics"), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3" }, kpis.map((kpi) => /* @__PURE__ */ React.createElement(KpiCard$1, { key: kpi.label, ...kpi }))), /* @__PURE__ */ React.createElement("div", { className: "mt-8 rounded-2xl border border-white/10 bg-white/4 p-5 shadow-xl shadow-black/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", { className: "text-xs font-semibold uppercase tracking-widest text-slate-500" }, "Medal Breakdown"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, "Real medal totals collected across all public artworks.")), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-widest text-slate-500" }, "Weighted Score"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-2xl font-bold text-white tabular-nums" }, Number(medalTotals?.score_total ?? 0).toLocaleString()))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3 md:grid-cols-4" }, [ !hasStats ? /* @__PURE__ */ React.createElement("div", { className: "bg-white/4 ring-1 ring-white/10 rounded-2xl p-10 text-center shadow-xl shadow-black/20" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-bar text-3xl text-slate-600 mb-3 block" }), /* @__PURE__ */ React.createElement("p", { className: "text-slate-400 font-medium" }, "No stats available yet"), /* @__PURE__ */ React.createElement("p", { className: "text-slate-600 text-sm mt-1" }, "Stats will appear once there is activity on this profile.")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("h2", { className: "text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-bar text-green-400 fa-fw" }), "Lifetime Statistics"), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3" }, kpis.map((kpi) => /* @__PURE__ */ React.createElement(KpiCard$1, { key: kpi.label, ...kpi }))), /* @__PURE__ */ React.createElement("div", { className: "mt-8 rounded-2xl border border-white/10 bg-white/4 p-5 shadow-xl shadow-black/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", { className: "text-xs font-semibold uppercase tracking-widest text-slate-500" }, "Medal Breakdown"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, "Real medal totals collected across all public artworks.")), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-widest text-slate-500" }, "Weighted Score"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-2xl font-bold text-white tabular-nums" }, formatProfileNumber(medalTotals?.score_total ?? 0)))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3 md:grid-cols-4" }, [
{ label: "Gold", value: medalTotals?.gold ?? 0, color: "text-amber-300" }, { label: "Gold", value: medalTotals?.gold ?? 0, color: "text-amber-300" },
{ label: "Silver", value: medalTotals?.silver ?? 0, color: "text-slate-300" }, { label: "Silver", value: medalTotals?.silver ?? 0, color: "text-slate-300" },
{ label: "Bronze", value: medalTotals?.bronze ?? 0, color: "text-orange-300" }, { label: "Bronze", value: medalTotals?.bronze ?? 0, color: "text-orange-300" },
{ label: "Total Medals", value: medalTotals?.count ?? 0, color: "text-cyan-300" } { label: "Total Medals", value: medalTotals?.count ?? 0, color: "text-cyan-300" }
].map((item) => /* @__PURE__ */ React.createElement("div", { key: item.label, className: "rounded-2xl border border-white/10 bg-black/15 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-widest text-slate-500" }, item.label), /* @__PURE__ */ React.createElement("div", { className: `mt-2 text-2xl font-semibold tabular-nums ${item.color}` }, Number(item.value).toLocaleString()))))), /* @__PURE__ */ React.createElement("h3", { className: "mt-8 mb-4 text-xs font-semibold uppercase tracking-widest text-slate-500 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user-group text-emerald-400 fa-fw" }), "Follow Growth"), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3" }, trendCards.map((card) => /* @__PURE__ */ React.createElement(KpiCard$1, { key: card.label, ...card }))), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-600 mt-6 text-center" }, "More detailed analytics (charts, trends) coming soon.")) ].map((item) => /* @__PURE__ */ React.createElement("div", { key: item.label, className: "rounded-2xl border border-white/10 bg-black/15 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-widest text-slate-500" }, item.label), /* @__PURE__ */ React.createElement("div", { className: `mt-2 text-2xl font-semibold tabular-nums ${item.color}` }, formatProfileNumber(item.value)))))), /* @__PURE__ */ React.createElement("h3", { className: "mt-8 mb-4 text-xs font-semibold uppercase tracking-widest text-slate-500 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user-group text-emerald-400 fa-fw" }), "Follow Growth"), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3" }, trendCards.map((card) => /* @__PURE__ */ React.createElement(KpiCard$1, { key: card.label, ...card }))), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-600 mt-6 text-center" }, "More detailed analytics (charts, trends) coming soon."))
); );
} }
function FavSkeleton() { function FavSkeleton() {
return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl overflow-hidden bg-white/5 animate-pulse" }, /* @__PURE__ */ React.createElement("div", { className: "aspect-square bg-white/8" })); return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl overflow-hidden bg-white/5 animate-pulse" }, /* @__PURE__ */ React.createElement("div", { className: "aspect-square bg-white/8" }));
} }
function TabFavourites({ favourites, isOwner, username }) { function TabFavourites({ favourites, isOwner, username, loading = false, error = false, onRetry }) {
const initialItems = Array.isArray(favourites) ? favourites : favourites?.data ?? []; const initialItems = Array.isArray(favourites) ? favourites : favourites?.data ?? [];
const [items, setItems] = reactExports.useState(initialItems); const [items, setItems] = reactExports.useState(initialItems);
const [nextCursor, setNextCursor] = reactExports.useState(favourites?.next_cursor ?? null); const [nextCursor, setNextCursor] = reactExports.useState(favourites?.next_cursor ?? null);
@@ -96681,7 +96685,9 @@ function TabFavourites({ favourites, isOwner, username }) {
className: "pt-6" className: "pt-6"
}, },
/* @__PURE__ */ React.createElement("h2", { className: "text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-heart text-pink-400 fa-fw" }), isOwner ? "Your Favourites" : "Favourites"), /* @__PURE__ */ React.createElement("h2", { className: "text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-heart text-pink-400 fa-fw" }), isOwner ? "Your Favourites" : "Favourites"),
items.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center justify-center py-20 text-center" }, /* @__PURE__ */ React.createElement("div", { className: "w-20 h-20 rounded-2xl bg-white/5 flex items-center justify-center mb-5 text-slate-500" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-heart text-3xl" })), /* @__PURE__ */ React.createElement("p", { className: "text-slate-400 font-medium" }, "No favourites yet"), /* @__PURE__ */ React.createElement("p", { className: "text-slate-600 text-sm mt-1" }, "Artworks added to favourites will appear here.")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement( loading && items.length === 0 && /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-4" }, Array.from({ length: 4 }).map((_2, i) => /* @__PURE__ */ React.createElement(FavSkeleton, { key: `initial-sk-${i}` }))),
error && items.length === 0 && /* @__PURE__ */ React.createElement("div", { className: "py-12 text-center text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "Favourites could not be loaded."), onRetry ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: onRetry, className: "mt-4 rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-slate-200" }, "Retry") : null),
!loading && !error && items.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center justify-center py-20 text-center" }, /* @__PURE__ */ React.createElement("div", { className: "w-20 h-20 rounded-2xl bg-white/5 flex items-center justify-center mb-5 text-slate-500" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-heart text-3xl" })), /* @__PURE__ */ React.createElement("p", { className: "text-slate-400 font-medium" }, "No favourites yet"), /* @__PURE__ */ React.createElement("p", { className: "text-slate-600 text-sm mt-1" }, "Artworks added to favourites will appear here.")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
ArtworkGallery, ArtworkGallery,
{ {
items, items,
@@ -98197,9 +98203,7 @@ function ProfileWorldRecognitionBadge({ recognition, isPrimary = false }) {
function formatDate$9(value) { function formatDate$9(value) {
if (!value) return null; if (!value) return null;
try { try {
const date = new Date(value); return formatProfileDate(value, { month: "short", day: "numeric", year: "numeric" });
if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -98221,9 +98225,7 @@ function ProfileWorldHistorySummary({ history: history2 }) {
function formatDate$8(value) { function formatDate$8(value) {
if (!value) return null; if (!value) return null;
try { try {
const date = new Date(value); return formatProfileDate(value, { month: "short", day: "numeric", year: "numeric" });
if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
} catch { } catch {
return null; return null;
} }
@@ -98313,13 +98315,7 @@ function TabWorlds({ worldHistory, isOwner }) {
function formatFollowedAt(value) { function formatFollowedAt(value) {
if (!value) return null; if (!value) return null;
try { try {
const date = new Date(value); return formatProfileDate(value, { year: "numeric", month: "short", day: "numeric" });
if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString(void 0, {
year: "numeric",
month: "short",
day: "numeric"
});
} catch { } catch {
return null; return null;
} }
@@ -98339,7 +98335,7 @@ function TabFollowers({ listing, username, followerCount }) {
"aria-labelledby": "tab-followers", "aria-labelledby": "tab-followers",
className: "pt-6" className: "pt-6"
}, },
/* @__PURE__ */ React.createElement("h2", { className: "mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-slate-500" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-users text-sky-400 fa-fw" }), "Followers", /* @__PURE__ */ React.createElement("span", { className: "rounded bg-white/5 px-1.5 py-0.5 text-[10px] font-medium normal-case tracking-normal text-slate-400" }, total.toLocaleString())), /* @__PURE__ */ React.createElement("h2", { className: "mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-slate-500" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-users text-sky-400 fa-fw" }), "Followers", /* @__PURE__ */ React.createElement("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))),
items.length === 0 ? /* @__PURE__ */ React.createElement("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.") : /* @__PURE__ */ React.createElement("ul", { className: "grid gap-3 sm:grid-cols-2" }, items.map((follower) => { items.length === 0 ? /* @__PURE__ */ React.createElement("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.") : /* @__PURE__ */ React.createElement("ul", { className: "grid gap-3 sm:grid-cols-2" }, items.map((follower) => {
const handle = follower.username || follower.display_name || follower.name; const handle = follower.username || follower.display_name || follower.name;
const followedAt = formatFollowedAt(follower.followed_at); const followedAt = formatFollowedAt(follower.followed_at);
@@ -98363,10 +98359,164 @@ function TabFollowers({ listing, username, followerCount }) {
lastPage > 1 ? /* @__PURE__ */ React.createElement("nav", { className: "mt-6 flex items-center justify-center gap-3 text-sm", "aria-label": "Followers pagination" }, prevUrl ? /* @__PURE__ */ React.createElement("a", { href: prevUrl, className: "rounded-xl border border-white/10 px-3 py-2 text-slate-300 hover:bg-white/5" }, "Previous") : /* @__PURE__ */ React.createElement("span", { className: "rounded-xl border border-white/5 px-3 py-2 text-slate-600" }, "Previous"), /* @__PURE__ */ React.createElement("span", { className: "text-slate-500" }, "Page ", currentPage, " of ", lastPage), nextUrl ? /* @__PURE__ */ React.createElement("a", { href: nextUrl, className: "rounded-xl border border-white/10 px-3 py-2 text-slate-300 hover:bg-white/5" }, "Next") : /* @__PURE__ */ React.createElement("span", { className: "rounded-xl border border-white/5 px-3 py-2 text-slate-600" }, "Next")) : null lastPage > 1 ? /* @__PURE__ */ React.createElement("nav", { className: "mt-6 flex items-center justify-center gap-3 text-sm", "aria-label": "Followers pagination" }, prevUrl ? /* @__PURE__ */ React.createElement("a", { href: prevUrl, className: "rounded-xl border border-white/10 px-3 py-2 text-slate-300 hover:bg-white/5" }, "Previous") : /* @__PURE__ */ React.createElement("span", { className: "rounded-xl border border-white/5 px-3 py-2 text-slate-600" }, "Previous"), /* @__PURE__ */ React.createElement("span", { className: "text-slate-500" }, "Page ", currentPage, " of ", lastPage), nextUrl ? /* @__PURE__ */ React.createElement("a", { href: nextUrl, className: "rounded-xl border border-white/10 px-3 py-2 text-slate-300 hover:bg-white/5" }, "Next") : /* @__PURE__ */ React.createElement("span", { className: "rounded-xl border border-white/5 px-3 py-2 text-slate-600" }, "Next")) : null
); );
} }
function normalizeGroupContribution(entry = {}) {
const counts = entry?.counts || {};
const role = entry?.role;
return {
...entry,
roleLabel: typeof role === "string" ? role : role?.label || null,
trusted: entry?.trusted_indicator ?? entry?.trusted ?? false,
counts: {
...counts,
credited_artworks: counts.credited_artworks ?? counts.artworks ?? 0,
releases: counts.releases ?? 0,
projects: counts.projects ?? 0,
review_actions: counts.review_actions ?? 0
},
role_labels: Array.isArray(entry?.role_labels) ? entry.role_labels : [],
recent_release_titles: Array.isArray(entry?.recent_release_titles) ? entry.recent_release_titles : []
};
}
function GroupProfileSummary({ contributions = [], href = null }) { function GroupProfileSummary({ contributions = [], href = null }) {
if (!Array.isArray(contributions) || contributions.length === 0) return null; if (!Array.isArray(contributions) || contributions.length === 0) return null;
const featured = contributions.slice(0, 3); const featured = contributions.slice(0, 3).map(normalizeGroupContribution);
return /* @__PURE__ */ React.createElement("section", { className: "mx-auto mt-8 max-w-6xl px-4 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[30px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(14,165,233,0.12),transparent_34%),linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02))] p-5 shadow-[0_18px_55px_rgba(2,6,23,0.28)] backdrop-blur-xl sm:p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80" }, "Group footprint"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Collaborative work across public groups"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-6 text-slate-400" }, "See the groups this creator contributes to through releases, credited artworks, and shared publishing activity.")), href ? /* @__PURE__ */ React.createElement("a", { href, className: "text-sm font-semibold text-sky-200 transition hover:text-white" }, "View full contribution history") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-4 lg:grid-cols-3" }, featured.map((entry) => /* @__PURE__ */ React.createElement("a", { key: entry.group?.slug || entry.group?.id, href: entry.group?.profile_url || "/groups", className: "rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20 hover:bg-black/30" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-white/[0.04]" }, entry.group?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: entry.group.avatar_url, alt: entry.group?.name, className: "h-full w-full object-cover", loading: "lazy" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-people-group text-slate-300" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-base font-semibold text-white" }, entry.group?.name), entry.role?.label ? /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, entry.role.label) : null, entry.summary ? /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-300" }, entry.summary) : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, entry.trusted ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Trusted contributor", tone: "sky" }) : null, entry.recent_release_titles?.length ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Recent releases", tone: "amber" }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-4 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, Number(entry.counts?.artworks || 0).toLocaleString("en-US"), " artworks"), /* @__PURE__ */ React.createElement("span", null, Number(entry.counts?.releases || 0).toLocaleString("en-US"), " releases"), /* @__PURE__ */ React.createElement("span", null, Number(entry.counts?.projects || 0).toLocaleString("en-US"), " projects"))))))); return /* @__PURE__ */ React.createElement("section", { className: "mx-auto mt-8 max-w-6xl px-4 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[30px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(14,165,233,0.12),transparent_34%),linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02))] p-5 shadow-[0_18px_55px_rgba(2,6,23,0.28)] backdrop-blur-xl sm:p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80" }, "Group footprint"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Collaborative work across public groups"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-6 text-slate-400" }, "See the groups this creator contributes to through releases, credited artworks, and shared publishing activity.")), href ? /* @__PURE__ */ React.createElement("a", { href, className: "text-sm font-semibold text-sky-200 transition hover:text-white" }, "View full contribution history") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-4 lg:grid-cols-3" }, featured.map((entry) => /* @__PURE__ */ React.createElement("a", { key: entry.group?.slug || entry.group?.id, href: entry.group?.profile_url || "/groups", className: "rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20 hover:bg-black/30" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-white/[0.04]" }, entry.group?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: entry.group.avatar_url, alt: entry.group?.name, className: "h-full w-full object-cover", loading: "lazy" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-people-group text-slate-300" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-base font-semibold text-white" }, entry.group?.name), entry.roleLabel ? /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, entry.roleLabel) : null, entry.summary ? /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-300" }, entry.summary) : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, entry.trusted ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Trusted contributor", tone: "sky" }) : null, entry.recent_release_titles?.length ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Recent releases", tone: "amber" }) : null, entry.role_labels.map((label, index2) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: `${label}-${index2}`, label, tone: "slate" }))), entry.recent_release_titles.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-300" }, "Recent releases:"), " ", entry.recent_release_titles.join(", ")) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-4 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts.credited_artworks), " artworks"), /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts.releases), " releases"), /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts.projects), " projects"), /* @__PURE__ */ React.createElement("span", null, formatProfileNumber(entry.counts.review_actions), " reviews")))))));
}
function useLazyProfileFeaturedArtworks({ username, initialFeaturedArtworks, enabled }) {
const hasInitialValue = Array.isArray(initialFeaturedArtworks);
const [items, setItems] = reactExports.useState(() => hasInitialValue ? initialFeaturedArtworks : []);
const [loaded, setLoaded] = reactExports.useState(hasInitialValue);
const [loading, setLoading] = reactExports.useState(false);
const [error, setError] = reactExports.useState(false);
const requestKeyRef = reactExports.useRef(username);
const requestedRef = reactExports.useRef(hasInitialValue);
const abortRef = reactExports.useRef(null);
reactExports.useEffect(() => {
if (requestKeyRef.current === username) return void 0;
abortRef.current?.abort();
abortRef.current = null;
requestKeyRef.current = username;
requestedRef.current = false;
setItems([]);
setLoaded(false);
setLoading(false);
setError(false);
return void 0;
}, [username]);
reactExports.useEffect(() => {
if (!hasInitialValue || requestKeyRef.current !== username) return;
abortRef.current?.abort();
abortRef.current = null;
requestedRef.current = true;
setItems(initialFeaturedArtworks);
setLoaded(true);
setLoading(false);
setError(false);
}, [hasInitialValue, initialFeaturedArtworks, username]);
const load = reactExports.useCallback(async () => {
if (!enabled || hasInitialValue || requestedRef.current || !username) return;
requestedRef.current = true;
const controller = new AbortController();
const requestKey = requestKeyRef.current;
abortRef.current = controller;
setLoading(true);
setError(false);
try {
const response = await fetch(`/api/profile/${encodeURIComponent(username)}/featured-artworks`, {
headers: { Accept: "application/json" },
credentials: "same-origin",
signal: controller.signal
});
if (!response.ok) throw new Error("Featured artworks could not be loaded.");
const payload = await response.json();
if (requestKey !== requestKeyRef.current || controller.signal.aborted) return;
setItems(Array.isArray(payload?.data) ? payload.data : []);
setLoaded(true);
} catch (caught) {
if (caught?.name === "AbortError" || requestKey !== requestKeyRef.current) return;
requestedRef.current = false;
setError(true);
} finally {
if (requestKey === requestKeyRef.current) {
abortRef.current = null;
setLoading(false);
}
}
}, [enabled, hasInitialValue, username]);
reactExports.useEffect(() => {
if (enabled) load();
}, [enabled, load]);
reactExports.useEffect(() => () => abortRef.current?.abort(), []);
return { items, loaded, loading, error, retry: load };
}
function useLazyProfileFavourites({ username, initialFavourites, enabled }) {
const hasInitialValue = initialFavourites !== null && initialFavourites !== void 0;
const initialItems = Array.isArray(initialFavourites) ? initialFavourites : initialFavourites?.data ?? [];
const [items, setItems] = reactExports.useState(() => initialItems);
const [nextCursor, setNextCursor] = reactExports.useState(() => initialFavourites?.next_cursor ?? null);
const [loading, setLoading] = reactExports.useState(false);
const [error, setError] = reactExports.useState(false);
const requestKeyRef = reactExports.useRef(username);
const requestedRef = reactExports.useRef(hasInitialValue);
const abortRef = reactExports.useRef(null);
reactExports.useEffect(() => {
if (requestKeyRef.current === username) return void 0;
abortRef.current?.abort();
abortRef.current = null;
requestKeyRef.current = username;
requestedRef.current = false;
setItems([]);
setNextCursor(null);
setLoading(false);
setError(false);
return void 0;
}, [username]);
reactExports.useEffect(() => {
if (requestKeyRef.current !== username || !hasInitialValue) return void 0;
abortRef.current?.abort();
abortRef.current = null;
requestedRef.current = true;
setItems(initialItems);
setNextCursor(initialFavourites?.next_cursor ?? null);
setLoading(false);
setError(false);
return void 0;
}, [hasInitialValue, initialFavourites, initialItems, username]);
const load = reactExports.useCallback(async () => {
if (!enabled || hasInitialValue || requestedRef.current || !username) return;
requestedRef.current = true;
const controller = new AbortController();
const requestKey = requestKeyRef.current;
abortRef.current = controller;
setLoading(true);
setError(false);
try {
const response = await fetch(`/api/profile/${encodeURIComponent(username)}/favourites`, {
headers: { Accept: "application/json" },
credentials: "same-origin",
signal: controller.signal
});
if (!response.ok) throw new Error("Favourites could not be loaded.");
const payload = await response.json();
if (requestKey !== requestKeyRef.current || controller.signal.aborted) return;
setItems(Array.isArray(payload?.data) ? payload.data : []);
setNextCursor(payload?.next_cursor ?? null);
} catch (caught) {
if (caught?.name === "AbortError" || requestKey !== requestKeyRef.current) return;
requestedRef.current = false;
setError(true);
} finally {
if (requestKey === requestKeyRef.current) {
abortRef.current = null;
setLoading(false);
}
}
}, [enabled, hasInitialValue, username]);
reactExports.useEffect(() => {
if (enabled) load();
}, [enabled, load]);
reactExports.useEffect(() => () => abortRef.current?.abort(), []);
return { items, nextCursor, loading, error, retry: load };
} }
const VALID_TABS = ["posts", "artworks", "stories", "achievements", "worlds", "collections", "about", "stats", "favourites", "activity", "followers"]; const VALID_TABS = ["posts", "artworks", "stories", "achievements", "worlds", "collections", "about", "stats", "favourites", "activity", "followers"];
function getInitialTab(initialTab = "posts") { function getInitialTab(initialTab = "posts") {
@@ -98426,6 +98576,16 @@ function ProfileShow() {
seo seo
} = props; } = props;
const [activeTab, setActiveTab] = reactExports.useState(() => getInitialTab(initialTab)); const [activeTab, setActiveTab] = reactExports.useState(() => getInitialTab(initialTab));
const featuredState = useLazyProfileFeaturedArtworks({
username: user?.username || user?.name || "",
initialFeaturedArtworks: featuredArtworks,
enabled: activeTab === "artworks"
});
const favouritesState = useLazyProfileFavourites({
username: user?.username || user?.name || "",
initialFavourites: favourites,
enabled: activeTab === "favourites"
});
const handleTabChange = reactExports.useCallback((tab2) => { const handleTabChange = reactExports.useCallback((tab2) => {
if (!VALID_TABS.includes(tab2)) return; if (!VALID_TABS.includes(tab2)) return;
if (tab2 === "followers") { if (tab2 === "followers") {
@@ -98454,8 +98614,8 @@ function ProfileShow() {
const isLoggedIn = !!auth?.user; const isLoggedIn = !!auth?.user;
const artworkList = Array.isArray(artworks) ? artworks : artworks?.data ?? []; const artworkList = Array.isArray(artworks) ? artworks : artworks?.data ?? [];
const artworkNextCursor = artworks?.next_cursor ?? null; const artworkNextCursor = artworks?.next_cursor ?? null;
const favouriteList = Array.isArray(favourites) ? favourites : favourites?.data ?? []; const favouriteList = favouritesState.items;
const favouriteNextCursor = favourites?.next_cursor ?? null; const favouriteNextCursor = favouritesState.nextCursor;
const socialLinksObj = Array.isArray(socialLinks) ? socialLinks.reduce((acc, l3) => { const socialLinksObj = Array.isArray(socialLinks) ? socialLinks.reduce((acc, l3) => {
acc[l3.platform] = l3; acc[l3.platform] = l3;
return acc; return acc;
@@ -98510,7 +98670,10 @@ function ProfileShow() {
TabArtworks, TabArtworks,
{ {
artworks: { data: artworkList, next_cursor: artworkNextCursor }, artworks: { data: artworkList, next_cursor: artworkNextCursor },
featuredArtworks, featuredArtworks: featuredState.items,
featuredLoading: featuredState.loading,
featuredError: featuredState.error,
onRetryFeatured: featuredState.retry,
username: user.username || user.name, username: user.username || user.name,
galleryUrl, galleryUrl,
isActive: true isActive: true
@@ -98580,7 +98743,10 @@ function ProfileShow() {
{ {
favourites: { data: favouriteList, next_cursor: favouriteNextCursor }, favourites: { data: favouriteList, next_cursor: favouriteNextCursor },
isOwner, isOwner,
username: user.username || user.name username: user.username || user.name,
loading: favouritesState.loading,
error: favouritesState.error,
onRetry: favouritesState.retry
} }
), activeTab === "activity" && /* @__PURE__ */ React.createElement( ), activeTab === "activity" && /* @__PURE__ */ React.createElement(
TabActivity, TabActivity,