messages implemented

This commit is contained in:
2026-02-26 21:12:32 +01:00
parent d0aefc5ddc
commit 15b7b77d20
168 changed files with 14728 additions and 6786 deletions

View File

@@ -1,6 +1,16 @@
@extends('layouts.nova')
@section('content')
@php
$contentTypes = $contentTypes ?? collect([
(object) [
'name' => 'Categories',
'description' => null,
'roots' => $categories ?? collect(),
],
]);
$subgroups = $subgroups ?? collect();
@endphp
<div class="container-fluid legacy-page">
<div class="effect2 page-header-wrap">
<header class="page-heading">

View File

@@ -2,81 +2,23 @@
@section('content')
{{-- ── Hero header ── --}}
<div class="px-6 pt-10 pb-6 md:px-10">
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Community</p>
<h1 class="text-3xl font-bold text-white leading-tight">Latest Comments</h1>
<p class="mt-1 text-sm text-white/50">Most recent artwork comments from the community.</p>
{{-- Inline props for the React component --}}
<script id="latest-comments-props" type="application/json">
{!! json_encode($props, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP) !!}
</script>
<div id="latest-comments-root" class="min-h-screen">
{{-- SSR skeleton replaced on React hydration --}}
<div class="px-4 pt-10 pb-20 sm:px-6 lg:px-8 max-w-5xl mx-auto">
<div class="mb-8">
<p class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Community</p>
<h1 class="text-3xl font-bold text-white leading-tight">{{ $page_title }}</h1>
<p class="mt-1 text-sm text-white/50">Most recent artwork comments from the community.</p>
</div>
<div class="h-8 w-8 animate-spin rounded-full border-4 border-nova-500 border-t-transparent mx-auto mt-20"></div>
</div>
</div>
{{-- ── Comment cards grid ── --}}
<div class="px-6 pb-16 md:px-10">
@if ($comments->isNotEmpty())
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
@foreach ($comments as $comment)
@php
$artUrl = '/art/' . (int)($comment->id ?? 0) . '/' . ($comment->artwork_slug ?? 'artwork');
$userUrl = ($comment->commenter_username ?? null) ? '/@' . $comment->commenter_username : '/profile/' . (int)($comment->commenter_id ?? 0);
$avatarUrl = \App\Support\AvatarUrl::forUser((int)($comment->commenter_id ?? 0), $comment->icon ?? null, 40);
$ago = \Carbon\Carbon::parse($comment->datetime ?? now())->diffForHumans();
$snippet = \Illuminate\Support\Str::limit(strip_tags($comment->comment_description ?? ''), 160);
@endphp
<article class="flex flex-col rounded-xl border border-white/[0.06] bg-white/[0.03] hover:border-white/[0.1] hover:bg-white/[0.05] transition-all duration-200 overflow-hidden">
{{-- Artwork thumbnail --}}
@if (!empty($comment->thumb))
<a href="{{ $artUrl }}" class="block overflow-hidden bg-neutral-900 flex-shrink-0">
<img src="{{ $comment->thumb }}" alt="{{ $comment->name ?? 'Artwork' }}"
class="w-full h-36 object-cover transition-transform duration-300 hover:scale-[1.03]"
loading="lazy">
</a>
@endif
<div class="flex flex-col flex-1 p-4 gap-3">
{{-- Commenter row --}}
<div class="flex items-center gap-2.5">
<a href="{{ $userUrl }}" class="flex-shrink-0">
<img src="{{ $avatarUrl }}" alt="{{ $comment->uname ?? 'User' }}"
class="w-8 h-8 rounded-full object-cover ring-1 ring-white/[0.08]">
</a>
<div class="min-w-0 flex-1">
<a href="{{ $userUrl }}" class="block truncate text-xs font-semibold text-white/85 hover:text-white transition-colors">
{{ $comment->uname ?? 'Unknown' }}
</a>
<span class="text-[10px] text-white/35">{{ $ago }}</span>
</div>
</div>
{{-- Comment text --}}
<p class="flex-1 text-sm text-white/60 leading-relaxed line-clamp-4">{{ $snippet }}</p>
{{-- Artwork link footer --}}
@if (!empty($comment->name))
<a href="{{ $artUrl }}"
class="mt-auto inline-flex items-center gap-1.5 text-xs text-sky-400/70 hover:text-sky-300 transition-colors truncate">
<svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
<span class="truncate">{{ $comment->name }}</span>
</a>
@endif
</div>
</article>
@endforeach
</div>
<div class="mt-10 flex justify-center">
{{ $comments->withQueryString()->links() }}
</div>
@else
<div class="rounded-xl border border-white/[0.06] bg-white/[0.02] px-8 py-12 text-center">
<p class="text-white/40 text-sm">No comments found.</p>
</div>
@endif
</div>
@vite(['resources/js/Pages/Community/LatestCommentsPage.jsx'])
@endsection

View File

@@ -0,0 +1,88 @@
@extends('layouts.nova')
@section('content')
<div class="px-6 pt-10 pb-6 md:px-10">
<div class="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-4">
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Creators</p>
<h1 class="text-3xl font-bold text-white leading-tight flex items-center gap-3">
<i class="fa-solid fa-arrow-trend-up text-sky-400 text-2xl"></i>
Rising Creators
</h1>
<p class="mt-1 text-sm text-white/50">Creators gaining momentum with the most views over the last 90 days.</p>
</div>
<a href="{{ route('creators.top') }}"
class="inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium border border-white/[0.08] bg-white/[0.04] text-white/70 hover:bg-white/[0.08] hover:text-white transition-colors">
<i class="fa-solid fa-star text-xs"></i>
Top Creators
</a>
</div>
</div>
<div class="px-6 pb-16 md:px-10">
@php $offset = ($creators->currentPage() - 1) * $creators->perPage(); @endphp
@if ($creators->isNotEmpty())
<div class="rounded-xl border border-white/[0.06] overflow-hidden">
<div class="grid grid-cols-[3rem_1fr_auto] items-center gap-4 px-5 py-3 bg-white/[0.03] border-b border-white/[0.06]">
<span class="text-xs font-semibold uppercase tracking-widest text-white/30 text-center">#</span>
<span class="text-xs font-semibold uppercase tracking-widest text-white/30">Creator</span>
<span class="text-xs font-semibold uppercase tracking-widest text-white/30 text-right">Recent Views</span>
</div>
<div class="divide-y divide-white/[0.04]">
@foreach ($creators as $i => $creator)
@php
$rank = $offset + $i + 1;
$profileUrl = ($creator->username ?? null)
? '/@' . $creator->username
: '/profile/' . (int) $creator->user_id;
$avatarUrl = \App\Support\AvatarUrl::forUser((int) $creator->user_id, null, 40);
@endphp
<div class="grid grid-cols-[3rem_1fr_auto] items-center gap-4 px-5 py-4
{{ $rank <= 3 ? 'bg-white/[0.015]' : '' }} hover:bg-white/[0.03] transition-colors">
<div class="text-center">
@if ($rank === 1)
<span class="inline-flex items-center justify-center w-7 h-7 rounded-full bg-amber-400/15 text-amber-300 text-xs font-bold ring-1 ring-amber-400/30">1</span>
@elseif ($rank === 2)
<span class="inline-flex items-center justify-center w-7 h-7 rounded-full bg-slate-400/15 text-slate-300 text-xs font-bold ring-1 ring-slate-400/30">2</span>
@elseif ($rank === 3)
<span class="inline-flex items-center justify-center w-7 h-7 rounded-full bg-orange-700/20 text-orange-400 text-xs font-bold ring-1 ring-orange-600/30">3</span>
@else
<span class="text-sm text-white/30 font-medium">{{ $rank }}</span>
@endif
</div>
<a href="{{ $profileUrl }}" class="flex items-center gap-3 min-w-0 hover:opacity-90 transition-opacity">
<img src="{{ $avatarUrl }}" alt="{{ $creator->uname }}"
class="w-9 h-9 rounded-full object-cover ring-1 ring-white/10 shrink-0"
onerror="this.src='https://files.skinbase.org/avatars/default.webp'" />
<div class="min-w-0">
<p class="text-sm font-medium text-white truncate">{{ $creator->uname }}</p>
@if($creator->username ?? null)
<p class="text-xs text-white/40 truncate">@{{ $creator->username }}</p>
@endif
</div>
</a>
<div class="text-right">
<span class="text-sm font-semibold text-white/80">{{ number_format($creator->total) }}</span>
</div>
</div>
@endforeach
</div>
</div>
<div class="mt-10 flex justify-center">
{{ $creators->withQueryString()->links() }}
</div>
@else
<div class="rounded-xl border border-white/[0.06] bg-white/[0.02] px-8 py-12 text-center">
<p class="text-white/40 text-sm">No rising creators found yet. Check back soon!</p>
</div>
@endif
</div>
@endsection

View File

@@ -0,0 +1,73 @@
@extends('layouts.nova')
@section('content')
{{-- ── Hero header ── --}}
<div class="px-6 pt-10 pb-6 md:px-10">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Discover</p>
<h1 class="text-3xl font-bold text-white leading-tight flex items-center gap-3">
<i class="fa-solid {{ $icon ?? 'fa-compass' }} text-sky-400 text-2xl"></i>
{{ $page_title ?? 'Discover' }}
</h1>
@isset($description)
<p class="mt-1 text-sm text-white/50">{{ $description }}</p>
@endisset
</div>
{{-- Section switcher pills --}}
<div class="flex flex-wrap items-center gap-2 text-sm">
@php
$sections = [
'trending' => ['label' => 'Trending', 'icon' => 'fa-fire'],
'fresh' => ['label' => 'Fresh', 'icon' => 'fa-bolt'],
'top-rated' => ['label' => 'Top Rated', 'icon' => 'fa-medal'],
'most-downloaded' => ['label' => 'Most Downloaded', 'icon' => 'fa-download'],
'on-this-day' => ['label' => 'On This Day', 'icon' => 'fa-calendar-day'],
];
$active = $section ?? '';
@endphp
@foreach($sections as $slug => $meta)
<a href="{{ route('discover.' . $slug) }}"
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors
{{ $active === $slug ? 'bg-sky-600 text-white' : 'bg-white/[0.05] text-white/60 hover:bg-white/[0.1] hover:text-white' }}">
<i class="fa-solid {{ $meta['icon'] }} text-xs"></i>
{{ $meta['label'] }}
</a>
@endforeach
</div>
</div>
</div>
{{-- ── Artwork grid ── --}}
<div class="px-6 pb-16 md:px-10">
@if ($artworks && $artworks->isNotEmpty())
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 gap-4 md:gap-5">
@foreach ($artworks as $art)
@php
$card = (object)[
'id' => $art->id,
'name' => $art->name,
'thumb' => $art->thumb_url ?? $art->thumb ?? null,
'thumb_srcset' => $art->thumb_srcset ?? null,
'uname' => $art->uname ?? '',
'category_name' => $art->category_name ?? '',
];
@endphp
<x-artwork-card :art="$card" />
@endforeach
</div>
{{-- Pagination --}}
<div class="mt-10 flex justify-center">
{{ $artworks->withQueryString()->links() }}
</div>
@else
<div class="rounded-xl border border-white/[0.06] bg-white/[0.02] px-8 py-12 text-center">
<p class="text-white/40 text-sm">No artworks found for this section yet.</p>
</div>
@endif
</div>
@endsection

View File

@@ -0,0 +1,40 @@
@extends('layouts.nova')
@section('content')
<div class="px-6 pt-10 pb-6 md:px-10">
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Browse</p>
<h1 class="text-3xl font-bold text-white leading-tight flex items-center gap-3">
<i class="fa-solid fa-tags text-sky-400 text-2xl"></i>
Tags
</h1>
<p class="mt-1 text-sm text-white/50">Browse all artwork tags on Skinbase.</p>
</div>
</div>
<div class="px-6 pb-16 md:px-10">
@if($tags->isNotEmpty())
<div class="flex flex-wrap gap-2">
@foreach($tags as $tag)
<a href="{{ route('tags.show', $tag->slug) }}"
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-white/[0.05] border border-white/[0.07]
text-sm text-white/70 hover:bg-white/[0.1] hover:text-white transition-colors">
<i class="fa-solid fa-hashtag text-xs text-sky-400/70"></i>
{{ $tag->name }}
<span class="text-xs text-white/30 ml-1">{{ number_format($tag->artworks_count) }}</span>
</a>
@endforeach
</div>
<div class="mt-10 flex justify-center">
{{ $tags->withQueryString()->links() }}
</div>
@else
<div class="rounded-xl border border-white/[0.06] bg-white/[0.02] px-8 py-12 text-center">
<p class="text-white/40 text-sm">No tags found.</p>
</div>
@endif
</div>
@endsection