import React from 'react' function formatDate(value) { if (!value) return null try { return new Intl.DateTimeFormat('en', { month: 'short', day: 'numeric', year: 'numeric', }).format(new Date(value)) } catch { return null } } function formatYear(value) { if (!value) return null try { return new Intl.DateTimeFormat('en', { year: 'numeric' }).format(new Date(value)) } catch { return null } } function iconForType(type) { switch (type) { case 'first_upload': return 'fa-solid fa-seedling' case 'first_featured_artwork': return 'fa-solid fa-star' case 'first_group_release': return 'fa-solid fa-people-group' case 'biggest_download_spike': return 'fa-solid fa-bolt' case 'best_performing_work': return 'fa-solid fa-trophy' case 'most_productive_year': return 'fa-solid fa-calendar-check' case 'yearly_recap': return 'fa-solid fa-chart-column' // v2 case 'comeback_minor': return 'fa-solid fa-rotate-right' case 'comeback_major': return 'fa-solid fa-person-walking-arrow-right' case 'comeback_legendary': return 'fa-solid fa-fire-flame-curved' case 'upload_streak_3': case 'upload_streak_6': case 'upload_streak_12': return 'fa-solid fa-fire' case 'active_year_streak_3': case 'active_year_streak_5': return 'fa-solid fa-calendar-days' case 'before_now': return 'fa-solid fa-arrows-rotate' case 'era_started': return 'fa-solid fa-flag' default: return 'fa-solid fa-sparkles' } } function milestoneHref(item) { return item?.artwork?.url || item?.release?.url || null } function StatPill({ label, value }) { if (value === null || value === undefined || value === '') return null return (
{label}
{value}
) } function EmptyJourneyState({ username, memberSinceYear, yearsOnSkinbase }) { return (
Creator Journey is just getting started
Public milestones will appear here as @{username} builds more history on Skinbase.
) } // ── v2: Era strip ───────────────────────────────────────────────────────────── const ERA_COLORS = { early_years: { bg: 'bg-slate-800/60', border: 'border-slate-600/40', text: 'text-slate-300', icon: 'fa-solid fa-seedling', dot: 'bg-slate-400' }, breakthrough: { bg: 'bg-amber-900/30', border: 'border-amber-600/30', text: 'text-amber-200', icon: 'fa-solid fa-star', dot: 'bg-amber-400' }, experimental: { bg: 'bg-violet-900/30', border: 'border-violet-600/30', text: 'text-violet-200', icon: 'fa-solid fa-flask', dot: 'bg-violet-400' }, comeback: { bg: 'bg-emerald-900/30', border: 'border-emerald-600/30', text: 'text-emerald-200', icon: 'fa-solid fa-rotate-right', dot: 'bg-emerald-400' }, current: { bg: 'bg-sky-900/30', border: 'border-sky-600/30', text: 'text-sky-200', icon: 'fa-solid fa-bolt', dot: 'bg-sky-400' }, } function EraStrip({ eras }) { if (!eras?.length) return null return (
Creator Eras
{eras.map((era, i) => { const style = ERA_COLORS[era.type] ?? ERA_COLORS.current return (
{era.title} {era.is_current && ( Now )}
{formatYear(era.starts_at)} {era.ends_at ? ` – ${formatYear(era.ends_at)}` : era.is_current ? ' – present' : ''}
{era.description && (

{era.description}

)} {(era.stats?.uploads_count ?? 0) > 0 && (
{era.stats.uploads_count} upload{era.stats.uploads_count !== 1 ? 's' : ''}
)}
) })}
) } // ── v2: Streaks ────────────────────────────────────────────────────────────── function StreakBadge({ label, value, active = false }) { if (!value) return null return (
{value}
{label}
) } function StreaksSection({ streaks }) { if (!streaks) return null const { current_monthly_upload_streak, best_monthly_upload_streak, current_active_year_streak, best_active_year_streak } = streaks const hasAny = current_monthly_upload_streak > 0 || best_monthly_upload_streak > 0 || best_active_year_streak > 0 if (!hasAny) return null return (
Creative Streaks
{current_monthly_upload_streak > 0 && ( )} {best_monthly_upload_streak > 0 && ( )} {current_active_year_streak > 0 && ( = 3} /> )} {best_active_year_streak > 0 && ( )}
) } // ── v2: Growth & Evolution ─────────────────────────────────────────────────── const RELATION_LABELS = { remake_of: 'Remake', remaster_of: 'Remaster', revision_of: 'Revision', inspired_by: 'Inspired by own work', variation_of: 'Variation', } function EvolutionSection({ evolution }) { if (!evolution?.length) return null return (
Growth & Evolution
Then & Now
{evolution.map((item) => (
{/* Original */}
Original
{item.target_artwork?.title}
{formatYear(item.target_artwork?.published_at)}
{/* Arrow + relation type */}
{RELATION_LABELS[item.relation_type] ?? item.relation_type} {item.years_between > 0 && ( {item.years_between}yr later )}
{/* New version */}
New version
{item.source_artwork?.title}
{formatYear(item.source_artwork?.published_at)}
))}
) } export default function CreatorJourneySection({ journey, username }) { const summary = journey?.summary ?? {} const highlights = Array.isArray(journey?.highlights) ? journey.highlights : [] const timeline = Array.isArray(journey?.timeline) ? journey.timeline.slice(0, 6) : [] const recaps = Array.isArray(journey?.yearly_recaps) ? journey.yearly_recaps.slice(0, 3) : [] const eras = Array.isArray(journey?.eras) ? journey.eras : [] const evolution = Array.isArray(journey?.evolution) ? journey.evolution : [] const streaks = journey?.streaks ?? null const latestMilestone = summary.latest_milestone ?? null const available = !!summary.available if (!available) { return (
Creator Journey

A profile built as a story, not only a feed

) } return (
Creator Journey

A profile shaped by milestones, turning points, and yearly chapters.

{latestMilestone && (

Latest moment: {latestMilestone.title} {latestMilestone.headline ? ` - ${latestMilestone.headline}` : ''}

)}
{/* ── v2: Era strip ── */} {highlights.length > 0 && (
{highlights.map((item) => { const href = milestoneHref(item) return (
{item.title}
{item.headline || item.value}
{item.value && (
{item.value}
)}
{item.summary && (

{item.summary}

)} {href && ( Open source moment )}
) })}
)}
Timeline
Important creator milestones
{timeline.map((item, index) => { const href = milestoneHref(item) return (
{index < timeline.length - 1 &&
}
{item.title}
{formatDate(item.occurred_at) && (
{formatDate(item.occurred_at)}
)}
{item.headline &&
{item.headline}
} {item.summary &&
{item.summary}
} {href && ( View linked work )}
) })}
Yearly recap
Recent chapters
{recaps.map((item) => { const status = item.metrics?.year_status const statusColors = { breakout: 'bg-emerald-400/12 text-emerald-200', steady: 'bg-sky-400/12 text-sky-200', experimental: 'bg-violet-400/12 text-violet-200', comeback: 'bg-amber-400/12 text-amber-200', quiet: 'bg-slate-700 text-slate-400', } return (
{item.value}
{status && ( {status} )}
{item.headline}

{item.summary}

{(item.metrics?.featured_count ?? 0) > 0 && ( )} {item.metrics?.top_category && ( )}
) })}
{/* ── v2: Streaks ── */} {/* ── v2: Growth & Evolution ── */}
) }