Repair: copy legacy joinDate into new user's created_at when creating users from legacy wallz

This commit is contained in:
2026-03-22 09:13:39 +01:00
parent e8b5edf5d2
commit 2608be7420
80 changed files with 3991 additions and 723 deletions

View File

@@ -1,7 +1,6 @@
import React, { useState, useEffect, useCallback } from 'react'
import { usePage } from '@inertiajs/react'
import ProfileHero from '../../components/profile/ProfileHero'
import ProfileStatsRow from '../../components/profile/ProfileStatsRow'
import ProfileTabs from '../../components/profile/ProfileTabs'
import TabArtworks from '../../components/profile/tabs/TabArtworks'
import TabAchievements from '../../components/profile/tabs/TabAchievements'
@@ -13,16 +12,26 @@ import TabActivity from '../../components/profile/tabs/TabActivity'
import TabPosts from '../../components/profile/tabs/TabPosts'
import TabStories from '../../components/profile/tabs/TabStories'
const VALID_TABS = ['artworks', 'stories', 'achievements', 'posts', 'collections', 'about', 'stats', 'favourites', 'activity']
const VALID_TABS = ['posts', 'artworks', 'stories', 'achievements', 'collections', 'about', 'stats', 'favourites', 'activity']
function getInitialTab() {
try {
const sp = new URLSearchParams(window.location.search)
const t = sp.get('tab')
return VALID_TABS.includes(t) ? t : 'artworks'
} catch {
return 'artworks'
function getInitialTab(initialTab = 'posts') {
if (typeof window === 'undefined') {
return VALID_TABS.includes(initialTab) ? initialTab : 'posts'
}
try {
const pathname = window.location.pathname.replace(/\/+$/, '')
const segments = pathname.split('/').filter(Boolean)
const lastSegment = segments.at(-1)
if (VALID_TABS.includes(lastSegment)) {
return lastSegment
}
} catch {
return VALID_TABS.includes(initialTab) ? initialTab : 'posts'
}
return VALID_TABS.includes(initialTab) ? initialTab : 'posts'
}
/**
@@ -52,34 +61,37 @@ export default function ProfileShow() {
countryName,
isOwner,
auth,
initialTab,
profileUrl,
galleryUrl,
profileTabUrls,
} = props
const [activeTab, setActiveTab] = useState(getInitialTab)
const [activeTab, setActiveTab] = useState(() => getInitialTab(initialTab))
const handleTabChange = useCallback((tab) => {
if (!VALID_TABS.includes(tab)) return
setActiveTab(tab)
// Update URL query param without full navigation
try {
const url = new URL(window.location.href)
if (tab === 'artworks') {
url.searchParams.delete('tab')
} else {
url.searchParams.set('tab', tab)
}
window.history.pushState({}, '', url.toString())
} catch (_) {}
}, [])
const currentUrl = new URL(window.location.href)
const targetBase = profileTabUrls?.[tab] || `${profileUrl || `${window.location.origin}`}/${tab}`
const nextUrl = new URL(targetBase, window.location.origin)
const sharedPostId = currentUrl.searchParams.get('post')
if (sharedPostId) {
nextUrl.searchParams.set('post', sharedPostId)
}
window.history.pushState({}, '', nextUrl.toString())
} catch (_) {}
}, [profileTabUrls, profileUrl])
// Handle browser back/forward
useEffect(() => {
const onPop = () => setActiveTab(getInitialTab())
const onPop = () => setActiveTab(getInitialTab(initialTab))
window.addEventListener('popstate', onPop)
return () => window.removeEventListener('popstate', onPop)
}, [])
}, [initialTab])
const isLoggedIn = !!(auth?.user)
@@ -98,9 +110,27 @@ export default function ProfileShow() {
? socialLinks.reduce((acc, l) => { acc[l.platform] = l; return acc }, {})
: (socialLinks ?? {})
const contentShellClassName = activeTab === 'artworks'
? 'w-full px-4 md:px-6'
: activeTab === 'posts'
? 'mx-auto max-w-7xl px-4 md:px-6'
: 'max-w-6xl mx-auto px-4'
return (
<div className="min-h-screen pb-16">
{/* Hero section */}
<div className="relative min-h-screen overflow-hidden pb-16">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-90"
style={{
background: 'radial-gradient(circle at top left, rgba(56,189,248,0.18), transparent 32%), radial-gradient(circle at 82% 10%, rgba(249,115,22,0.16), transparent 28%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #0a1220 100%)',
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 -z-10 opacity-[0.06]"
style={{ backgroundImage: 'url(/gfx/noise.png)', backgroundSize: '180px' }}
/>
<ProfileHero
user={user}
profile={profile}
@@ -121,26 +151,20 @@ export default function ProfileShow() {
) : null}
/>
{/* Stats pills row */}
<ProfileStatsRow
stats={stats}
followerCount={followerCount}
onTabChange={handleTabChange}
/>
<div className="mt-6">
<ProfileTabs
activeTab={activeTab}
onTabChange={handleTabChange}
/>
</div>
{/* Sticky tabs */}
<ProfileTabs
activeTab={activeTab}
onTabChange={handleTabChange}
/>
{/* Tab content area */}
<div className={activeTab === 'artworks' ? 'w-full px-4 md:px-6' : 'max-w-6xl mx-auto px-4'}>
<div className={`${contentShellClassName} pt-6`}>
{activeTab === 'artworks' && (
<TabArtworks
artworks={{ data: artworkList, next_cursor: artworkNextCursor }}
featuredArtworks={featuredArtworks}
username={user.username || user.name}
galleryUrl={galleryUrl}
isActive
/>
)}
@@ -156,6 +180,7 @@ export default function ProfileShow() {
recentFollowers={recentFollowers}
socialLinks={socialLinksObj}
countryName={countryName}
profileUrl={profileUrl}
onTabChange={handleTabChange}
/>
)}
@@ -175,9 +200,16 @@ export default function ProfileShow() {
<TabAbout
user={user}
profile={profile}
stats={stats}
achievements={achievements}
artworks={artworkList}
creatorStories={creatorStories}
profileComments={profileComments}
socialLinks={socialLinksObj}
countryName={countryName}
followerCount={followerCount}
recentFollowers={recentFollowers}
leaderboardRank={leaderboardRank}
/>
)}
{activeTab === 'stats' && (