first commit
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'BreadcrumbList',
|
||||
'itemListElement' => [
|
||||
['@type' => 'ListItem', 'position' => 1, 'name' => 'Blog', 'item' => route('blog.index')],
|
||||
['@type' => 'ListItem', 'position' => 2, 'name' => $category->name, 'item' => $canonical],
|
||||
],
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<p class="text-sm text-neutral-500">
|
||||
<a href="{{ route('blog.index') }}" class="hover:underline">Blog</a>
|
||||
<span aria-hidden="true">/</span>
|
||||
{{ $category->name }}
|
||||
</p>
|
||||
<h1 class="mt-2 text-3xl font-bold tracking-tight">{{ $category->name }}</h1>
|
||||
@if ($category->description)
|
||||
<p class="mt-2 text-neutral-600">{{ $category->description }}</p>
|
||||
@endif
|
||||
|
||||
<div class="mt-8">
|
||||
@forelse ($posts as $post)
|
||||
<x-post-card :post="$post" />
|
||||
@empty
|
||||
<p class="text-neutral-500">No posts in this category yet.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ $posts->links() }}
|
||||
</div>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ config('app.name') }}</title>
|
||||
<link>{{ route('blog.index') }}</link>
|
||||
<atom:link href="{{ route('blog.feed') }}" rel="self" type="application/rss+xml" />
|
||||
<description>Latest articles from {{ config('app.name') }}.</description>
|
||||
<language>{{ str_replace('_', '-', app()->getLocale()) }}</language>
|
||||
@foreach ($posts as $post)
|
||||
<item>
|
||||
<title>{{ $post->title }}</title>
|
||||
<link>{{ route('blog.show', $post) }}</link>
|
||||
<guid isPermaLink="true">{{ route('blog.show', $post) }}</guid>
|
||||
<pubDate>{{ $post->published_at->toRfc2822String() }}</pubDate>
|
||||
<description>{{ $post->excerpt_or_summary }}</description>
|
||||
</item>
|
||||
@endforeach
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -0,0 +1,26 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'Blog',
|
||||
'name' => config('app.name'),
|
||||
'url' => $canonical,
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<h1 class="text-3xl font-bold tracking-tight">Blog</h1>
|
||||
|
||||
<div class="mt-8">
|
||||
@forelse ($posts as $post)
|
||||
<x-post-card :post="$post" />
|
||||
@empty
|
||||
<p class="text-neutral-500">No posts published yet.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ $posts->links() }}
|
||||
</div>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,57 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical" :image="$image ?? null" type="article">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'BlogPosting',
|
||||
'headline' => $post->title,
|
||||
'description' => $description,
|
||||
'datePublished' => $post->published_at->toIso8601String(),
|
||||
'dateModified' => $post->updated_at->toIso8601String(),
|
||||
'author' => [
|
||||
'@type' => 'Person',
|
||||
'name' => $post->user->name,
|
||||
],
|
||||
'mainEntityOfPage' => [
|
||||
'@type' => 'WebPage',
|
||||
'@id' => $canonical,
|
||||
],
|
||||
...($image ?? null ? ['image' => [$image]] : []),
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<article>
|
||||
<p class="text-sm text-neutral-500">
|
||||
<a href="{{ route('blog.index') }}" class="hover:underline">Blog</a>
|
||||
@if ($post->category)
|
||||
<span aria-hidden="true">/</span>
|
||||
<a href="{{ route('blog.category', $post->category) }}" class="hover:underline">{{ $post->category->name }}</a>
|
||||
@endif
|
||||
</p>
|
||||
|
||||
<h1 class="mt-2 text-4xl font-bold tracking-tight">{{ $post->title }}</h1>
|
||||
|
||||
<div class="mt-3 flex flex-wrap items-center gap-3 text-sm text-neutral-500">
|
||||
<time datetime="{{ $post->published_at->toIso8601String() }}">{{ $post->published_at->format('F j, Y') }}</time>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ $post->user->name }}</span>
|
||||
</div>
|
||||
|
||||
@if ($post->featured_image)
|
||||
<img src="{{ asset('storage/'.$post->featured_image) }}" alt="{{ $post->title }}" class="mt-8 w-full rounded-lg">
|
||||
@endif
|
||||
|
||||
<div class="prose prose-neutral mt-8 max-w-none">
|
||||
{!! $post->body_html !!}
|
||||
</div>
|
||||
|
||||
@if ($post->tags->isNotEmpty())
|
||||
<div class="mt-10 flex flex-wrap gap-2 border-t border-neutral-200 pt-6">
|
||||
@foreach ($post->tags as $tag)
|
||||
<a href="{{ route('blog.tag', $tag) }}" class="rounded-full bg-neutral-100 px-3 py-1 text-sm hover:bg-neutral-200">#{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</article>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,33 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'BreadcrumbList',
|
||||
'itemListElement' => [
|
||||
['@type' => 'ListItem', 'position' => 1, 'name' => 'Blog', 'item' => route('blog.index')],
|
||||
['@type' => 'ListItem', 'position' => 2, 'name' => '#'.$tag->name, 'item' => $canonical],
|
||||
],
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<p class="text-sm text-neutral-500">
|
||||
<a href="{{ route('blog.index') }}" class="hover:underline">Blog</a>
|
||||
<span aria-hidden="true">/</span>
|
||||
#{{ $tag->name }}
|
||||
</p>
|
||||
<h1 class="mt-2 text-3xl font-bold tracking-tight">#{{ $tag->name }}</h1>
|
||||
|
||||
<div class="mt-8">
|
||||
@forelse ($posts as $post)
|
||||
<x-post-card :post="$post" />
|
||||
@empty
|
||||
<p class="text-neutral-500">No posts tagged with #{{ $tag->name }} yet.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ $posts->links() }}
|
||||
</div>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
@php
|
||||
$seoTitle = $title ?? config('app.name');
|
||||
$seoDescription = $description ?? 'A blog powered by ' . config('app.name') . '.';
|
||||
$seoCanonical = $canonical ?? url()->current();
|
||||
$seoImage = $image ?? null;
|
||||
$seoType = $type ?? 'website';
|
||||
@endphp
|
||||
|
||||
<title>{{ $seoTitle }}</title>
|
||||
<meta name="description" content="{{ $seoDescription }}">
|
||||
<link rel="canonical" href="{{ $seoCanonical }}">
|
||||
|
||||
{{-- Open Graph --}}
|
||||
<meta property="og:site_name" content="{{ config('app.name') }}">
|
||||
<meta property="og:type" content="{{ $seoType }}">
|
||||
<meta property="og:title" content="{{ $seoTitle }}">
|
||||
<meta property="og:description" content="{{ $seoDescription }}">
|
||||
<meta property="og:url" content="{{ $seoCanonical }}">
|
||||
@if ($seoImage)
|
||||
<meta property="og:image" content="{{ $seoImage }}">
|
||||
@endif
|
||||
|
||||
{{-- Twitter --}}
|
||||
<meta name="twitter:card" content="{{ $seoImage ? 'summary_large_image' : 'summary' }}">
|
||||
<meta name="twitter:title" content="{{ $seoTitle }}">
|
||||
<meta name="twitter:description" content="{{ $seoDescription }}">
|
||||
@if ($seoImage)
|
||||
<meta name="twitter:image" content="{{ $seoImage }}">
|
||||
@endif
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ config('app.name') }} Feed" href="{{ route('blog.feed') }}">
|
||||
<link rel="icon" type="image/svg+xml" href="{{ asset('favicon.svg') }}">
|
||||
|
||||
@stack('jsonld')
|
||||
|
||||
@fonts
|
||||
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="min-h-screen bg-white text-neutral-900 antialiased">
|
||||
<header class="border-b border-neutral-200">
|
||||
<div class="mx-auto flex max-w-3xl items-center justify-between px-6 py-6">
|
||||
<a href="{{ route('blog.index') }}" class="flex shrink-0 items-center gap-2 text-lg font-semibold tracking-tight">
|
||||
<x-logo class="h-8 w-8 shrink-0 text-neutral-900" />
|
||||
<span class="whitespace-nowrap">{{ config('app.name') }}</span>
|
||||
</a>
|
||||
<nav class="flex shrink-0 gap-6 text-sm text-neutral-600">
|
||||
<a href="{{ route('blog.index') }}" class="hover:text-neutral-900 hover:underline underline-offset-4">Blog</a>
|
||||
<a href="{{ route('tools.index') }}" class="hover:text-neutral-900 hover:underline underline-offset-4">Tools</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="mx-auto max-w-3xl px-6 py-10">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
|
||||
<footer class="mt-16 border-t border-neutral-200">
|
||||
<div class="mx-auto flex max-w-3xl flex-col gap-4 px-6 py-8 text-sm text-neutral-500 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p>© {{ now()->year }} {{ config('app.name') }}</p>
|
||||
|
||||
@if (($footerPages ?? collect())->isNotEmpty())
|
||||
<nav class="flex flex-wrap gap-x-6 gap-y-2">
|
||||
@foreach ($footerPages as $footerPage)
|
||||
<a href="{{ route('pages.show', $footerPage) }}" class="hover:text-neutral-900 hover:underline underline-offset-4">
|
||||
{{ $footerPage->title }}
|
||||
</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
@props(['class' => 'h-8 w-8'])
|
||||
|
||||
<svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg" {{ $attributes->merge(['class' => $class]) }}>
|
||||
{{-- antenna --}}
|
||||
<line x1="32" y1="16" x2="32" y2="7" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||
<circle cx="32" cy="5.5" r="4" fill="currentColor" />
|
||||
|
||||
{{-- ears --}}
|
||||
<rect x="3" y="27" width="8" height="15" rx="4" fill="currentColor" />
|
||||
<rect x="53" y="27" width="8" height="15" rx="4" fill="currentColor" />
|
||||
|
||||
{{-- head --}}
|
||||
<rect x="10" y="15" width="44" height="38" rx="15" fill="currentColor" />
|
||||
|
||||
{{-- open eye --}}
|
||||
<circle cx="24" cy="34" r="4.5" fill="white" />
|
||||
|
||||
{{-- winking eye --}}
|
||||
<path d="M 34 34 Q 39.5 29.5 45 34" stroke="white" stroke-width="3.25" stroke-linecap="round" fill="none" />
|
||||
|
||||
{{-- smile --}}
|
||||
<path d="M 23 45 Q 32 51.5 41 45" stroke="white" stroke-width="3.25" stroke-linecap="round" fill="none" />
|
||||
</svg>
|
||||
@@ -0,0 +1,35 @@
|
||||
<article class="flex gap-5 border-b border-neutral-200 py-8 first:pt-0">
|
||||
@if ($post->featured_image)
|
||||
<a href="{{ route('blog.show', $post) }}" class="hidden shrink-0 sm:block">
|
||||
<img
|
||||
src="{{ asset('storage/'.$post->featured_image) }}"
|
||||
alt="{{ $post->title }}"
|
||||
class="h-28 w-40 rounded-lg object-cover"
|
||||
loading="lazy"
|
||||
>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<div class="min-w-0">
|
||||
@if ($post->category)
|
||||
<a href="{{ route('blog.category', $post->category) }}" class="text-xs font-medium uppercase tracking-wide text-neutral-500 hover:text-neutral-900">
|
||||
{{ $post->category->name }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<h2 class="mt-2 text-2xl font-semibold tracking-tight">
|
||||
<a href="{{ route('blog.show', $post) }}" class="hover:underline">{{ $post->title }}</a>
|
||||
</h2>
|
||||
|
||||
<p class="mt-2 text-neutral-600">{{ $post->excerpt_or_summary }}</p>
|
||||
|
||||
<div class="mt-4 flex flex-wrap items-center gap-3 text-sm text-neutral-500">
|
||||
<time datetime="{{ $post->published_at->toIso8601String() }}">{{ $post->published_at->format('M j, Y') }}</time>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ $post->user->name }}</span>
|
||||
@foreach ($post->tags as $tag)
|
||||
<a href="{{ route('blog.tag', $tag) }}" class="rounded-full bg-neutral-100 px-2 py-0.5 text-xs hover:bg-neutral-200">#{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@@ -0,0 +1,34 @@
|
||||
<article class="flex flex-col rounded-lg border border-neutral-200 p-5">
|
||||
<div class="flex items-center gap-3">
|
||||
@if ($tool->logo_url)
|
||||
<img src="{{ $tool->logo_url }}" alt="{{ $tool->name }} logo" class="h-10 w-10 rounded-md object-cover" loading="lazy">
|
||||
@endif
|
||||
<div>
|
||||
<h3 class="font-semibold leading-tight">
|
||||
<a href="{{ route('tools.show', $tool) }}" class="hover:underline">{{ $tool->name }}</a>
|
||||
</h3>
|
||||
<a href="{{ route('tools.category', $tool->category) }}" class="text-xs text-neutral-500 hover:text-neutral-900">
|
||||
{{ $tool->category_label }}@if ($tool->subcategory) · {{ $tool->subcategory }} @endif
|
||||
</a>
|
||||
</div>
|
||||
@if ($tool->featured)
|
||||
<span class="ml-auto rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800">Featured</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<p class="mt-3 flex-1 text-sm text-neutral-600">{{ str($tool->description)->limit(120) }}</p>
|
||||
|
||||
<div class="mt-4 flex flex-wrap items-center justify-between gap-2">
|
||||
@if ($tool->pricing)
|
||||
<span class="rounded-full bg-neutral-100 px-2 py-0.5 text-xs text-neutral-700">{{ $tool->pricing }}</span>
|
||||
@endif
|
||||
<a
|
||||
href="{{ route('tools.visit', $tool) }}"
|
||||
target="_blank"
|
||||
rel="nofollow noopener sponsored"
|
||||
class="ml-auto text-sm font-medium text-neutral-900 hover:underline"
|
||||
>
|
||||
Visit site →
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
@@ -0,0 +1,21 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'WebPage',
|
||||
'name' => $page->title,
|
||||
'description' => $description,
|
||||
'url' => $canonical,
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<article>
|
||||
<h1 class="text-3xl font-bold tracking-tight">{{ $page->title }}</h1>
|
||||
|
||||
<div class="prose prose-neutral mt-8 max-w-none">
|
||||
{!! $page->body_html !!}
|
||||
</div>
|
||||
</article>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>{{ route('blog.index') }}</loc>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>{{ route('tools.index') }}</loc>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@foreach ($categories as $category)
|
||||
<url>
|
||||
<loc>{{ route('blog.category', $category) }}</loc>
|
||||
<lastmod>{{ $category->updated_at->toAtomString() }}</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
@endforeach
|
||||
@foreach ($tags as $tag)
|
||||
<url>
|
||||
<loc>{{ route('blog.tag', $tag) }}</loc>
|
||||
<lastmod>{{ $tag->updated_at->toAtomString() }}</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
@endforeach
|
||||
@foreach ($posts as $post)
|
||||
<url>
|
||||
<loc>{{ route('blog.show', $post) }}</loc>
|
||||
<lastmod>{{ $post->updated_at->toAtomString() }}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
@endforeach
|
||||
@foreach ($toolCategories as $toolCategory)
|
||||
<url>
|
||||
<loc>{{ route('tools.category', $toolCategory) }}</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
@endforeach
|
||||
@foreach ($tools as $tool)
|
||||
<url>
|
||||
<loc>{{ route('tools.show', $tool) }}</loc>
|
||||
<lastmod>{{ $tool->updated_at->toAtomString() }}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
@endforeach
|
||||
@foreach ($pages as $page)
|
||||
<url>
|
||||
<loc>{{ route('pages.show', $page) }}</loc>
|
||||
<lastmod>{{ $page->updated_at->toAtomString() }}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
@endforeach
|
||||
</urlset>
|
||||
@@ -0,0 +1,47 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'BreadcrumbList',
|
||||
'itemListElement' => [
|
||||
['@type' => 'ListItem', 'position' => 1, 'name' => 'AI Tools', 'item' => route('tools.index')],
|
||||
['@type' => 'ListItem', 'position' => 2, 'name' => $label, 'item' => $canonical],
|
||||
],
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<p class="text-sm text-neutral-500">
|
||||
<a href="{{ route('tools.index') }}" class="hover:underline">AI Tools</a>
|
||||
<span aria-hidden="true">/</span>
|
||||
{{ $label }}
|
||||
</p>
|
||||
<h1 class="mt-2 text-3xl font-bold tracking-tight">{{ $label }} AI Tools</h1>
|
||||
|
||||
<nav class="mt-4 flex flex-wrap gap-2">
|
||||
<a href="{{ route('tools.index') }}" class="rounded-full bg-neutral-100 px-3 py-1 text-sm text-neutral-700 hover:bg-neutral-200">
|
||||
All
|
||||
</a>
|
||||
@foreach ($categories as $slug => $categoryLabel)
|
||||
<a
|
||||
href="{{ route('tools.category', $slug) }}"
|
||||
class="rounded-full px-3 py-1 text-sm {{ $category === $slug ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-700 hover:bg-neutral-200' }}"
|
||||
>
|
||||
{{ $categoryLabel }}
|
||||
</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
|
||||
<div class="mt-8 grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
@forelse ($tools as $tool)
|
||||
<x-tool-card :tool="$tool" />
|
||||
@empty
|
||||
<p class="text-neutral-500 sm:col-span-2">No tools in this category yet.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ $tools->links() }}
|
||||
</div>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,54 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'CollectionPage',
|
||||
'name' => 'AI Tools Directory',
|
||||
'url' => $canonical,
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<h1 class="text-3xl font-bold tracking-tight">AI Tools Directory</h1>
|
||||
<p class="mt-2 text-neutral-600">A curated list of AI tools for developers, designers, marketers, and more.</p>
|
||||
|
||||
<form action="{{ route('tools.index') }}" method="GET" class="mt-6">
|
||||
<input
|
||||
type="search"
|
||||
name="q"
|
||||
value="{{ $search }}"
|
||||
placeholder="Search tools…"
|
||||
class="w-full rounded-lg border border-neutral-300 px-4 py-2 text-sm focus:border-neutral-900 focus:outline-none"
|
||||
>
|
||||
</form>
|
||||
|
||||
<nav class="mt-4 flex flex-wrap gap-2">
|
||||
<a
|
||||
href="{{ route('tools.index') }}"
|
||||
class="rounded-full px-3 py-1 text-sm {{ $activeCategory === '' ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-700 hover:bg-neutral-200' }}"
|
||||
>
|
||||
All
|
||||
</a>
|
||||
@foreach ($categories as $slug => $label)
|
||||
<a
|
||||
href="{{ route('tools.category', $slug) }}"
|
||||
class="rounded-full px-3 py-1 text-sm {{ $activeCategory === $slug ? 'bg-neutral-900 text-white' : 'bg-neutral-100 text-neutral-700 hover:bg-neutral-200' }}"
|
||||
>
|
||||
{{ $label }}
|
||||
</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
|
||||
<div class="mt-8 grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
@forelse ($tools as $tool)
|
||||
<x-tool-card :tool="$tool" />
|
||||
@empty
|
||||
<p class="text-neutral-500 sm:col-span-2">No tools found.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ $tools->links() }}
|
||||
</div>
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,76 @@
|
||||
<x-layouts.app :title="$title" :description="$description" :canonical="$canonical" :image="$image ?? null">
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{!! json_encode([
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'SoftwareApplication',
|
||||
'name' => $tool->name,
|
||||
'description' => $description,
|
||||
'url' => $tool->url,
|
||||
...($tool->logo_url ? ['image' => $tool->logo_url] : []),
|
||||
'applicationCategory' => $tool->category_label,
|
||||
...($tool->pricing ? ['offers' => ['@type' => 'Offer', 'price' => $tool->pricing]] : []),
|
||||
]) !!}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
<p class="text-sm text-neutral-500">
|
||||
<a href="{{ route('tools.index') }}" class="hover:underline">AI Tools</a>
|
||||
<span aria-hidden="true">/</span>
|
||||
<a href="{{ route('tools.category', $tool->category) }}" class="hover:underline">{{ $tool->category_label }}</a>
|
||||
</p>
|
||||
|
||||
<div class="mt-4 flex items-start gap-4">
|
||||
@if ($tool->logo_url)
|
||||
<img src="{{ $tool->logo_url }}" alt="{{ $tool->name }} logo" class="h-16 w-16 rounded-lg object-cover">
|
||||
@endif
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold tracking-tight">{{ $tool->name }}</h1>
|
||||
<p class="mt-1 text-sm text-neutral-500">
|
||||
{{ $tool->category_label }}@if ($tool->subcategory) · {{ $tool->subcategory }} @endif
|
||||
@if ($tool->pricing) · {{ $tool->pricing }} @endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="{{ route('tools.visit', $tool) }}"
|
||||
target="_blank"
|
||||
rel="nofollow noopener sponsored"
|
||||
class="mt-6 inline-block rounded-lg bg-neutral-900 px-5 py-2.5 text-sm font-medium text-white hover:bg-neutral-700"
|
||||
>
|
||||
Visit {{ $tool->name }} →
|
||||
</a>
|
||||
|
||||
<p class="mt-8 text-neutral-700">{{ $tool->description }}</p>
|
||||
|
||||
@if (! empty($tool->features))
|
||||
<div class="mt-8">
|
||||
<h2 class="text-lg font-semibold">Features</h2>
|
||||
<ul class="mt-3 list-inside list-disc space-y-1 text-neutral-700">
|
||||
@foreach ($tool->features as $feature)
|
||||
<li>{{ $feature }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (! empty($tool->tags))
|
||||
<div class="mt-8 flex flex-wrap gap-2">
|
||||
@foreach ($tool->tags as $tag)
|
||||
<span class="rounded-full bg-neutral-100 px-3 py-1 text-sm text-neutral-700">#{{ $tag }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($related->isNotEmpty())
|
||||
<div class="mt-12 border-t border-neutral-200 pt-8">
|
||||
<h2 class="text-lg font-semibold">More {{ $tool->category_label }} tools</h2>
|
||||
<div class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
@foreach ($related as $relatedTool)
|
||||
<x-tool-card :tool="$relatedTool" />
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</x-layouts.app>
|
||||
Reference in New Issue
Block a user