libra/docs/stories/story-5.4-public-posts-disp...

199 lines
4.7 KiB
Markdown

# Story 5.4: Public Posts Display
## Epic Reference
**Epic 5:** Posts/Blog System
## User Story
As a **website visitor**,
I want **to view published blog posts**,
So that **I can read legal updates and articles from the firm**.
## Story Context
### Existing System Integration
- **Integrates with:** posts table (published only)
- **Technology:** Livewire Volt (public routes)
- **Follows pattern:** Public content display
- **Touch points:** Navigation, homepage
## Acceptance Criteria
### Posts Listing Page
- [ ] Public access (no login required)
- [ ] Display in reverse chronological order
- [ ] Each post card shows:
- Title
- Publication date
- Excerpt (first ~150 characters)
- Read more link
- [ ] Pagination for many posts
### Individual Post Page
- [ ] Full post content displayed
- [ ] Clean, readable typography
- [ ] Publication date shown
- [ ] Back to posts list link
### Language Support
- [ ] Content displayed based on current site language
- [ ] Title and body in selected language
- [ ] Date formatting per locale
### Design Requirements
- [ ] Responsive design (mobile-friendly)
- [ ] Professional typography
- [ ] Consistent with brand colors
### Quality Requirements
- [ ] Only published posts visible
- [ ] Fast loading
- [ ] SEO-friendly URLs (optional per PRD)
- [ ] Tests for display logic
## Technical Notes
### Routes
```php
// routes/web.php (public)
Route::get('/posts', \App\Livewire\Posts\Index::class)->name('posts.index');
Route::get('/posts/{post}', \App\Livewire\Posts\Show::class)->name('posts.show');
```
### Posts Index Component
```php
<?php
use App\Models\Post;
use Livewire\Volt\Component;
use Livewire\WithPagination;
new class extends Component {
use WithPagination;
public function with(): array
{
return [
'posts' => Post::published()
->latest()
->paginate(10),
];
}
}; ?>
<div class="max-w-4xl mx-auto">
<flux:heading>{{ __('posts.title') }}</flux:heading>
<div class="mt-8 space-y-6">
@forelse($posts as $post)
<article class="bg-cream p-6 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<h2 class="text-xl font-semibold text-navy">
<a href="{{ route('posts.show', $post) }}" class="hover:text-gold">
{{ $post->title }}
</a>
</h2>
<time class="text-sm text-charcoal/70 mt-2 block">
{{ $post->created_at->translatedFormat('d F Y') }}
</time>
<p class="mt-3 text-charcoal">
{{ $post->excerpt }}
</p>
<a href="{{ route('posts.show', $post) }}" class="text-gold hover:underline mt-4 inline-block">
{{ __('posts.read_more') }} &rarr;
</a>
</article>
@empty
<p class="text-center text-charcoal/70 py-12">
{{ __('posts.no_posts') }}
</p>
@endforelse
</div>
{{ $posts->links() }}
</div>
```
### Post Show Component
```php
<?php
use App\Models\Post;
use Livewire\Volt\Component;
new class extends Component {
public Post $post;
public function mount(Post $post): void
{
// Only show published posts
abort_unless($post->status === 'published', 404);
$this->post = $post;
}
}; ?>
<article class="max-w-3xl mx-auto">
<header class="mb-8">
<flux:heading>{{ $post->title }}</flux:heading>
<time class="text-charcoal/70 mt-2 block">
{{ $post->created_at->translatedFormat('l, d F Y') }}
</time>
</header>
<div class="prose prose-lg prose-navy max-w-none">
{!! $post->body !!}
</div>
<footer class="mt-12 pt-6 border-t border-charcoal/20">
<a href="{{ route('posts.index') }}" class="text-gold hover:underline">
&larr; {{ __('posts.back_to_posts') }}
</a>
</footer>
</article>
```
### Prose Styling
```css
/* In app.css */
.prose-navy {
--tw-prose-headings: theme('colors.navy');
--tw-prose-links: theme('colors.gold');
--tw-prose-bold: theme('colors.navy');
}
.prose-navy a {
text-decoration: underline;
}
.prose-navy a:hover {
color: theme('colors.gold-light');
}
```
## Definition of Done
- [ ] Posts listing page works
- [ ] Individual post page works
- [ ] Only published posts visible
- [ ] Reverse chronological order
- [ ] Excerpts display correctly
- [ ] Full content renders properly
- [ ] Language-appropriate content shown
- [ ] Mobile responsive
- [ ] RTL support
- [ ] Tests pass
- [ ] Code formatted with Pint
## Dependencies
- **Story 5.1:** Post creation
- **Epic 1:** Base UI, navigation
## Estimation
**Complexity:** Medium
**Estimated Effort:** 3-4 hours