Save workspace changes

This commit is contained in:
2026-04-18 17:02:56 +02:00
parent f02ea9a711
commit 87d60af5a9
4220 changed files with 1388603 additions and 1554 deletions

View File

@@ -1,160 +1,264 @@
@extends('layouts.nova')
@php($useUnifiedSeo = true)
@php
$useUnifiedSeo = true;
@endphp
@section('content')
<div class="px-6 pt-10 pb-8 md:px-10" id="search-page" data-q="{{ $q ?? '' }}">
<div class="mb-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<p class="mb-1 text-xs font-semibold uppercase tracking-widest text-white/30">Discover</p>
<h1 class="flex items-center gap-3 text-3xl font-bold leading-tight text-white">
<i class="fa-solid fa-magnifying-glass text-2xl text-sky-400"></i>
Search
</h1>
<p class="mt-1 text-sm text-white/50">
{{ $hasQuery ? 'Results for "' . $q . '"' : 'Find artworks, creators, groups, and styles across Skinbase.' }}
</p>
</div>
<div id="search-page" data-q="{{ $q ?? '' }}">
{{-- ── Hero ──────────────────────────────────────────────────────────── --}}
<div class="relative overflow-hidden border-b border-white/[0.05] bg-[radial-gradient(ellipse_80%_60%_at_50%_-10%,rgba(14,165,233,0.12),transparent)] px-6 pb-8 pt-10 md:px-10">
{{-- ambient orbs --}}
<div aria-hidden="true" class="pointer-events-none absolute inset-0 overflow-hidden">
<div class="absolute -left-20 -top-10 h-72 w-72 rounded-full bg-sky-500/10 blur-3xl"></div>
<div class="absolute right-0 top-0 h-96 w-64 rounded-full bg-cyan-400/8 blur-3xl"></div>
<div class="absolute left-1/2 top-16 h-48 w-80 -translate-x-1/2 rounded-full bg-indigo-500/6 blur-3xl"></div>
</div>
<form action="/search" method="GET" class="relative mt-5 max-w-3xl" role="search">
<input
type="search"
name="q"
value="{{ $q ?? '' }}"
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"
>
@if($hasQuery)
<input type="hidden" name="sort" value="{{ $sort ?? 'latest' }}">
<div class="relative">
{{-- eyebrow --}}
<div class="mb-4 inline-flex items-center gap-2 rounded-full border border-sky-400/20 bg-sky-400/8 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-300">
<i class="fa-solid fa-magnifying-glass fa-fw text-[10px]"></i>
Discover
</div>
<div class="flex flex-col gap-5 md:flex-row md:items-end md:justify-between">
<div>
<h1 class="text-3xl font-bold tracking-tight text-white md:text-4xl">
@if($hasQuery)
Results for <span class="text-sky-300">&ldquo;{{ $q }}&rdquo;</span>
@else
Search Skinbase
@endif
</h1>
<p class="mt-2 max-w-xl text-sm leading-relaxed text-white/50">
@if(!$hasQuery)
Find artworks, creators, groups, and styles across Skinbase.
@elseif(($resultCount + $groupResultCount + $newsResultCount) > 0)
Found <span class="font-semibold text-white/80">{{ number_format($resultCount + $groupResultCount + $newsResultCount) }}</span> {{ ($resultCount + $groupResultCount + $newsResultCount) === 1 ? 'result' : 'results' }} across artworks, groups, and news.
@else
No results matched your query. Try a different keyword.
@endif
</p>
</div>
@if($hasQuery)
<a href="/search" class="shrink-0 inline-flex items-center gap-1.5 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white/50 transition hover:border-white/20 hover:bg-white/[0.08] hover:text-white/80">
<i class="fa-solid fa-xmark fa-fw"></i>
Clear search
</a>
@endif
</div>
{{-- Search bar --}}
<form action="/search" method="GET" role="search" class="relative mt-6 max-w-2xl">
<div class="relative flex items-center">
<i class="fa-solid fa-magnifying-glass pointer-events-none absolute left-4 top-1/2 z-10 -translate-y-1/2 text-sm text-white/30"></i>
<input
type="search"
name="q"
id="search-input"
value="{{ $q ?? '' }}"
placeholder="Search artworks, groups, artists, tags…"
autofocus
autocomplete="off"
class="h-14 w-full rounded-2xl border border-white/[0.10] bg-white/[0.05] pl-11 pr-[5.5rem] text-[15px] text-white shadow-[0_4px_24px_rgba(0,0,0,0.25)] outline-none transition-all placeholder:text-white/25 focus:border-sky-400/40 focus:bg-white/[0.07] focus:ring-2 focus:ring-sky-400/15"
>
@if($hasQuery)
<input type="hidden" name="sort" value="{{ $sort ?? 'latest' }}">
@endif
<button
type="submit"
class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex h-10 items-center gap-2 rounded-xl bg-sky-500 px-4 text-sm font-semibold text-slate-950 transition hover:bg-sky-400 active:scale-95"
>
Search
</button>
</div>
</form>
{{-- Sort chips only when we have artwork results --}}
@if($hasQuery && $resultCount > 0)
<div class="mt-5 flex flex-wrap items-center gap-2">
<span class="text-[11px] font-semibold uppercase tracking-widest text-white/30">Sort</span>
@foreach(['latest' => 'Newest', 'popular' => 'Most viewed', 'likes' => 'Most liked', 'downloads' => 'Most downloaded'] as $key => $label)
<a
href="{{ request()->fullUrlWithQuery(['sort' => $key, 'page' => null]) }}"
class="rounded-full px-3.5 py-1.5 text-[12px] font-medium transition-all {{ ($sort ?? 'latest') === $key ? 'bg-sky-500 text-white shadow-[0_0_16px_rgba(14,165,233,0.35)]' : 'border border-white/[0.08] bg-white/[0.04] text-white/50 hover:border-white/15 hover:bg-white/[0.08] hover:text-white/80' }}"
>
{{ $label }}
</a>
@endforeach
<span class="ml-auto flex items-center gap-1.5 text-xs text-white/30">
<i class="fa-solid fa-layer-group fa-fw text-[10px]"></i>
{{ number_format($resultCount) }} artwork{{ $resultCount === 1 ? '' : 's' }}
</span>
</div>
@endif
<button type="submit" class="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400 transition-colors hover:text-sky-400">
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z" />
</svg>
</button>
</form>
</div>
</div>
@if($hasQuery)
<div class="mb-6 flex flex-wrap items-center gap-3">
<span class="text-sm text-neutral-400">Sort by:</span>
@foreach(['latest' => 'Newest', 'popular' => 'Most viewed', 'likes' => 'Most liked', 'downloads' => 'Most downloaded'] as $key => $label)
<a
href="{{ request()->fullUrlWithQuery(['sort' => $key, 'page' => null]) }}"
class="rounded-lg px-3 py-1.5 text-xs font-medium transition-colors {{ ($sort ?? 'latest') === $key ? 'bg-sky-500 text-white' : 'bg-white/5 text-neutral-400 hover:bg-white/10 hover:text-white' }}"
>
{{ $label }}
</a>
@endforeach
{{-- ── Body ──────────────────────────────────────────────────────────── --}}
<div class="px-6 py-8 md:px-10">
<span class="ml-auto text-sm text-neutral-500">
{{ number_format($resultCount + $groupResultCount + $newsResultCount) }} {{ ($resultCount + $groupResultCount + $newsResultCount) === 1 ? 'result' : 'results' }}
</span>
</div>
@endif
@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>
{{-- Groups section --}}
@if($groupResultCount > 0)
<section class="mb-10">
<div class="mb-5 flex items-end justify-between gap-4">
<div>
<p class="mb-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-300/70">Groups</p>
<h2 class="text-xl font-semibold text-white">Collaborative identities</h2>
</div>
<a href="/groups" class="shrink-0 text-sm font-medium text-sky-400/70 transition hover:text-sky-300">
Browse all <i class="fa-solid fa-arrow-right fa-fw text-xs"></i>
</a>
</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>
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
@foreach($groupResults as $group)
@php
$groupUrl = $group['urls']['public'] ?? $group['profile_url'] ?? '/groups';
$groupName = $group['name'] ?? 'Group';
$artworksCount = number_format((int) ($group['counts']['artworks'] ?? $group['artworks_count'] ?? 0));
$membersCount = number_format((int) ($group['counts']['members'] ?? $group['members_count'] ?? 0));
$followersCount = number_format((int) ($group['counts']['followers'] ?? $group['followers_count'] ?? 0));
$bio = $group['headline'] ?? $group['bio_excerpt'] ?? '';
@endphp
<a href="{{ $groupUrl }}"
class="group relative flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/[0.07] bg-white/[0.03] p-5 transition-all duration-200 hover:-translate-y-0.5 hover:border-sky-500/20 hover:bg-white/[0.05] hover:shadow-[0_12px_32px_rgba(0,0,0,0.28)]">
{{-- avatar + name --}}
<div class="flex items-center gap-4">
<div class="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-xl border border-white/[0.08] bg-white/[0.03] text-slate-400">
@if(!empty($group['avatar_url']))
<img src="{{ $group['avatar_url'] }}" alt="{{ $groupName }}" class="h-full w-full object-cover" loading="lazy">
@else
<i class="fa-solid fa-people-group text-lg"></i>
@endif
</div>
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<span class="truncate text-[15px] font-semibold text-white">{{ $groupName }}</span>
@if(!empty($group['is_recruiting']))
<span class="shrink-0 rounded-full border border-emerald-300/20 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-emerald-300">Hiring</span>
@endif
</div>
@if($bio)
<p class="mt-0.5 truncate text-sm text-white/40">{{ $bio }}</p>
@endif
</div>
</div>
{{-- trust signals --}}
@if(!empty($group['trust_signals']))
<div class="flex flex-wrap gap-1.5">
@foreach(collect($group['trust_signals'])->take(3) as $signal)
<span class="rounded-full border border-white/[0.08] bg-white/[0.04] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wide text-white/50">{{ $signal['label'] ?? '' }}</span>
@endforeach
</div>
@endif
{{-- stats row --}}
<div class="mt-auto flex items-center gap-5 border-t border-white/[0.05] pt-3 text-xs text-white/35">
<span class="flex items-center gap-1.5"><i class="fa-solid fa-image fa-fw text-[10px]"></i>{{ $artworksCount }}</span>
<span class="flex items-center gap-1.5"><i class="fa-solid fa-users fa-fw text-[10px]"></i>{{ $membersCount }}</span>
<span class="flex items-center gap-1.5"><i class="fa-solid fa-heart fa-fw text-[10px]"></i>{{ $followersCount }}</span>
<span class="ml-auto flex items-center gap-1 text-sky-400/50 opacity-0 transition-opacity group-hover:opacity-100">
View <i class="fa-solid fa-arrow-right fa-fw text-[10px]"></i>
</span>
</div>
</a>
@endforeach
</div>
</section>
@endif
{{-- Artwork divider (only if other sections exist above) --}}
@if($hasQuery && ($groupResultCount > 0 || $newsResultCount > 0) && $resultCount > 0)
<div class="mb-6 flex items-center gap-4">
<div class="flex-1 border-t border-white/[0.05]"></div>
<span class="text-[11px] font-semibold uppercase tracking-[0.24em] text-white/30">
<i class="fa-solid fa-image fa-fw text-[10px]"></i>
Artworks
</span>
<div class="flex-1 border-t border-white/[0.05]"></div>
</div>
@endif
{{-- No-results state --}}
@if($hasQuery && !$hasAnyResults)
<div class="rounded-2xl border border-white/[0.06] bg-white/[0.02] px-8 py-16 text-center">
<div class="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-2xl border border-white/[0.07] bg-white/[0.04] text-white/25">
<i class="fa-regular fa-face-frown text-2xl"></i>
</div>
<p class="text-lg font-semibold text-white/60">Nothing found for <span class="text-white">&ldquo;{{ $q }}&rdquo;</span></p>
<p class="mt-2 text-sm text-white/35">Try a broader keyword, or explore below.</p>
<div class="mt-7 flex flex-wrap items-center justify-center gap-3">
<a href="/groups" class="inline-flex items-center gap-2 rounded-full border border-white/[0.08] bg-white/[0.04] px-5 py-2.5 text-sm font-medium text-white/60 transition hover:bg-white/[0.08] hover:text-white">
<i class="fa-solid fa-people-group fa-fw text-xs"></i> Browse Groups
</a>
<a href="/news" class="inline-flex items-center gap-2 rounded-full border border-white/[0.08] bg-white/[0.04] px-5 py-2.5 text-sm font-medium text-white/60 transition hover:bg-white/[0.08] hover:text-white">
<i class="fa-solid fa-newspaper fa-fw text-xs"></i> Browse News
</a>
<a href="/discover/trending" class="inline-flex items-center gap-2 rounded-full border border-sky-400/15 bg-sky-500/8 px-5 py-2.5 text-sm font-medium text-sky-300/80 transition hover:bg-sky-500/15 hover:text-sky-200">
<i class="fa-solid fa-fire fa-fw text-xs"></i> Trending
</a>
</div>
</div>
@else
<div
data-react-masonry-gallery
data-artworks="{{ json_encode($galleryArtworks) }}"
data-gallery-type="search"
@if($galleryNextPageUrl) data-next-page-url="{{ $galleryNextPageUrl }}" @endif
data-limit="24"
class="min-h-32"
></div>
@endif
{{-- News section --}}
@if($newsResultCount > 0)
<section class="mt-10">
<div class="mb-5 flex items-end justify-between gap-4">
<div>
<p class="mb-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-indigo-300/70">News &amp; Editorials</p>
<h2 class="text-xl font-semibold text-white">Related stories &amp; updates</h2>
</div>
<a href="/news" class="shrink-0 text-sm font-medium text-indigo-400/70 transition hover:text-indigo-300">
Browse all <i class="fa-solid fa-arrow-right fa-fw text-xs"></i>
</a>
</div>
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
@foreach($newsResults as $item)
<a href="{{ route('news.show', $item->slug) }}"
class="group flex flex-col gap-3 overflow-hidden rounded-2xl border border-white/[0.07] bg-white/[0.03] p-5 transition-all duration-200 hover:-translate-y-0.5 hover:border-indigo-500/20 hover:bg-white/[0.05] hover:shadow-[0_12px_32px_rgba(0,0,0,0.28)]">
{{-- badges --}}
<div class="flex flex-wrap items-center gap-1.5">
<span class="rounded-full border border-white/[0.08] bg-white/[0.04] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-white/45">{{ $item->type_label }}</span>
@if($item->category)
<span class="rounded-full border border-indigo-400/20 bg-indigo-500/10 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-indigo-200">{{ $item->category->name }}</span>
@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
<h3 class="flex-1 text-[15px] font-semibold leading-snug text-white/90 group-hover:text-white">{{ $item->title }}</h3>
<p class="line-clamp-3 text-[13px] leading-relaxed text-white/40">{{ Str::limit(strip_tags((string) ($item->excerpt ?: $item->rendered_content)), 130) }}</p>
<div class="mt-auto flex items-center justify-between gap-3 border-t border-white/[0.05] pt-3 text-[11px] text-white/30">
<span class="flex items-center gap-1.5">
<i class="fa-solid fa-user fa-fw text-[9px]"></i>
{{ $item->author?->name ?? 'Skinbase' }}
</span>
<span>{{ $item->published_at?->format('d M Y') }}</span>
</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>
</a>
@endforeach
</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="/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
data-react-masonry-gallery
data-artworks="{{ json_encode($galleryArtworks) }}"
data-gallery-type="search"
@if($galleryNextPageUrl) data-next-page-url="{{ $galleryNextPageUrl }}" @endif
data-limit="24"
class="min-h-32"
></div>
@endif
</section>
@endif
</div>
</div>
@endsection