Files
fitness/resources/views/admin/posts/_form.blade.php
T
rootandClaude Sonnet 5 4686dea609 Add blog feature and fix broken test/build infra
BlogController was an empty stub with no view, so /blog 500'd on every
visit. Adds a real Post model, public listing/detail pages, and an admin
CRUD screen mirroring the existing Exercise admin pattern (image upload
or URL, auto-slug, publish toggle). Seeds 5 real posts so it's not empty
on launch.

Also fixes two things that were silently broken: phpunit.xml pointed
DB_DATABASE at a non-existent "testing" file with no DB_CONNECTION set,
and the stock ExampleTest had RefreshDatabase commented out - both meant
the test suite never actually ran successfully.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 11:36:31 +03:00

61 lines
4.9 KiB
PHP

@csrf
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Title</label>
<input type="text" name="title" value="{{ old('title', $post->title ?? '') }}" required class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2">
<x-input-error :messages="$errors->get('title')" class="mt-1" />
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Slug (optional)</label>
<input type="text" name="slug" value="{{ old('slug', $post->slug ?? '') }}" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2">
<p class="mt-1 text-xs text-gray-500">Leave empty to auto-generate from the title.</p>
<x-input-error :messages="$errors->get('slug')" class="mt-1" />
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Excerpt</label>
<textarea name="excerpt" rows="2" maxlength="500" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2">{{ old('excerpt', $post->excerpt ?? '') }}</textarea>
<p class="mt-1 text-xs text-gray-500">Short summary shown on the blog listing page.</p>
<x-input-error :messages="$errors->get('excerpt')" class="mt-1" />
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Cover image</label>
<div class="mt-1 grid grid-cols-1 gap-3 sm:grid-cols-2">
<div>
<input type="file" name="cover_image_file" accept="image/*" class="block w-full text-sm text-gray-900 dark:text-gray-100 file:mr-4 file:rounded-md file:border-0 file:bg-indigo-50 file:px-3 file:py-2 file:text-sm file:font-semibold file:text-indigo-700 hover:file:bg-indigo-100 dark:file:bg-indigo-900/30 dark:file:text-indigo-200" />
<p class="mt-1 text-xs text-gray-500">Upload a new image (JPG, PNG, WebP, up to 8MB). If provided, it will override the URL below.</p>
<x-input-error :messages="$errors->get('cover_image_file')" class="mt-1" />
</div>
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400">Or use an external URL</label>
<input type="url" name="cover_image" value="{{ old('cover_image', $post->cover_image ?? '') }}" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2" placeholder="https://...">
<x-input-error :messages="$errors->get('cover_image')" class="mt-1" />
</div>
</div>
@php
$cover = old('cover_image', $post->cover_image ?? '');
$isHttp = is_string($cover) && preg_match('/^https?:\/\//i', $cover);
$src = $isHttp ? $cover : ($cover ? \Illuminate\Support\Facades\Storage::url($cover) : '');
@endphp
@if(!empty($src))
<div class="mt-2">
<img src="{{ $src }}" alt="Cover preview" class="h-32 w-auto rounded-lg border border-gray-200 dark:border-gray-700 object-cover">
</div>
@endif
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Body</label>
<textarea name="body" rows="12" required class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2">{{ old('body', $post->body ?? '') }}</textarea>
<p class="mt-1 text-xs text-gray-500">Plain text, one paragraph per line.</p>
<x-input-error :messages="$errors->get('body')" class="mt-1" />
</div>
<div class="flex items-center">
<input id="published" name="published" type="checkbox" value="1" @checked(old('published', $post->is_published ?? false)) class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
<label for="published" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Published</label>
<x-input-error :messages="$errors->get('published')" class="mt-2" />
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<a href="{{ route('admin.posts.index') }}" class="text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Cancel</a>
<button type="submit" class="inline-flex items-center rounded-xl bg-gradient-to-r from-fuchsia-600 to-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow hover:from-fuchsia-500 hover:to-indigo-500">Save</button>
</div>