Commit workspace changes
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
$artworkData = $artworkData ?? [];
|
||||
$relatedItems = $relatedItems ?? [];
|
||||
$comments = $comments ?? [];
|
||||
$groupSummary = $groupSummary ?? null;
|
||||
$useUnifiedSeo = true;
|
||||
@endphp
|
||||
|
||||
@@ -47,6 +48,7 @@
|
||||
data-cdn='@json(rtrim((string) config("cdn.files_url", "https://files.skinbase.org"), "/"))'
|
||||
data-canonical='@json($meta["canonical"])'
|
||||
data-comments='@json($comments)'
|
||||
data-group-summary='@json($groupSummary)'
|
||||
data-is-authenticated='@json(auth()->check())'>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
<h2 class="text-2xl font-semibold mb-2 text-white">Reset Password</h2>
|
||||
<p class="text-sm text-white/60 mb-6">Enter your email and we'll send a link to reset your password.</p>
|
||||
|
||||
@include('auth.partials.help-links', [
|
||||
'title' => 'Need help recovering access?',
|
||||
'description' => 'Use the auth guide for recovery basics or the troubleshooting page if the issue still feels broader than a reset link.',
|
||||
'showLogin' => true,
|
||||
])
|
||||
|
||||
<x-auth-session-status class="mt-4 mb-2 text-green-300" :status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('password.email') }}" class="mt-4">
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
<p class="text-sm text-white/60 mb-6">Access your Skinbase account.</p>
|
||||
|
||||
@include('auth.partials.help-links', [
|
||||
'title' => 'Need help signing in?',
|
||||
'description' => 'Use the signup and login guide or the troubleshooting page before spending time guessing at the wrong fix.',
|
||||
'showReset' => true,
|
||||
])
|
||||
|
||||
<x-auth-session-status class="mt-4 mb-2 text-green-300" :status="session('status')" />
|
||||
|
||||
@if($errors->has('oauth'))
|
||||
|
||||
24
resources/views/auth/partials/help-links.blade.php
Normal file
24
resources/views/auth/partials/help-links.blade.php
Normal file
@@ -0,0 +1,24 @@
|
||||
@php
|
||||
$title = $title ?? 'Need help?';
|
||||
$description = $description ?? 'Use the help guides when the fastest next step is still unclear.';
|
||||
$showReset = $showReset ?? false;
|
||||
$showLogin = $showLogin ?? false;
|
||||
@endphp
|
||||
|
||||
<div class="mt-6 rounded-2xl border border-sky-400/15 bg-sky-400/10 p-4">
|
||||
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/80">{{ $title }}</p>
|
||||
<p class="mt-2 text-sm leading-6 text-slate-200/85">{{ $description }}</p>
|
||||
|
||||
<div class="mt-4 flex flex-wrap gap-3 text-sm">
|
||||
<a href="{{ route('help.auth') }}" class="rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.08]">Read signup and login help</a>
|
||||
<a href="{{ route('help.troubleshooting') }}" class="rounded-full border border-white/10 bg-black/20 px-4 py-2 font-semibold text-slate-200 transition hover:border-white/20 hover:bg-white/[0.05]">Open troubleshooting</a>
|
||||
|
||||
@if($showReset)
|
||||
<a href="{{ route('password.request') }}" class="rounded-full border border-white/10 bg-black/20 px-4 py-2 font-semibold text-slate-200 transition hover:border-white/20 hover:bg-white/[0.05]">Reset password</a>
|
||||
@endif
|
||||
|
||||
@if($showLogin)
|
||||
<a href="{{ route('login') }}" class="rounded-full border border-white/10 bg-black/20 px-4 py-2 font-semibold text-slate-200 transition hover:border-white/20 hover:bg-white/[0.05]">Open login</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -7,6 +7,12 @@
|
||||
<h2 class="text-2xl font-semibold mb-2 text-white">Create Account</h2>
|
||||
|
||||
<p class="text-sm text-white/60 mb-6">Start with your email. You’ll choose a password and username after verification.</p>
|
||||
|
||||
@include('auth.partials.help-links', [
|
||||
'title' => 'Need help before you start?',
|
||||
'description' => 'Use the signup and login guide if you want a clearer view of verification, recovery, or what happens after the first account step.',
|
||||
])
|
||||
|
||||
@if($errors->has('oauth'))
|
||||
<div class="rounded-lg bg-red-900/40 border border-red-500/40 px-4 py-3 text-sm text-red-300 mb-4">
|
||||
{{ $errors->first('oauth') }}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@php
|
||||
use App\Banner;
|
||||
$src = $sourceArtwork;
|
||||
$useUnifiedSeo = true;
|
||||
@endphp
|
||||
|
||||
@push('head')
|
||||
@@ -49,8 +50,6 @@
|
||||
@section('content')
|
||||
<div class="container-fluid legacy-page">
|
||||
@php Banner::ShowResponsiveAd(); @endphp
|
||||
|
||||
$useUnifiedSeo = true;
|
||||
<div class="pt-0">
|
||||
|
||||
{{-- Source info --}}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<div class="flex flex-wrap gap-x-6 gap-y-2 text-sm text-neutral-400">
|
||||
<a class="hover:text-white" href="/contact">Contact / Apply</a>
|
||||
<a class="hover:text-white" href="/help">Help</a>
|
||||
<a class="hover:text-white" href="/rss-feeds">RSS Feeds</a>
|
||||
<a class="hover:text-white" href="/faq">FAQ</a>
|
||||
<a class="hover:text-white" href="/rules-and-guidelines">Rules and Guidelines</a>
|
||||
|
||||
@@ -23,11 +23,12 @@
|
||||
<span class="sr-only">Skinbase.org</span>
|
||||
</a>
|
||||
|
||||
<!-- Desktop left nav: Discover · Browse · Creators · Community -->
|
||||
<!-- Desktop left nav: Discover · Browse · Groups · Creators · Community -->
|
||||
@php
|
||||
$navSection = match(true) {
|
||||
request()->is('discover', 'discover/*') => 'discover',
|
||||
request()->is('browse', 'photography', 'wallpapers', 'skins', 'other', 'tags', 'tags/*') => 'browse',
|
||||
request()->is('groups', 'groups/*') => 'groups',
|
||||
request()->is('creators', 'creators/*', 'stories', 'stories/*', 'following', 'leaderboard') => 'creators',
|
||||
request()->is('forum', 'forum/*', 'news', 'news/*') => 'community',
|
||||
default => null,
|
||||
@@ -106,6 +107,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<button class="inline-flex items-center gap-1 px-3 py-2 rounded-lg transition-colors {{ $navSection === 'groups' ? 'text-white bg-white/10' : 'hover:text-white hover:bg-white/5' }}"
|
||||
data-dd="groups"
|
||||
{{ $navSection === 'groups' ? 'aria-current=page' : '' }}>
|
||||
Groups
|
||||
<svg class="w-4 h-4 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6" /></svg>
|
||||
</button>
|
||||
<div id="dd-groups" class="dd-menu absolute left-0 mt-1 w-64 rounded-xl bg-panel border border-panel shadow-sb overflow-hidden">
|
||||
<a class="flex items-center gap-3 px-4 py-2.5 text-sm hover:bg-white/5" href="/groups">
|
||||
<i class="fa-solid fa-people-group w-4 text-center text-sb-muted"></i>Browse Groups
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-4 py-2.5 text-sm hover:bg-white/5" href="/groups?surface=featured">
|
||||
<i class="fa-solid fa-star w-4 text-center text-sb-muted"></i>Featured Groups
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-4 py-2.5 text-sm hover:bg-white/5" href="/groups?surface=recruiting">
|
||||
<i class="fa-solid fa-user-plus w-4 text-center text-sb-muted"></i>Recruiting Now
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-4 py-2.5 text-sm hover:bg-white/5" href="/groups?surface=new_rising">
|
||||
<i class="fa-solid fa-arrow-trend-up w-4 text-center text-sb-muted"></i>New & Rising
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-4 py-2.5 text-sm hover:bg-white/5" href="/leaderboard?type=groups&period=monthly">
|
||||
<i class="fa-solid fa-trophy w-4 text-center text-sb-muted"></i>Group Leaderboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- CREATORS --}}
|
||||
<div class="relative">
|
||||
<button class="inline-flex items-center gap-1 px-3 py-2 rounded-lg transition-colors {{ $navSection === 'creators' ? 'text-white bg-white/10' : 'hover:text-white hover:bg-white/5' }}"
|
||||
@@ -374,6 +401,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-1">
|
||||
<button type="button" data-mobile-section-toggle aria-controls="mobileSectionGroups" aria-expanded="false" class="w-full flex items-center justify-between py-2.5 px-3 rounded-lg text-[11px] font-semibold uppercase tracking-widest text-sb-muted hover:bg-white/5">
|
||||
<span>Groups</span>
|
||||
<i data-mobile-section-icon class="fa-solid fa-chevron-down text-xs transition-transform"></i>
|
||||
</button>
|
||||
<div id="mobileSectionGroups" data-mobile-section-panel class="hidden mt-0.5 space-y-0.5">
|
||||
<a class="flex items-center gap-3 py-2.5 px-3 rounded-lg hover:bg-white/5" href="/groups"><i class="fa-solid fa-people-group w-4 text-center text-sb-muted"></i>Browse Groups</a>
|
||||
<a class="flex items-center gap-3 py-2.5 px-3 rounded-lg hover:bg-white/5" href="/groups?surface=featured"><i class="fa-solid fa-star w-4 text-center text-sb-muted"></i>Featured Groups</a>
|
||||
<a class="flex items-center gap-3 py-2.5 px-3 rounded-lg hover:bg-white/5" href="/groups?surface=recruiting"><i class="fa-solid fa-user-plus w-4 text-center text-sb-muted"></i>Recruiting Now</a>
|
||||
<a class="flex items-center gap-3 py-2.5 px-3 rounded-lg hover:bg-white/5" href="/groups?surface=new_rising"><i class="fa-solid fa-arrow-trend-up w-4 text-center text-sb-muted"></i>New & Rising</a>
|
||||
<a class="flex items-center gap-3 py-2.5 px-3 rounded-lg hover:bg-white/5" href="/leaderboard?type=groups&period=monthly"><i class="fa-solid fa-trophy w-4 text-center text-sb-muted"></i>Group Leaderboard</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-1">
|
||||
<button type="button" data-mobile-section-toggle aria-controls="mobileSectionCreators" aria-expanded="false" class="w-full flex items-center justify-between py-2.5 px-3 rounded-lg text-[11px] font-semibold uppercase tracking-widest text-sb-muted hover:bg-white/5">
|
||||
<span>Creators</span>
|
||||
|
||||
@@ -12,9 +12,15 @@
|
||||
|
||||
<div class="flex h-full flex-col p-5">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="inline-flex items-center rounded-full border border-white/[0.08] bg-white/[0.04] px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-white/70">{{ $article->type_label }}</span>
|
||||
@if($article->category)
|
||||
<a href="{{ route('news.category', $article->category->slug) }}" class="inline-flex items-center rounded-full border border-sky-400/20 bg-sky-500/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-sky-200">{{ $article->category->name }}</a>
|
||||
@endif
|
||||
@if($article->is_pinned)
|
||||
<span class="inline-flex items-center rounded-full border border-amber-300/20 bg-amber-400/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-amber-100">Pinned</span>
|
||||
@elseif($article->is_featured)
|
||||
<span class="inline-flex items-center rounded-full border border-emerald-300/20 bg-emerald-400/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-emerald-100">Featured</span>
|
||||
@endif
|
||||
<span class="text-[11px] uppercase tracking-[0.16em] text-white/30">{{ $article->published_at?->format('d M Y') }}</span>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +33,13 @@
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center justify-between gap-3 text-sm text-white/40">
|
||||
<span class="truncate">{{ $article->author?->name ?? 'Skinbase' }}</span>
|
||||
<span class="truncate">
|
||||
@if($article->author?->username)
|
||||
<a href="{{ route('news.author', $article->author->username) }}" class="transition hover:text-white">{{ $article->author->name ?? $article->author->username }}</a>
|
||||
@else
|
||||
{{ $article->author?->name ?? 'Skinbase' }}
|
||||
@endif
|
||||
</span>
|
||||
<span class="shrink-0 inline-flex items-center gap-1.5">
|
||||
<i class="fa-regular fa-eye text-[11px]"></i>
|
||||
{{ number_format((int) $article->views) }}
|
||||
|
||||
49
resources/views/news/_related_entities.blade.php
Normal file
49
resources/views/news/_related_entities.blade.php
Normal file
@@ -0,0 +1,49 @@
|
||||
@if(!empty($relatedEntities) && count($relatedEntities) > 0)
|
||||
<section class="mt-8">
|
||||
<div class="mb-4 flex items-center justify-between gap-3">
|
||||
<h2 class="text-lg font-semibold text-white/90">Connected in Nova</h2>
|
||||
<span class="text-xs uppercase tracking-[0.18em] text-white/35">{{ count($relatedEntities) }} linked</span>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach($relatedEntities as $entity)
|
||||
<a href="{{ $entity['url'] ?? '#' }}" class="group overflow-hidden rounded-[24px] border border-white/[0.08] bg-white/[0.03] transition hover:-translate-y-0.5 hover:border-white/[0.16]">
|
||||
<div class="relative aspect-[16/9] overflow-hidden bg-black/20">
|
||||
@if(!empty($entity['image']))
|
||||
<img src="{{ $entity['image'] }}" alt="{{ $entity['title'] }}" class="h-full w-full object-cover transition duration-300 group-hover:scale-[1.04]">
|
||||
@else
|
||||
<div class="absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.14),transparent_45%),linear-gradient(180deg,rgba(15,23,42,0.92),rgba(2,6,23,0.98))]"></div>
|
||||
@endif
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-[#020611cc] via-transparent to-transparent"></div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="flex flex-wrap items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-white/40">
|
||||
<span class="text-sky-200">{{ $entity['context_label'] ?? ($entity['entity_label'] ?? 'Related') }}</span>
|
||||
<span class="rounded-full border border-white/[0.08] bg-white/[0.04] px-2 py-0.5 text-white/55">{{ $entity['entity_label'] ?? 'Link' }}</span>
|
||||
</div>
|
||||
<div class="mt-3 flex items-start gap-3">
|
||||
@if(!empty($entity['avatar']))
|
||||
<img src="{{ $entity['avatar'] }}" alt="{{ $entity['title'] }}" class="h-11 w-11 rounded-2xl border border-white/[0.08] object-cover">
|
||||
@endif
|
||||
<div class="min-w-0 flex-1">
|
||||
<h3 class="text-lg font-semibold text-white">{{ $entity['title'] }}</h3>
|
||||
@if(!empty($entity['subtitle']))
|
||||
<p class="mt-1 text-xs uppercase tracking-[0.16em] text-white/35">{{ $entity['subtitle'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@if(!empty($entity['description']))
|
||||
<p class="mt-3 text-sm leading-6 text-white/60">{{ $entity['description'] }}</p>
|
||||
@endif
|
||||
@if(!empty($entity['meta']))
|
||||
<div class="mt-4 flex flex-wrap gap-2 text-xs text-white/40">
|
||||
@foreach($entity['meta'] as $meta)
|
||||
<span class="rounded-full border border-white/[0.08] bg-white/[0.04] px-2.5 py-1">{{ $meta }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
44
resources/views/news/archive.blade.php
Normal file
44
resources/views/news/archive.blade.php
Normal file
@@ -0,0 +1,44 @@
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => $archiveDate->format('F Y') . ' — News Archive',
|
||||
'metaDescription' => 'News archive for ' . $archiveDate->format('F Y') . ' on Skinbase Nova.',
|
||||
'metaCanonical' => route('news.archive', ['year' => $archiveDate->year, 'month' => $archiveDate->month]),
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
@php
|
||||
$headerBreadcrumbs = collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
(object) ['name' => $archiveDate->format('F Y'), 'url' => route('news.archive', ['year' => $archiveDate->year, 'month' => $archiveDate->month])],
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-nova-page-header
|
||||
section="Community"
|
||||
:title="$archiveDate->format('F Y')"
|
||||
icon="fa-calendar-days"
|
||||
:breadcrumbs="$headerBreadcrumbs"
|
||||
:description="'Published News stories from ' . $archiveDate->format('F Y') . '.'"
|
||||
headerClass="pb-6"
|
||||
/>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-6 pt-8 pb-16 md:px-10">
|
||||
<div class="grid gap-8 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<section>
|
||||
@if($articles->isEmpty())
|
||||
<div class="rounded-[28px] border border-white/[0.06] bg-white/[0.025] px-8 py-14 text-center text-white/45">No articles were published during this month.</div>
|
||||
@else
|
||||
<div class="grid gap-5 md:grid-cols-2">
|
||||
@foreach($articles as $article)
|
||||
@include('news._article_card', ['article' => $article])
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="mt-8 flex justify-center">{{ $articles->links() }}</div>
|
||||
@endif
|
||||
</section>
|
||||
<aside class="space-y-4">
|
||||
@include('news._sidebar', ['categories' => $categories, 'trending' => $trending, 'tags' => $tags])
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
56
resources/views/news/author.blade.php
Normal file
56
resources/views/news/author.blade.php
Normal file
@@ -0,0 +1,56 @@
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => ($author->name ?: $author->username) . ' — News Author',
|
||||
'metaDescription' => 'News stories and announcements by ' . ($author->name ?: $author->username) . '.',
|
||||
'metaCanonical' => route('news.author', ['username' => $author->username]),
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
@php
|
||||
$authorLabel = $author->name ?: $author->username;
|
||||
$headerBreadcrumbs = collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
(object) ['name' => $authorLabel, 'url' => route('news.author', ['username' => $author->username])],
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-nova-page-header
|
||||
section="Community"
|
||||
:title="$authorLabel"
|
||||
icon="fa-user-pen"
|
||||
:breadcrumbs="$headerBreadcrumbs"
|
||||
:description="'Editorial stories and updates by ' . $authorLabel . '.'"
|
||||
headerClass="pb-6"
|
||||
/>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-6 pt-8 pb-16 md:px-10">
|
||||
<div class="mb-8 rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-5 shadow-[0_18px_45px_rgba(0,0,0,0.16)] sm:p-6">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center">
|
||||
<img src="{{ \App\Support\AvatarUrl::forUser((int) $author->id, $author->profile?->avatar_hash ?? null, 96) }}" alt="{{ $authorLabel }}" class="h-20 w-20 rounded-[24px] border border-white/[0.08] object-cover">
|
||||
<div>
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.2em] text-white/40">News author</div>
|
||||
<h2 class="mt-2 text-2xl font-semibold text-white">{{ $authorLabel }}</h2>
|
||||
<p class="mt-2 text-sm leading-7 text-white/60">{{ $author->profile?->bio ? Str::limit($author->profile->bio, 180) : 'Writes updates, announcements, and editorial stories for Skinbase Nova.' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-8 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<section>
|
||||
@if($articles->isEmpty())
|
||||
<div class="rounded-[28px] border border-white/[0.06] bg-white/[0.025] px-8 py-14 text-center text-white/45">This author has not published News articles yet.</div>
|
||||
@else
|
||||
<div class="grid gap-5 md:grid-cols-2">
|
||||
@foreach($articles as $article)
|
||||
@include('news._article_card', ['article' => $article])
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="mt-8 flex justify-center">{{ $articles->links() }}</div>
|
||||
@endif
|
||||
</section>
|
||||
<aside class="space-y-4">
|
||||
@include('news._sidebar', ['categories' => $categories, 'trending' => $trending, 'tags' => $tags])
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -52,7 +52,8 @@
|
||||
<div class="flex flex-col justify-between gap-5 p-6 lg:p-8">
|
||||
<div>
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-white/45">
|
||||
<span class="text-sky-300">Featured story</span>
|
||||
<span class="text-sky-300">{{ $featured->is_pinned ? 'Pinned story' : 'Featured story' }}</span>
|
||||
<span class="rounded-full border border-white/[0.08] bg-white/[0.04] px-2.5 py-1 text-[11px] tracking-[0.12em] text-white/70">{{ $featured->type_label }}</span>
|
||||
@if($featured->category)
|
||||
<span class="rounded-full border border-sky-400/20 bg-sky-500/10 px-2.5 py-1 text-[11px] tracking-[0.12em] text-sky-200">{{ $featured->category->name }}</span>
|
||||
@endif
|
||||
@@ -74,6 +75,14 @@
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@if(!empty($highlights) && $highlights->isNotEmpty())
|
||||
<section class="mb-8 grid gap-5 md:grid-cols-3">
|
||||
@foreach($highlights as $article)
|
||||
@include('news._article_card', ['article' => $article])
|
||||
@endforeach
|
||||
</section>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-8 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<section>
|
||||
@if($articles->isEmpty())
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@php($useUnifiedSeo = true)
|
||||
@php
|
||||
$useUnifiedSeo = true;
|
||||
@endphp
|
||||
@extends('layouts.nova')
|
||||
|
||||
@php
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
@php
|
||||
$isPreview = (bool) ($previewMode ?? false);
|
||||
$articleUrl = $isPreview ? ($previewCanonical ?? url()->current()) : route('news.show', $article->slug);
|
||||
$seo = \App\Support\Seo\SeoDataBuilder::fromArray([
|
||||
'title' => $article->meta_title ?: $article->title,
|
||||
'description' => $article->meta_description ?: Str::limit(strip_tags((string) $article->excerpt), 160),
|
||||
'keywords' => $article->meta_keywords,
|
||||
'canonical' => route('news.show', $article->slug),
|
||||
'robots' => 'index,follow',
|
||||
'canonical' => $isPreview ? $articleUrl : ($article->canonical_url ?: $articleUrl),
|
||||
'robots' => $isPreview ? 'noindex,nofollow' : 'index,follow',
|
||||
'og_type' => 'article',
|
||||
'og_title' => $article->effective_og_title,
|
||||
'og_description' => $article->effective_og_description,
|
||||
@@ -30,7 +32,7 @@
|
||||
'@type' => 'Person',
|
||||
'name' => $article->author?->name,
|
||||
]),
|
||||
'mainEntityOfPage' => route('news.show', $article->slug),
|
||||
'mainEntityOfPage' => $articleUrl,
|
||||
], fn (mixed $value): bool => $value !== null && $value !== ''))
|
||||
->build();
|
||||
@endphp
|
||||
@@ -38,7 +40,8 @@
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => $article->meta_title ?: $article->title,
|
||||
'metaDescription' => $article->meta_description ?: Str::limit(strip_tags((string)$article->excerpt), 160),
|
||||
'metaCanonical' => route('news.show', $article->slug),
|
||||
'metaCanonical' => $isPreview ? $articleUrl : ($article->canonical_url ?: $articleUrl),
|
||||
'metaRobots' => $isPreview ? 'noindex,nofollow' : 'index,follow',
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
@@ -50,7 +53,7 @@
|
||||
$article->category
|
||||
? (object) ['name' => $article->category->name, 'url' => route('news.category', $article->category->slug)]
|
||||
: null,
|
||||
(object) ['name' => $article->title, 'url' => route('news.show', $article->slug)],
|
||||
(object) ['name' => $article->title, 'url' => $articleUrl],
|
||||
])->filter()->values();
|
||||
@endphp
|
||||
|
||||
@@ -64,9 +67,13 @@
|
||||
>
|
||||
<x-slot name="actions">
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm text-white/60">
|
||||
<span class="inline-flex items-center rounded-full border border-white/[0.08] bg-white/[0.04] px-3 py-1.5 text-white/75">{{ $article->type_label }}</span>
|
||||
@if($article->category)
|
||||
<a href="{{ route('news.category', $article->category->slug) }}" class="inline-flex items-center rounded-full border border-sky-400/20 bg-sky-500/10 px-3 py-1.5 text-sky-200">{{ $article->category->name }}</a>
|
||||
@endif
|
||||
@if($article->is_pinned)
|
||||
<span class="inline-flex items-center rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-1.5 text-amber-100">Pinned</span>
|
||||
@endif
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full border border-white/[0.08] bg-white/[0.04] px-3 py-1.5">
|
||||
<i class="fa-regular fa-clock text-xs"></i>
|
||||
{{ $article->reading_time }} min read
|
||||
@@ -76,6 +83,23 @@
|
||||
</x-nova-page-header>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-6 pt-8 pb-16 md:px-10">
|
||||
@if($isPreview)
|
||||
<div class="mb-6 rounded-[24px] border border-indigo-300/20 bg-indigo-400/10 px-5 py-4 text-sm text-indigo-100 shadow-[0_16px_40px_rgba(30,41,59,0.24)]">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-indigo-100/70">Preview mode</div>
|
||||
<div class="mt-1">This article preview is visible only to newsroom staff and is excluded from indexing.</div>
|
||||
</div>
|
||||
@if(!empty($previewBackUrl))
|
||||
<a href="{{ $previewBackUrl }}" class="inline-flex items-center gap-2 rounded-full border border-indigo-200/20 bg-indigo-500/10 px-4 py-2 text-sm font-semibold text-indigo-50 transition hover:bg-indigo-500/15">
|
||||
<i class="fa-solid fa-arrow-left text-xs"></i>
|
||||
Back to editor
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-8 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<article class="min-w-0">
|
||||
@if($article->cover_url)
|
||||
@@ -86,7 +110,13 @@
|
||||
|
||||
<div class="mt-6 rounded-[32px] border border-white/[0.06] bg-[linear-gradient(180deg,rgba(11,16,26,0.94),rgba(7,11,19,0.92))] p-6 shadow-[0_18px_45px_rgba(0,0,0,0.2)] sm:p-8">
|
||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-white/45">
|
||||
<span>{{ $article->author?->name ?? 'Skinbase' }}</span>
|
||||
<span>
|
||||
@if($article->author?->username)
|
||||
<a href="{{ route('news.author', $article->author->username) }}" class="transition hover:text-white">{{ $article->author->name ?? $article->author->username }}</a>
|
||||
@else
|
||||
{{ $article->author?->name ?? 'Skinbase' }}
|
||||
@endif
|
||||
</span>
|
||||
<span>{{ $article->published_at?->format('d M Y') }}</span>
|
||||
<span>{{ number_format((int) $article->views) }} views</span>
|
||||
</div>
|
||||
@@ -96,7 +126,7 @@
|
||||
@endif
|
||||
|
||||
<div class="prose prose-invert prose-sky mt-8 max-w-none prose-p:text-white/72 prose-li:text-white/70 prose-strong:text-white prose-a:text-sky-300 hover:prose-a:text-sky-200 prose-headings:text-white">
|
||||
{!! $article->content !!}
|
||||
{!! $article->rendered_content !!}
|
||||
</div>
|
||||
|
||||
@if($article->tags->isNotEmpty())
|
||||
@@ -133,6 +163,8 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@include('news._related_entities', ['relatedEntities' => $relatedEntities ?? []])
|
||||
|
||||
@if($related->isNotEmpty())
|
||||
<section class="mt-8">
|
||||
<div class="mb-4 flex items-center justify-between gap-3">
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
@php
|
||||
$hasQuery = isset($q) && $q !== '';
|
||||
$resultCount = method_exists($artworks, 'total') ? (int) $artworks->total() : 0;
|
||||
$groupResults = collect($groups ?? []);
|
||||
$groupResultCount = $groupResults->count();
|
||||
$newsResults = collect($news ?? []);
|
||||
$newsResultCount = $newsResults->count();
|
||||
$hasAnyResults = $resultCount > 0 || $groupResultCount > 0 || $newsResultCount > 0;
|
||||
$galleryArtworks = collect($artworks->items())->map(fn ($art) => [
|
||||
'id' => $art->id ?? null,
|
||||
'name' => $art->name ?? null,
|
||||
@@ -36,7 +41,7 @@
|
||||
Search
|
||||
</h1>
|
||||
<p class="mt-1 text-sm text-white/50">
|
||||
{{ $hasQuery ? 'Results for "' . $q . '"' : 'Find artworks, creators, and styles across Skinbase.' }}
|
||||
{{ $hasQuery ? 'Results for "' . $q . '"' : 'Find artworks, creators, groups, and styles across Skinbase.' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,7 +51,7 @@
|
||||
type="search"
|
||||
name="q"
|
||||
value="{{ $q ?? '' }}"
|
||||
placeholder="Search artworks, artists, tags..."
|
||||
placeholder="Search artworks, groups, artists, tags..."
|
||||
autofocus
|
||||
class="w-full rounded-xl border border-white/10 bg-white/[0.05] py-3 pl-4 pr-12 text-white outline-none transition-colors placeholder:text-neutral-500 focus:border-sky-500"
|
||||
>
|
||||
@@ -74,15 +79,99 @@
|
||||
@endforeach
|
||||
|
||||
<span class="ml-auto text-sm text-neutral-500">
|
||||
{{ number_format($resultCount) }} {{ $resultCount === 1 ? 'result' : 'results' }}
|
||||
{{ number_format($resultCount + $groupResultCount + $newsResultCount) }} {{ ($resultCount + $groupResultCount + $newsResultCount) === 1 ? 'result' : 'results' }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($hasQuery && $resultCount === 0)
|
||||
@if($groupResultCount > 0)
|
||||
<section class="mb-8 rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_18px_50px_rgba(2,6,23,0.2)]">
|
||||
<div class="mb-4 flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<p class="text-[11px] font-semibold uppercase tracking-[0.2em] text-sky-200/80">Groups</p>
|
||||
<h2 class="mt-1 text-xl font-semibold text-white">Collaborative identities matching your search</h2>
|
||||
<p class="mt-1 text-sm text-slate-400">Explore shared publishing brands, contributor teams, and recruiting collectives related to this query.</p>
|
||||
</div>
|
||||
<a href="/groups" class="text-sm font-semibold text-sky-300 transition hover:text-white">Browse all groups</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach($groupResults as $group)
|
||||
<a href="{{ $group['urls']['public'] ?? $group['profile_url'] ?? '/groups' }}" class="rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:-translate-y-0.5 hover:border-white/20">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex h-14 w-14 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-white/[0.04] text-slate-300">
|
||||
@if(!empty($group['avatar_url']))
|
||||
<img src="{{ $group['avatar_url'] }}" alt="{{ $group['name'] ?? 'Group' }}" class="h-full w-full object-cover" loading="lazy">
|
||||
@else
|
||||
<i class="fa-solid fa-people-group"></i>
|
||||
@endif
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-lg font-semibold text-white">{{ $group['name'] ?? 'Group' }}</div>
|
||||
@if(!empty($group['headline']))
|
||||
<div class="mt-1 text-sm text-slate-400">{{ $group['headline'] }}</div>
|
||||
@elseif(!empty($group['bio_excerpt']))
|
||||
<div class="mt-1 text-sm text-slate-400">{{ $group['bio_excerpt'] }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
@foreach(collect($group['trust_signals'] ?? [])->take(2) as $signal)
|
||||
<span class="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200">{{ $signal['label'] ?? 'Signal' }}</span>
|
||||
@endforeach
|
||||
@if(!empty($group['is_recruiting']))
|
||||
<span class="rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-emerald-100">Recruiting</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-wrap gap-4 text-xs text-slate-400">
|
||||
<span>{{ number_format((int) ($group['counts']['artworks'] ?? $group['artworks_count'] ?? 0)) }} artworks</span>
|
||||
<span>{{ number_format((int) ($group['counts']['members'] ?? $group['members_count'] ?? 0)) }} members</span>
|
||||
<span>{{ number_format((int) ($group['counts']['followers'] ?? $group['followers_count'] ?? 0)) }} followers</span>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@if($newsResultCount > 0)
|
||||
<section class="mb-8 rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_18px_50px_rgba(2,6,23,0.2)]">
|
||||
<div class="mb-4 flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<p class="text-[11px] font-semibold uppercase tracking-[0.2em] text-sky-200/80">News</p>
|
||||
<h2 class="mt-1 text-xl font-semibold text-white">Editorial stories and platform updates matching your search</h2>
|
||||
<p class="mt-1 text-sm text-slate-400">Find tutorials, release notes, community spotlights, and announcements connected to this topic.</p>
|
||||
</div>
|
||||
<a href="/news" class="text-sm font-semibold text-sky-300 transition hover:text-white">Browse all News</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
@foreach($newsResults as $item)
|
||||
<a href="{{ route('news.show', $item->slug) }}" class="rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:-translate-y-0.5 hover:border-white/20">
|
||||
<div class="flex flex-wrap items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-white/35">
|
||||
<span class="rounded-full border border-white/10 bg-white/[0.04] px-2 py-0.5 text-white/60">{{ $item->type_label }}</span>
|
||||
@if($item->category)
|
||||
<span class="rounded-full border border-sky-400/20 bg-sky-500/10 px-2 py-0.5 text-sky-200">{{ $item->category->name }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<h3 class="mt-3 text-lg font-semibold leading-7 text-white">{{ $item->title }}</h3>
|
||||
<p class="mt-2 text-sm leading-6 text-slate-400">{{ Str::limit(strip_tags((string) ($item->excerpt ?: $item->rendered_content)), 120) }}</p>
|
||||
<div class="mt-4 flex items-center justify-between gap-3 text-xs text-slate-500">
|
||||
<span>{{ $item->author?->name ?? 'Skinbase' }}</span>
|
||||
<span>{{ $item->published_at?->format('d M Y') }}</span>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@if($hasQuery && !$hasAnyResults)
|
||||
<div class="rounded-xl border border-white/[0.06] bg-white/[0.03] p-14 text-center">
|
||||
<p class="mb-2 text-lg text-neutral-400">No results for <span class="text-white">"{{ $q }}"</span></p>
|
||||
<p class="text-sm text-neutral-500">Try a different keyword or browse <a href="/discover/trending" class="text-sky-400 hover:underline">trending artworks</a>.</p>
|
||||
<p class="text-sm text-neutral-500">Try a different keyword or browse <a href="/groups" class="text-sky-400 hover:underline">public groups</a>, <a href="/news" class="text-sky-400 hover:underline">News</a>, and <a href="/discover/trending" class="text-sky-400 hover:underline">trending artworks</a>.</p>
|
||||
</div>
|
||||
@else
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user