- {/* Hero — above-fold, eager */}
-
+function GuestHomePage(props) {
+ const { hero, trending, fresh, tags, creators, news } = props
- {/* Below-fold sections — lazy */}
+ return (
+ <>
+ {/* 1. Hero */}
+
}>
+ {/* 3. Fresh Uploads */}
}>
+ {/* 4. Explore Categories */}
+
}>
+
+
+
+ {/* 5. Popular Tags */}
}>
+ {/* 6. Top Creators */}
}>
+ {/* 7. News */}
}>
+
+ {/* 8. CTA Upload */}
+
}>
+
+
+ >
+ )
+}
+
+function AuthHomePage(props) {
+ const {
+ user_data,
+ hero,
+ from_following,
+ trending,
+ fresh,
+ by_tags,
+ by_categories,
+ suggested_creators,
+ tags,
+ creators,
+ news,
+ preferences,
+ } = props
+
+ return (
+ <>
+ {/* P0. Welcome/status row */}
+
+
+
+
+ {/* 1. Hero */}
+
+
+ {/* P2. From Creators You Follow */}
+
}>
+
+
+
+ {/* P3. Trending For You (by_tags = Meilisearch tag overlap sorted by trending) */}
+
}>
+
+
+
+ {/* 2. Global Trending Now */}
+
}>
+
+
+
+ {/* P4. Because You Like {top tag} — uses by_categories for variety */}
+
}>
+
+
+
+ {/* 3. Fresh Uploads */}
+
}>
+
+
+
+ {/* 4. Explore Categories */}
+
}>
+
+
+
+ {/* P5. Suggested Creators */}
+
}>
+
+
+
+ {/* 5. Popular Tags */}
+
}>
+
+
+
+ {/* 6. Top Creators */}
+
}>
+
+
+
+ {/* 7. News */}
+
}>
+
+
+
+ {/* 8. CTA Upload */}
+
}>
+
+
+ >
+ )
+}
+
+function HomePage(props) {
+ return (
+
+ {props.is_logged_in
+ ?
+ :
+ }
)
}
diff --git a/resources/js/Pages/Home/HomeSuggestedCreators.jsx b/resources/js/Pages/Home/HomeSuggestedCreators.jsx
new file mode 100644
index 00000000..3ee77c92
--- /dev/null
+++ b/resources/js/Pages/Home/HomeSuggestedCreators.jsx
@@ -0,0 +1,67 @@
+import React from 'react'
+
+const AVATAR_FALLBACK = 'https://files.skinbase.org/avatars/default.webp'
+
+function CreatorCard({ creator }) {
+ return (
+
+
+
{ e.currentTarget.src = AVATAR_FALLBACK }}
+ />
+
+
+
+
+ {creator.name}
+
+ {creator.username && (
+
@{creator.username}
+ )}
+
+ {creator.followers_count > 0 && (
+ {creator.followers_count.toLocaleString()} followers
+ )}
+ {creator.artworks_count > 0 && (
+ {creator.artworks_count.toLocaleString()} artworks
+ )}
+
+
+
+
+ View Profile
+
+
+ )
+}
+
+export default function HomeSuggestedCreators({ creators }) {
+ if (!Array.isArray(creators) || creators.length === 0) return null
+
+ return (
+
+
+
+
💡 Suggested Creators
+
Creators you might enjoy following
+
+
+ Explore all →
+
+
+
+
+ {creators.map((creator) => (
+
+ ))}
+
+
+ )
+}
diff --git a/resources/js/Pages/Home/HomeTrending.jsx b/resources/js/Pages/Home/HomeTrending.jsx
index 90ca2d89..b1042b21 100644
--- a/resources/js/Pages/Home/HomeTrending.jsx
+++ b/resources/js/Pages/Home/HomeTrending.jsx
@@ -60,13 +60,13 @@ export default function HomeTrending({ items }) {
🔥 Trending This Week
-
+
See all →
-
- {items.map((item) => (
+
+ {items.slice(0, Math.floor(items.length / 5) * 5 || items.length).map((item) => (
))}
diff --git a/resources/js/Pages/Home/HomeTrendingForYou.jsx b/resources/js/Pages/Home/HomeTrendingForYou.jsx
new file mode 100644
index 00000000..f072d7ed
--- /dev/null
+++ b/resources/js/Pages/Home/HomeTrendingForYou.jsx
@@ -0,0 +1,70 @@
+import React from 'react'
+
+const FALLBACK = 'https://files.skinbase.org/default/missing_md.webp'
+const AVATAR_FALLBACK = 'https://files.skinbase.org/avatars/default.webp'
+
+function ArtCard({ item }) {
+ const username = item.author_username ? `@${item.author_username}` : null
+ return (
+
+
+
+
+

{ e.currentTarget.src = FALLBACK }}
+ />
+
+
{item.title}
+
+

{ e.currentTarget.src = AVATAR_FALLBACK }}
+ />
+
{item.author}
+ {username &&
{username}}
+
+
+
+ {item.title} by {item.author}
+
+
+ )
+}
+
+/**
+ * Personalized trending: artworks matching user's top tags, sorted by trending score.
+ * Label and browse link adapt to the user's first top tag.
+ */
+export default function HomeTrendingForYou({ items, preferences }) {
+ if (!Array.isArray(items) || items.length === 0) return null
+
+ const topTag = preferences?.top_tags?.[0]
+ const heading = topTag ? `🎯 Trending in #${topTag}` : '🎯 Trending For You'
+ const link = topTag ? `/browse?tags=${encodeURIComponent(topTag)}&sort=trending` : '/discover/trending'
+
+ return (
+
+
+
+ {items.slice(0, Math.floor(items.length / 5) * 5 || items.length).map((item) => (
+
+ ))}
+
+
+ )
+}
diff --git a/resources/js/Pages/Home/HomeWelcomeRow.jsx b/resources/js/Pages/Home/HomeWelcomeRow.jsx
new file mode 100644
index 00000000..adc9cc18
--- /dev/null
+++ b/resources/js/Pages/Home/HomeWelcomeRow.jsx
@@ -0,0 +1,74 @@
+import React from 'react'
+
+const AVATAR_FALLBACK = 'https://files.skinbase.org/avatars/default.webp'
+
+export default function HomeWelcomeRow({ user_data }) {
+ if (!user_data) return null
+
+ const { name, avatar, messages_unread, notifications_unread, url } = user_data
+
+ const firstName = name?.split(' ')[0] || name || 'there'
+
+ return (
+
+
+
+
+ {/* Left: greeting */}
+
+
+ {/* Right: action badges */}
+
+
+
+
+ )
+}