Save workspace changes
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
<article class="group overflow-hidden rounded-[28px] border border-white/[0.06] bg-[linear-gradient(180deg,rgba(11,16,26,0.94),rgba(7,11,19,0.92))] shadow-[0_18px_45px_rgba(0,0,0,0.22)] transition hover:-translate-y-0.5 hover:border-white/[0.12]">
|
||||
<a href="{{ route('news.show', $article->slug) }}" class="block">
|
||||
<div class="relative aspect-[16/9] overflow-hidden bg-black/20">
|
||||
@if($article->cover_url)
|
||||
<img src="{{ $article->cover_url }}" alt="{{ $article->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>
|
||||
</a>
|
||||
|
||||
<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>
|
||||
|
||||
<h3 class="mt-3 text-xl font-semibold leading-tight text-white/95">
|
||||
<a href="{{ route('news.show', $article->slug) }}" class="transition hover:text-sky-200">{{ $article->title }}</a>
|
||||
</h3>
|
||||
|
||||
@if($article->excerpt)
|
||||
<p class="mt-3 flex-1 text-sm leading-7 text-white/55">{{ Str::limit(strip_tags((string) $article->excerpt), 135) }}</p>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center justify-between gap-3 text-sm text-white/40">
|
||||
<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) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@@ -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
|
||||
@@ -0,0 +1,58 @@
|
||||
@if(!empty($categories) && $categories->isNotEmpty())
|
||||
<section class="rounded-[24px] border border-white/[0.06] bg-white/[0.025] p-5">
|
||||
<div class="mb-4 flex items-center justify-between gap-3">
|
||||
<h2 class="text-sm font-semibold uppercase tracking-[0.18em] text-white/45">Categories</h2>
|
||||
<span class="text-xs text-white/30">{{ $categories->count() }}</span>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
@foreach($categories as $cat)
|
||||
<a href="{{ route('news.category', $cat->slug) }}" class="flex items-center justify-between rounded-2xl px-3 py-2.5 text-sm text-white/65 transition hover:bg-white/[0.04] hover:text-white">
|
||||
<span>{{ $cat->name }}</span>
|
||||
<span class="rounded-full border border-white/[0.06] bg-white/[0.04] px-2 py-0.5 text-[11px] text-white/45">{{ number_format((int) ($cat->published_articles_count ?? 0)) }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@if(!empty($trending) && $trending->isNotEmpty())
|
||||
<section class="rounded-[24px] border border-white/[0.06] bg-white/[0.025] p-5">
|
||||
<div class="mb-4 flex items-center gap-2 text-sm font-semibold uppercase tracking-[0.18em] text-white/45">
|
||||
<i class="fa-solid fa-fire text-[11px] text-rose-300"></i>
|
||||
Trending
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
@foreach($trending as $item)
|
||||
<a href="{{ route('news.show', $item->slug) }}" class="block rounded-2xl border border-white/[0.04] bg-black/10 px-4 py-3 transition hover:border-white/[0.08] hover:bg-white/[0.03]">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<span class="text-sm font-medium leading-6 text-white/80">{{ Str::limit($item->title, 70) }}</span>
|
||||
<span class="shrink-0 rounded-full border border-sky-400/20 bg-sky-500/10 px-2 py-0.5 text-[11px] text-sky-200">{{ number_format((int) $item->views) }}</span>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-white/35">{{ $item->published_at?->diffForHumans() }}</p>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@if(!empty($tags) && $tags->isNotEmpty())
|
||||
<section class="rounded-[24px] border border-white/[0.06] bg-white/[0.025] p-5">
|
||||
<div class="mb-4 flex items-center gap-2 text-sm font-semibold uppercase tracking-[0.18em] text-white/45">
|
||||
<i class="fa-solid fa-tags text-[11px] text-sky-300"></i>
|
||||
Topics
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@foreach($tags as $tag)
|
||||
<a href="{{ route('news.tag', $tag->slug) }}" class="inline-flex items-center rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1.5 text-xs font-medium text-white/60 transition hover:border-white/[0.14] hover:bg-white/[0.06] hover:text-white">#{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
<section class="rounded-[24px] border border-amber-400/20 bg-amber-500/10 p-5 text-center">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-amber-100/70">Stay updated</p>
|
||||
<a href="{{ route('news.rss') }}" class="mt-3 inline-flex items-center gap-2 rounded-full border border-amber-300/25 bg-amber-500/10 px-4 py-2 text-sm font-medium text-amber-100 transition hover:bg-amber-500/20" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fa-solid fa-rss text-xs"></i>
|
||||
RSS Feed
|
||||
</a>
|
||||
</section>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,44 @@
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => $category->name . ' — News',
|
||||
'metaDescription' => $category->description ?: ('Announcements in the ' . $category->name . ' category.'),
|
||||
'metaCanonical' => route('news.category', $category->slug),
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
@php
|
||||
$headerBreadcrumbs = collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
(object) ['name' => $category->name, 'url' => route('news.category', $category->slug)],
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-nova-page-header
|
||||
section="Community"
|
||||
:title="$category->name"
|
||||
icon="fa-folder-open"
|
||||
:breadcrumbs="$headerBreadcrumbs"
|
||||
:description="$category->description ?: ('Announcements filed under ' . $category->name . '.')"
|
||||
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 in this category 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
|
||||
@@ -0,0 +1,114 @@
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => config('news.rss_title', 'News'),
|
||||
'metaDescription' => config('news.rss_description', ''),
|
||||
'metaCanonical' => route('news.index'),
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
@php
|
||||
$headerBreadcrumbs = collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-nova-page-header
|
||||
section="Community"
|
||||
title="News"
|
||||
icon="fa-newspaper"
|
||||
:breadcrumbs="$headerBreadcrumbs"
|
||||
:description="config('news.rss_description', 'Latest news, feature rollouts, and team updates from Skinbase.')"
|
||||
headerClass="pb-6"
|
||||
>
|
||||
<x-slot name="actions">
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm">
|
||||
@if(($articles->total() ?? 0) > 0)
|
||||
<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 text-white/65">
|
||||
<i class="fa-solid fa-file-lines text-xs text-sky-300"></i>
|
||||
{{ number_format($articles->total()) }} articles
|
||||
</span>
|
||||
@endif
|
||||
<a href="{{ route('news.rss') }}" class="inline-flex items-center gap-2 rounded-lg border border-amber-400/20 bg-amber-500/10 px-4 py-2 text-sm font-medium text-amber-200 transition hover:bg-amber-500/15">
|
||||
<i class="fa-solid fa-rss text-xs"></i>
|
||||
RSS feed
|
||||
</a>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-nova-page-header>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-6 pt-8 pb-16 md:px-10">
|
||||
@if($featured)
|
||||
<section class="mb-8">
|
||||
<a href="{{ route('news.show', $featured->slug) }}" class="group block overflow-hidden rounded-[32px] border border-white/[0.08] bg-white/[0.03] shadow-[0_24px_60px_rgba(0,0,0,0.24)] transition hover:border-white/[0.12]">
|
||||
<div class="grid lg:grid-cols-[1.25fr_0.95fr]">
|
||||
<div class="relative min-h-[280px] overflow-hidden bg-black/20">
|
||||
@if($featured->cover_url)
|
||||
<img src="{{ $featured->cover_url }}" alt="{{ $featured->title }}" class="h-full w-full object-cover transition duration-500 group-hover:scale-[1.03]">
|
||||
@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.96),rgba(2,6,23,0.98))]"></div>
|
||||
@endif
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-[#020611] via-[#02061166] to-transparent"></div>
|
||||
</div>
|
||||
<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->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
|
||||
</div>
|
||||
<h2 class="mt-4 text-3xl font-bold leading-tight text-white/95">{{ $featured->title }}</h2>
|
||||
@if($featured->excerpt)
|
||||
<p class="mt-4 text-sm leading-7 text-white/60">{{ Str::limit(strip_tags((string) $featured->excerpt), 220) }}</p>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-white/45">
|
||||
<span>{{ $featured->author?->name ?? 'Skinbase' }}</span>
|
||||
<span>{{ $featured->published_at?->format('d M Y') }}</span>
|
||||
<span>{{ $featured->reading_time }} min read</span>
|
||||
<span>{{ number_format((int) $featured->views) }} views</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</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())
|
||||
<div class="rounded-[28px] border border-white/[0.06] bg-white/[0.025] px-8 py-14 text-center text-white/45">
|
||||
No announcements have been published 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
|
||||
@@ -0,0 +1,15 @@
|
||||
@php
|
||||
$useUnifiedSeo = true;
|
||||
@endphp
|
||||
@extends('layouts.nova')
|
||||
|
||||
@php
|
||||
$page_title = $metaTitle ?? config('news.rss_title', 'News');
|
||||
$page_meta_description = $metaDescription ?? config('news.rss_description', 'Latest announcements and community updates from Skinbase.');
|
||||
$page_canonical = $metaCanonical ?? url()->current();
|
||||
$page_robots = $metaRobots ?? 'index,follow';
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
@yield('news_content')
|
||||
@endsection
|
||||
@@ -0,0 +1,187 @@
|
||||
@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' => $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,
|
||||
'og_image' => $article->effective_og_image,
|
||||
'breadcrumbs' => collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
$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)],
|
||||
])->filter()->values(),
|
||||
])
|
||||
->addJsonLd(array_filter([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'Article',
|
||||
'headline' => $article->title,
|
||||
'description' => $article->meta_description ?: Str::limit(strip_tags((string) $article->excerpt), 160),
|
||||
'image' => $article->effective_og_image,
|
||||
'datePublished' => $article->published_at?->toIso8601String(),
|
||||
'dateModified' => $article->updated_at?->toIso8601String(),
|
||||
'author' => array_filter([
|
||||
'@type' => 'Person',
|
||||
'name' => $article->author?->name,
|
||||
]),
|
||||
'mainEntityOfPage' => $articleUrl,
|
||||
], fn (mixed $value): bool => $value !== null && $value !== ''))
|
||||
->build();
|
||||
@endphp
|
||||
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => $article->meta_title ?: $article->title,
|
||||
'metaDescription' => $article->meta_description ?: Str::limit(strip_tags((string)$article->excerpt), 160),
|
||||
'metaCanonical' => $isPreview ? $articleUrl : ($article->canonical_url ?: $articleUrl),
|
||||
'metaRobots' => $isPreview ? 'noindex,nofollow' : 'index,follow',
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
|
||||
@php
|
||||
$headerBreadcrumbs = collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
$article->category
|
||||
? (object) ['name' => $article->category->name, 'url' => route('news.category', $article->category->slug)]
|
||||
: null,
|
||||
(object) ['name' => $article->title, 'url' => $articleUrl],
|
||||
])->filter()->values();
|
||||
@endphp
|
||||
|
||||
<x-nova-page-header
|
||||
section="Community"
|
||||
:title="$article->title"
|
||||
icon="fa-newspaper"
|
||||
:breadcrumbs="$headerBreadcrumbs"
|
||||
:description="$article->excerpt ? Str::limit(strip_tags((string) $article->excerpt), 180) : 'Latest Skinbase announcement and community update.'"
|
||||
headerClass="pb-6"
|
||||
>
|
||||
<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
|
||||
</span>
|
||||
</div>
|
||||
</x-slot>
|
||||
</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)
|
||||
<div class="overflow-hidden rounded-[32px] border border-white/[0.06] bg-black/20 shadow-[0_24px_60px_rgba(0,0,0,0.24)]">
|
||||
<img src="{{ $article->cover_url }}" alt="{{ $article->title }}" class="h-auto max-h-[520px] w-full object-cover">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<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>
|
||||
@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>
|
||||
|
||||
@if($article->excerpt)
|
||||
<p class="mt-5 text-lg leading-8 text-white/65">{{ $article->excerpt }}</p>
|
||||
@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->rendered_content !!}
|
||||
</div>
|
||||
|
||||
@if($article->tags->isNotEmpty())
|
||||
<div class="mt-8 flex flex-wrap gap-2 border-t border-white/[0.06] pt-6">
|
||||
@foreach($article->tags as $tag)
|
||||
<a href="{{ route('news.tag', $tag->slug) }}" class="inline-flex items-center rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1.5 text-xs font-medium text-white/60 transition hover:border-white/[0.14] hover:bg-white/[0.06] hover:text-white">#{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-8 flex flex-wrap items-center gap-3 border-t border-white/[0.06] pt-6">
|
||||
<span class="text-sm font-medium text-white/55">Share</span>
|
||||
<a href="https://twitter.com/intent/tweet?url={{ urlencode(url()->current()) }}&text={{ urlencode($article->title) }}" class="inline-flex items-center gap-2 rounded-full border border-sky-400/20 bg-sky-500/10 px-4 py-2 text-sm text-sky-200 transition hover:bg-sky-500/15" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fab fa-twitter text-xs"></i>
|
||||
Twitter
|
||||
</a>
|
||||
<a href="https://www.facebook.com/sharer/sharer.php?u={{ urlencode(url()->current()) }}" class="inline-flex items-center gap-2 rounded-full border border-blue-400/20 bg-blue-500/10 px-4 py-2 text-sm text-blue-200 transition hover:bg-blue-500/15" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fab fa-facebook text-xs"></i>
|
||||
Facebook
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if($article->forum_thread_id)
|
||||
<div class="mt-6 rounded-2xl border border-emerald-400/20 bg-emerald-500/10 px-4 py-4 text-sm text-emerald-100/90">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<i class="fa-solid fa-comments text-xs"></i>
|
||||
<span class="font-medium">Join the discussion</span>
|
||||
</div>
|
||||
<a href="{{ url('/forum/thread/discussion-' . $article->slug) }}" class="mt-2 inline-flex items-center gap-2 text-emerald-200 transition hover:text-white">
|
||||
Discussion: {{ $article->title }}
|
||||
<i class="fa-solid fa-arrow-right text-[11px]"></i>
|
||||
</a>
|
||||
</div>
|
||||
@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">
|
||||
<h2 class="text-lg font-semibold text-white/90">Related Articles</h2>
|
||||
</div>
|
||||
<div class="grid gap-5 md:grid-cols-2">
|
||||
@foreach($related as $rel)
|
||||
@include('news._article_card', ['article' => $rel])
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
</article>
|
||||
|
||||
<aside class="space-y-4">
|
||||
@include('news._sidebar', ['categories' => $categories, 'trending' => $trending, 'tags' => $tags])
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,44 @@
|
||||
@extends('news.layout', [
|
||||
'metaTitle' => '#' . $tag->name . ' — News',
|
||||
'metaDescription' => 'Announcements tagged with ' . $tag->name . '.',
|
||||
'metaCanonical' => route('news.tag', $tag->slug),
|
||||
])
|
||||
|
||||
@section('news_content')
|
||||
@php
|
||||
$headerBreadcrumbs = collect([
|
||||
(object) ['name' => 'Community', 'url' => route('community.activity')],
|
||||
(object) ['name' => 'Announcements', 'url' => route('news.index')],
|
||||
(object) ['name' => '#' . $tag->name, 'url' => route('news.tag', $tag->slug)],
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-nova-page-header
|
||||
section="Community"
|
||||
:title="'#' . $tag->name"
|
||||
icon="fa-tag"
|
||||
:breadcrumbs="$headerBreadcrumbs"
|
||||
:description="'Stories and announcements tagged with #' . $tag->name . '.'"
|
||||
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 are using this tag 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
|
||||
Reference in New Issue
Block a user