feat: Nova homepage, profile redesign, and legacy view system overhaul
Homepage
- Add HomepageService with hero, trending (award-weighted), fresh uploads,
popular tags, creator spotlight (weekly uploads ranking), and news sections
- Add React components: HomePage, HomeHero, HomeTrending, HomeFresh,
HomeTags, HomeCreators, HomeNews (lazy-loaded below the fold)
- Wire home.blade.php with JSON props, SEO meta, JSON-LD, and hero preload
- Add HomePage.jsx to vite.config.js inputs
Profile page
- Hero banner with random user artwork as background + dark gradient overlay
- Favourites section uses real Artwork models + <x-artwork-card> for CDN URLs
- Newest artworks grid: gallery-grid → grid grid-cols-2 gap-4
Edit Profile page (user.blade.php)
- Add hero banner (featured wallpaper/photography via artwork_features,
content_type_id IN [2,3]) sourced in UserController
- Remove bg-deep from outer wrapper; card backgrounds: bg-panel → bg-nova-800
- Remove stray AI-generated tag fragment from template
Author profile links
- Fix all /@username routes in: HomepageService, MonthlyCommentatorsController,
LatestCommentsController, MyBuddiesController and corresponding blade views
Legacy view namespace
- Register View::addNamespace('legacy', resource_path('views/_legacy'))
in AppServiceProvider::boot()
- Convert all view('legacy.x') and @include('legacy.x') calls to legacy::x
- Migrate legacy views to resources/views/_legacy/ with namespace support
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import SearchBar from '../Search/SearchBar'
|
||||
|
||||
export default function Topbar() {
|
||||
const DEFAULT_AVATAR = 'https://files.skinbase.org/avatars/default.webp'
|
||||
|
||||
export default function Topbar({ user = null }) {
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<header className="fixed top-0 left-0 right-0 h-16 bg-neutral-900 border-b border-neutral-800 z-50">
|
||||
<div className="h-full px-5 flex items-center justify-between gap-4">
|
||||
@@ -16,11 +20,53 @@ export default function Topbar() {
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 sm:gap-5">
|
||||
<a href="/forum" className="hidden sm:inline text-sm hover:text-sky-400">Forum</a>
|
||||
<button aria-label="User menu" className="text-neutral-200 hover:text-sky-400">
|
||||
<i className="fas fa-user" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div className="flex items-center gap-3 sm:gap-4">
|
||||
<a href="/forum" className="hidden sm:inline text-sm text-neutral-300 hover:text-sky-400 transition-colors">Forum</a>
|
||||
|
||||
{user ? (
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setMenuOpen(o => !o)}
|
||||
className="flex items-center gap-2 rounded-lg px-2 py-1 hover:bg-white/5 transition-colors"
|
||||
aria-label="User menu"
|
||||
>
|
||||
<img
|
||||
src={user.avatarUrl || DEFAULT_AVATAR}
|
||||
alt={user.displayName}
|
||||
className="w-7 h-7 rounded-full object-cover ring-1 ring-white/10"
|
||||
onError={(e) => { e.currentTarget.src = DEFAULT_AVATAR }}
|
||||
/>
|
||||
<span className="hidden sm:inline text-sm text-white/90">{user.displayName}</span>
|
||||
<i className="fas fa-chevron-down text-xs text-white/50" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
{menuOpen && (
|
||||
<div className="absolute right-0 mt-2 w-48 rounded-lg bg-neutral-800 border border-neutral-700 shadow-xl overflow-hidden z-50">
|
||||
<a href={`/@${user.username}`} className="flex items-center gap-2 px-4 py-2 text-sm hover:bg-white/5">
|
||||
<img
|
||||
src={user.avatarUrl || DEFAULT_AVATAR}
|
||||
alt={user.displayName}
|
||||
className="w-6 h-6 rounded-full object-cover"
|
||||
onError={(e) => { e.currentTarget.src = DEFAULT_AVATAR }}
|
||||
/>
|
||||
<span className="truncate">{user.displayName}</span>
|
||||
</a>
|
||||
<div className="border-t border-neutral-700" />
|
||||
<a href={user.uploadUrl} className="block px-4 py-2 text-sm hover:bg-white/5">Upload</a>
|
||||
<a href="/dashboard" className="block px-4 py-2 text-sm hover:bg-white/5">Dashboard</a>
|
||||
<div className="border-t border-neutral-700" />
|
||||
<a href="/logout" className="block px-4 py-2 text-sm text-red-400 hover:bg-white/5"
|
||||
onClick={(e) => { e.preventDefault(); document.getElementById('logout-form')?.submit() }}>
|
||||
Sign out
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<a href="/login" className="text-sm text-neutral-300 hover:text-sky-400 transition-colors">
|
||||
<i className="fas fa-user" aria-hidden="true"></i>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user