Files
SkinbaseNova/resources/views/_legacy/profile.blade.php
Gregor Klevze d0aefc5ddc 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
2026-02-26 10:25:35 +01:00

693 lines
35 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('layouts.nova')
@php
$uname = $user->username ?? $user->name ?? 'Unknown';
$displayName = $user->name ?? $uname;
$avatarUrl = \App\Support\AvatarUrl::forUser((int) $user->id, $user->profile?->avatar_hash, 128);
$genderMap = [
'M' => ['label' => 'Male', 'icon' => 'fa-mars', 'color' => 'text-blue-400'],
'F' => ['label' => 'Female', 'icon' => 'fa-venus', 'color' => 'text-pink-400'],
'X' => ['label' => 'N/A', 'icon' => 'fa-question', 'color' => 'text-gray-400'],
];
$genderCode = strtoupper((string) ($profile?->gender ?? 'X'));
$gender = $genderMap[$genderCode] ?? $genderMap['X'];
$birthdate = null;
if ($profile?->birthdate) {
try {
$bd = \Carbon\Carbon::parse($profile->birthdate);
if ($bd->year > 1900) {
$birthdate = $bd->format('F d, Y');
}
} catch (\Throwable) {}
}
$website = $profile?->website ?? null;
if ($website && !preg_match('#^https?://#i', $website)) {
$website = 'https://' . $website;
}
$about = $profile?->about ?? null;
$lastVisit = null;
if ($user->last_visit_at) {
try { $lastVisit = \Carbon\Carbon::parse($user->last_visit_at); } catch (\Throwable) {}
}
$socialIcons = [
'twitter' => ['icon' => 'fa-brands fa-x-twitter', 'label' => 'X / Twitter'],
'deviantart' => ['icon' => 'fa-brands fa-deviantart', 'label' => 'DeviantArt'],
'instagram' => ['icon' => 'fa-brands fa-instagram', 'label' => 'Instagram'],
'behance' => ['icon' => 'fa-brands fa-behance', 'label' => 'Behance'],
'artstation' => ['icon' => 'fa-solid fa-palette', 'label' => 'ArtStation'],
'youtube' => ['icon' => 'fa-brands fa-youtube', 'label' => 'YouTube'],
'website' => ['icon' => 'fa-solid fa-link', 'label' => 'Website'],
];
$seoPage = max(1, (int) request()->query('page', 1));
$seoBase = url()->current();
$seoQ = request()->query(); unset($seoQ['page']);
$seoUrl = fn(int $p) => $seoBase . ($p > 1
? '?' . http_build_query(array_merge($seoQ, ['page' => $p]))
: (count($seoQ) ? '?' . http_build_query($seoQ) : ''));
$seoPrev = $seoPage > 1 ? $seoUrl($seoPage - 1) : null;
$seoNext = (isset($artworks) && method_exists($artworks, 'nextPageUrl')) ? $artworks->nextPageUrl() : null;
@endphp
@push('head')
<link rel="canonical" href="{{ $seoUrl($seoPage) }}">
@if($seoPrev)<link rel="prev" href="{{ $seoPrev }}">@endif
@if($seoNext)<link rel="next" href="{{ $seoNext }}">@endif
<meta name="robots" content="index,follow">
<meta property="og:title" content="Profile: {{ e($uname) }} Skinbase.org">
<meta property="og:image" content="{{ $avatarUrl }}">
<meta property="og:url" content="{{ $page_canonical ?? url()->current() }}">
<style>
.profile-hero-bg {
position: relative;
overflow: hidden;
}
.profile-hero-bg::after {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 20% 50%, rgba(77,163,255,.12), transparent 60%),
radial-gradient(ellipse at 80% 20%, rgba(224,122,33,.08), transparent 50%);
pointer-events: none;
z-index: 1;
}
.nova-panel {
background: var(--panel-dark);
border: 1px solid var(--sb-line);
border-radius: 0.75rem;
overflow: hidden;
}
.nova-panel-header {
padding: 0.65rem 1rem;
border-bottom: 1px solid var(--sb-line);
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
font-size: 0.82rem;
color: var(--sb-text);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.nova-panel-body { padding: 1rem; }
.stat-item { text-align: center; padding: 0.5rem 0.75rem; }
.stat-item .stat-value {
font-size: 1.15rem;
font-weight: 700;
color: #fff;
line-height: 1.2;
}
.stat-item .stat-label {
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--sb-muted);
margin-top: 1px;
}
.profile-table td:first-child {
color: var(--sb-muted);
font-size: 0.75rem;
white-space: nowrap;
padding-right: 0.75rem;
padding-top: 0.45rem;
padding-bottom: 0.45rem;
}
.profile-table td:last-child {
color: var(--sb-text);
font-size: 0.82rem;
text-align: right;
}
.profile-table tr {
border-bottom: 1px solid rgba(42,42,51,0.5);
}
.profile-table tr:last-child { border-bottom: none; }
.profile-table td { vertical-align: middle; }
.comment-avatar {
width: 38px; height: 38px;
border-radius: 50%; object-fit: cover; flex-shrink: 0;
}
.follower-avatar {
width: 34px; height: 34px;
border-radius: 50%; object-fit: cover;
border: 1px solid var(--sb-line);
transition: opacity 0.2s;
}
.follower-avatar:hover { opacity: 0.85; }
.fav-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
gap: 4px;
}
.fav-grid a img {
width: 100%; aspect-ratio: 1; object-fit: cover;
border-radius: 4px; transition: opacity 0.2s;
display: block;
}
.fav-grid a:hover img { opacity: 0.82; }
.follow-btn { transition: all 0.2s ease; }
</style>
@endpush
@section('content')
{{-- ═══════════════════════════════════════════════════════════
PROFILE HERO
═══════════════════════════════════════════════════════════ --}}
<div class="profile-hero-bg border-b border-[--sb-line]"
@if(!empty($heroBgUrl))
style="background: url('{{ $heroBgUrl }}') center/cover no-repeat;"
@else
style="background: linear-gradient(135deg, rgba(15,23,36,1) 0%, rgba(21,30,46,1) 50%, rgba(9,16,26,1) 100%);"
@endif
>
{{-- Dark overlay so the content stays readable --}}
@if(!empty($heroBgUrl))
<div class="absolute inset-0 bg-gradient-to-r from-[#0f1724]/95 via-[#0f1724]/80 to-[#0f1724]/60"></div>
@endif
<div class="relative z-10 max-w-screen-xl mx-auto px-4 py-8">
<div class="flex flex-col sm:flex-row items-center sm:items-end gap-5">
{{-- Avatar --}}
<div class="shrink-0">
<img src="{{ $avatarUrl }}"
alt="{{ e($uname) }}"
class="w-24 h-24 sm:w-32 sm:h-32 rounded-full object-cover border-4 border-[--sb-line] shadow-lg">
</div>
{{-- Name + meta --}}
<div class="flex-1 text-center sm:text-left min-w-0">
<h1 class="text-2xl sm:text-3xl font-bold text-white leading-tight truncate">
{{ e($uname) }}
</h1>
@if($displayName && $displayName !== $uname)
<p class="text-[--sb-muted] text-sm mt-0.5">{{ e($displayName) }}</p>
@endif
@if($countryName)
<p class="text-[--sb-muted] text-sm mt-1 flex items-center justify-center sm:justify-start gap-1.5">
@if($profile?->country_code)
<img src="/gfx/flags/shiny/24/{{ rawurlencode($profile->country_code) }}.png"
alt="{{ e($countryName) }}"
class="w-5 h-auto rounded-sm inline-block"
onerror="this.style.display='none'">
@endif
{{ e($countryName) }}
</p>
@endif
@if($lastVisit)
<p class="text-[--sb-muted] text-xs mt-1">
<i class="fa-solid fa-clock fa-fw mr-1"></i>
Last seen {{ $lastVisit->diffForHumans() }}
</p>
@endif
</div>
{{-- Action buttons --}}
<div class="shrink-0 flex flex-col gap-2 items-center sm:items-end">
@if(!$isOwner)
@auth
<div x-data="{
following: {{ $viewerIsFollowing ? 'true' : 'false' }},
count: {{ (int) $followerCount }},
loading: false,
async toggle() {
this.loading = true;
try {
const r = await fetch('{{ route('profile.follow', ['username' => strtolower((string)$uname)]) }}', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content,
'Accept': 'application/json'
}
});
const d = await r.json();
if (r.ok) { this.following = d.following; this.count = d.follower_count; }
} catch(e) {}
this.loading = false;
}
}">
<button @click="toggle" :disabled="loading" class="follow-btn inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium border transition-all"
:class="following
? 'bg-green-500/10 border-green-500/40 text-green-400 hover:bg-red-500/10 hover:border-red-500/40 hover:text-red-400'
: 'bg-[--sb-blue]/10 border-[--sb-blue]/40 text-[--sb-blue] hover:bg-[--sb-blue]/20'">
<i class="fa-solid fa-fw"
:class="loading ? 'fa-circle-notch fa-spin' : (following ? 'fa-user-check' : 'fa-user-plus')"></i>
<span x-text="following ? 'Following' : 'Follow'"></span>
<span class="text-xs opacity-60" x-text="'(' + count + ')'"></span>
</button>
</div>
@else
<a href="{{ route('login') }}"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium border border-[--sb-blue]/40 text-[--sb-blue] hover:bg-[--sb-blue]/10 transition-all">
<i class="fa-solid fa-user-plus fa-fw"></i> Follow
<span class="text-xs opacity-60">({{ $followerCount }})</span>
</a>
@endauth
@else
<a href="{{ route('dashboard.profile') }}"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium border border-[--sb-line] text-[--sb-text] hover:bg-white/5 transition-all">
<i class="fa-solid fa-pen fa-fw"></i> Edit Profile
</a>
@endif
</div>
</div>
</div>
</div>
{{-- ═══════════════════════════════════════════════════════════
STATS STRIP
═══════════════════════════════════════════════════════════ --}}
<div class="bg-[--sb-panel2] border-b border-[--sb-line]">
<div class="max-w-screen-xl mx-auto px-4">
<div class="flex divide-x divide-[--sb-line] overflow-x-auto">
@foreach([
['value' => number_format($stats->uploads ?? 0), 'label' => 'Uploads', 'icon' => 'fa-cloud-arrow-up'],
['value' => number_format($stats->downloads ?? 0), 'label' => 'Downloads', 'icon' => 'fa-download'],
['value' => number_format($stats->profile_views ?? 0), 'label' => 'Profile Views', 'icon' => 'fa-eye'],
['value' => number_format($followerCount), 'label' => 'Followers', 'icon' => 'fa-users'],
['value' => number_format($stats->awards ?? 0), 'label' => 'Awards', 'icon' => 'fa-trophy'],
] as $si)
<div class="stat-item flex-1 py-3">
<div class="stat-value">{{ $si['value'] }}</div>
<div class="stat-label">
<i class="fa-solid {{ $si['icon'] }} fa-fw mr-0.5"></i>{{ $si['label'] }}
</div>
</div>
@endforeach
</div>
</div>
</div>
{{-- ═══════════════════════════════════════════════════════════
MAIN CONTENT
═══════════════════════════════════════════════════════════ --}}
<div class="max-w-screen-xl mx-auto px-4 py-6">
<div class="flex flex-col lg:flex-row gap-5">
{{-- ─── LEFT COLUMN (artworks) ─── --}}
<div class="flex-1 min-w-0 space-y-5">
{{-- Featured Artworks --}}
@if(isset($featuredArtworks) && $featuredArtworks->isNotEmpty())
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-star text-yellow-400 fa-fw"></i>
Featured Artworks
</div>
<div class="nova-panel-body">
<div class="flex flex-col md:flex-row gap-4">
@php $feat = $featuredArtworks->first() @endphp
{{-- Main featured --}}
<a href="/art/{{ $feat->id }}/{{ \Illuminate\Support\Str::slug($feat->name) }}"
class="flex-1 group block min-w-0">
<div class="overflow-hidden rounded-lg bg-black">
<img src="{{ $feat->thumb }}"
alt="{{ e($feat->name) }}"
class="w-full object-cover transition-transform duration-300 group-hover:scale-105"
style="aspect-ratio:4/3;">
</div>
<h4 class="mt-2 text-sm font-medium text-white truncate">{{ e($feat->name) }}</h4>
@if($feat->label)
<p class="text-xs text-[--sb-muted]">{{ e($feat->label) }}</p>
@endif
@if($feat->featured_at)
<p class="text-xs text-[--sb-muted] mt-0.5">
<i class="fa-solid fa-calendar fa-fw"></i>
Featured {{ \Carbon\Carbon::parse($feat->featured_at)->format('d M, Y') }}
</p>
@endif
</a>
{{-- Side featured (2nd & 3rd) --}}
@if($featuredArtworks->count() > 1)
<div class="md:w-44 space-y-2">
@foreach($featuredArtworks->slice(1) as $sideArt)
<a href="/art/{{ $sideArt->id }}/{{ \Illuminate\Support\Str::slug($sideArt->name) }}"
class="block group">
<div class="overflow-hidden rounded-md bg-black">
<img src="{{ $sideArt->thumb }}"
alt="{{ e($sideArt->name) }}"
class="w-full object-cover transition-transform duration-300 group-hover:scale-105"
style="aspect-ratio:16/9;">
</div>
<p class="text-xs text-[--sb-muted] mt-1 truncate">{{ e($sideArt->name) }}</p>
</a>
@endforeach
</div>
@endif
</div>
</div>
</div>
@endif
{{-- Newest Artworks --}}
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-images fa-fw text-[--sb-blue]"></i>
Newest Artworks
<a href="/gallery/{{ $user->id }}/{{ \Illuminate\Support\Str::slug($uname) }}"
class="ml-auto text-xs text-[--sb-blue] hover:underline normal-case tracking-normal font-normal">
View Gallery <i class="fa-solid fa-arrow-right fa-fw"></i>
</a>
</div>
<div class="nova-panel-body">
@if(isset($artworks) && !$artworks->isEmpty())
<div class="gallery-grid grid grid-cols-2 gap-4"
data-nova-gallery
data-gallery-type="profile"
data-gallery-grid
data-profile-id="{{ $user->id }}">
@foreach($artworks as $art)
<x-artwork-card :art="$art" />
@endforeach
</div>
<div class="hidden" data-gallery-skeleton-template aria-hidden="true">
<x-skeleton.artwork-card />
</div>
<div class="hidden mt-6" data-gallery-skeleton></div>
@else
<p class="text-[--sb-muted] text-sm text-center py-8">
<i class="fa-solid fa-image fa-2x mb-3 block opacity-20"></i>
No artworks yet.
</p>
@endif
</div>
</div>
{{-- Favourites --}}
@if(isset($favourites) && $favourites->isNotEmpty())
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-heart fa-fw text-pink-400"></i>
Favourites
</div>
<div class="nova-panel-body">
<div class="grid grid-cols-2 gap-4">
@foreach($favourites as $fav)
<x-artwork-card :art="$fav" />
@endforeach
</div>
</div>
</div>
@endif
</div>{{-- end left --}}
{{-- ─── RIGHT SIDEBAR ─── --}}
<div class="lg:w-80 xl:w-96 shrink-0 space-y-4">
{{-- Profile Info --}}
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-id-card fa-fw text-[--sb-blue]"></i>
Profile
</div>
<div class="nova-panel-body">
<table class="profile-table w-full">
<tr>
<td>Username</td>
<td>{{ e($uname) }}</td>
</tr>
@if($displayName && $displayName !== $uname)
<tr><td>Real Name</td><td>{{ e($displayName) }}</td></tr>
@endif
<tr>
<td>Gender</td>
<td>
<i class="fa-solid {{ $gender['icon'] }} fa-fw {{ $gender['color'] }}"></i>
{{ $gender['label'] }}
</td>
</tr>
@if($birthdate)
<tr><td>Birthday</td><td>{{ $birthdate }}</td></tr>
@endif
@if($countryName)
<tr>
<td>Country</td>
<td class="flex items-center justify-end gap-1.5">
@if($profile?->country_code)
<img src="/gfx/flags/shiny/24/{{ rawurlencode($profile->country_code) }}.png"
alt="{{ e($countryName) }}"
class="w-4 h-auto rounded-sm"
onerror="this.style.display='none'">
@endif
{{ e($countryName) }}
</td>
</tr>
@endif
@if($website)
<tr>
<td>Website</td>
<td>
<a href="{{ e($website) }}" rel="nofollow noopener" target="_blank"
class="text-[--sb-blue] hover:underline text-xs inline-flex items-center gap-1">
<i class="fa-solid fa-link fa-fw"></i>
{{ e(parse_url($website, PHP_URL_HOST) ?? $website) }}
</a>
</td>
</tr>
@endif
@if($lastVisit)
<tr>
<td>Last Activity</td>
<td class="text-[11px]">
{{ $lastVisit->format('d.M.Y') }}
<i class="fa-solid fa-clock fa-fw ml-1 opacity-60"></i>
{{ $lastVisit->format('H:i') }}
</td>
</tr>
@endif
<tr>
<td>Member since</td>
<td>{{ $user->created_at ? $user->created_at->format('M Y') : 'N/A' }}</td>
</tr>
</table>
</div>
</div>
{{-- About Me --}}
@if($about)
<div class="nova-panel" x-data="{ expanded: false }">
<div class="nova-panel-header">
<i class="fa-solid fa-quote-left fa-fw text-purple-400"></i>
About Me
</div>
<div class="nova-panel-body">
<div class="text-sm text-[--sb-text] leading-relaxed"
:class="expanded ? '' : 'line-clamp-6'">
{!! nl2br(e($about)) !!}
</div>
@if(strlen($about) > 300)
<button @click="expanded = !expanded"
class="mt-2 text-xs text-[--sb-blue] hover:underline">
<span x-text="expanded ? '↑ Show less' : '↓ Read more'"></span>
</button>
@endif
</div>
</div>
@endif
{{-- Statistics --}}
@if($stats)
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-chart-bar fa-fw text-green-400"></i>
Statistics
</div>
<div class="nova-panel-body p-0">
<table class="profile-table w-full">
@foreach([
['Profile Views', number_format($stats->profile_views ?? 0), null],
['Uploads', number_format($stats->uploads ?? 0), null],
['Downloads', number_format($stats->downloads ?? 0), null],
['Page Views', number_format($stats->pageviews ?? 0), null],
['Featured Works',number_format($stats->awards ?? 0), 'fa-star text-yellow-400'],
] as [$label, $value, $iconClass])
<tr>
<td class="pl-4">{{ $label }}</td>
<td class="pr-4">
{{ $value }}
@if($iconClass)<i class="fa-solid {{ $iconClass }} text-xs ml-1"></i>@endif
</td>
</tr>
@endforeach
</table>
</div>
</div>
@endif
{{-- Social Links --}}
@if(isset($socialLinks) && $socialLinks->isNotEmpty())
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-share-nodes fa-fw text-[--sb-blue]"></i>
Social Links
</div>
<div class="nova-panel-body flex flex-wrap gap-2">
@foreach($socialLinks as $platform => $link)
@php
$si = $socialIcons[$platform] ?? ['icon' => 'fa-solid fa-link', 'label' => ucfirst($platform)];
$href = str_starts_with($link->url, 'http') ? $link->url : ('https://' . $link->url);
@endphp
<a href="{{ e($href) }}" rel="nofollow noopener" target="_blank"
class="inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs border border-[--sb-line] text-[--sb-text] hover:bg-white/5 hover:border-[--sb-blue]/40 transition-all">
<i class="{{ $si['icon'] }} fa-fw"></i>
{{ $si['label'] }}
</a>
@endforeach
</div>
</div>
@endif
{{-- Recent Followers --}}
@if(isset($recentFollowers) && $recentFollowers->isNotEmpty())
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-users fa-fw text-[--sb-blue]"></i>
Followers
<span class="ml-1 px-1.5 py-0.5 rounded text-xs bg-white/5 text-[--sb-muted]">
{{ number_format($followerCount) }}
</span>
<a href="/following/{{ $user->id }}/{{ \Illuminate\Support\Str::slug($uname) }}"
class="ml-auto text-xs text-[--sb-blue] hover:underline normal-case tracking-normal font-normal">
All
</a>
</div>
<div class="nova-panel-body">
<div class="flex flex-wrap gap-1.5">
@foreach($recentFollowers as $follower)
<a href="{{ $follower->profile_url }}" title="{{ e($follower->uname) }}">
<img src="{{ $follower->avatar_url }}"
alt="{{ e($follower->uname) }}"
class="follower-avatar"
onerror="this.src='{{ \App\Support\AvatarUrl::default() }}'">
</a>
@endforeach
</div>
</div>
</div>
@elseif($followerCount > 0)
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-users fa-fw text-[--sb-blue]"></i>
Followers
<span class="ml-1 px-1.5 py-0.5 rounded text-xs bg-white/5 text-[--sb-muted]">
{{ number_format($followerCount) }}
</span>
</div>
</div>
@endif
{{-- Profile Comments --}}
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-comments fa-fw text-orange-400"></i>
Comments
@if(isset($profileComments) && $profileComments->isNotEmpty())
<span class="ml-1 px-1.5 py-0.5 rounded text-xs bg-white/5 text-[--sb-muted]">
{{ $profileComments->count() }}
</span>
@endif
</div>
<div class="nova-panel-body">
@if(!isset($profileComments) || $profileComments->isEmpty())
<p class="text-[--sb-muted] text-xs text-center py-3">No comments yet.</p>
@else
<div class="space-y-4">
@foreach($profileComments as $comment)
<div class="flex gap-3">
<a href="{{ $comment->author_profile_url }}" class="shrink-0">
<img src="{{ $comment->author_avatar }}"
alt="{{ e($comment->author_name) }}"
class="comment-avatar"
onerror="this.src='{{ \App\Support\AvatarUrl::default() }}'">
</a>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-1">
<a href="{{ $comment->author_profile_url }}"
class="text-xs font-semibold text-[--sb-text] hover:text-[--sb-blue] transition-colors">
{{ e($comment->author_name) }}
</a>
<span class="text-[--sb-muted] text-[10px] ml-auto whitespace-nowrap">
{{ \Carbon\Carbon::parse($comment->created_at)->diffForHumans() }}
</span>
</div>
<p class="text-xs text-[--sb-text] leading-relaxed break-words">
{!! nl2br(e($comment->body)) !!}
</p>
@if(!empty($comment->author_signature))
<p class="text-[--sb-muted] text-[10px] mt-1 italic border-t border-[--sb-line] pt-1 opacity-70">
{!! nl2br(e($comment->author_signature)) !!}
</p>
@endif
</div>
</div>
@endforeach
</div>
@endif
</div>
</div>
{{-- Write Comment --}}
@auth
@if(auth()->id() !== $user->id)
<div class="nova-panel">
<div class="nova-panel-header">
<i class="fa-solid fa-pen fa-fw text-[--sb-blue]"></i>
Write a Comment
</div>
<div class="nova-panel-body">
@if(session('status') === 'Comment posted!')
<div class="mb-3 px-3 py-2 rounded-lg bg-green-500/10 border border-green-500/30 text-green-400 text-xs">
<i class="fa-solid fa-check fa-fw"></i> Comment posted!
</div>
@endif
<form method="POST"
action="{{ route('profile.comment', ['username' => strtolower((string)$uname)]) }}">
@csrf
<textarea name="body" rows="4" required minlength="2" maxlength="2000"
placeholder="Write a comment for {{ e($uname) }}..."
class="w-full bg-[--sb-bg] border border-[--sb-line] rounded-lg px-3 py-2 text-sm text-[--sb-text] placeholder:text-[--sb-muted]/60 resize-none focus:outline-none focus:border-[--sb-blue]/50 transition-colors"
>{{ old('body') }}</textarea>
@error('body')
<p class="mt-1 text-xs text-red-400">{{ $message }}</p>
@enderror
<div class="mt-2 text-right">
<button type="submit"
class="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium bg-[--sb-blue]/90 hover:bg-[--sb-blue] text-white transition-colors">
<i class="fa-solid fa-paper-plane fa-fw"></i>
Post Comment
</button>
</div>
</form>
</div>
</div>
@endif
@else
<div class="nova-panel">
<div class="nova-panel-body text-center py-4">
<p class="text-[--sb-muted] text-sm">
<a href="{{ route('login') }}" class="text-[--sb-blue] hover:underline">Log in</a>
to leave a comment.
</p>
</div>
</div>
@endauth
</div>{{-- end right sidebar --}}
</div>{{-- end flex --}}
</div>{{-- end container --}}
@endsection
@push('scripts')
<script src="/js/legacy-gallery-init.js" defer></script>
@endpush