libra/docs/stories/story-14.5-latest-posts-sec...

6.7 KiB

Story 14.5: Latest Posts Section

Story

As a website visitor I want to see recent legal articles and updates on the home page So that I can access valuable legal content and see that the firm is active

Acceptance Criteria

AC1: Section Container

Given a visitor scrolls to the latest posts section When viewing the section Then display with:

  • Warm Cream (#F4F1EA) background
  • Section ID id="posts" for anchor linking
  • Adequate padding (64px vertical on desktop)

AC2: Section Heading

Given the latest posts section When displayed Then show a section heading:

  • English: "Latest Articles" or "Legal Insights"
  • Arabic: "أحدث المقالات" or "رؤى قانونية"
  • Typography: H2, SemiBold, Forest Green (#2D322A)
  • Centered alignment

AC3: Display 3 Latest Posts

Given the posts section When posts exist in the database Then display the 3 most recent published posts showing:

  • Post title
  • Publication date
  • Brief excerpt (first 100-150 characters of body)
  • "Read More" link

AC4: Post Card Design

Given each post card When displayed Then include:

  • Card background: White (#FFFFFF)
  • Title: Bold, Forest Green (#2D322A), clickable link
  • Date: Small text, muted color
  • Excerpt: Body text, truncated with ellipsis
  • "Read More" link: Warm Gold (#A68966)
  • Subtle shadow and border-radius
  • Hover effect on card or title

AC5: Empty State

Given the posts section When no published posts exist Then either:

  • Hide the entire section, OR
  • Show a message: "Articles coming soon" / "المقالات قريباً"

Given the posts section When posts are displayed Then show a "View All Articles" / "عرض جميع المقالات" link:

  • Links to /posts
  • Styled as secondary button or text link with arrow
  • Positioned below the post cards, centered

AC7: Grid Layout

Given different screen sizes When viewing the posts section:

  • Desktop (≥992px): 3 columns (1 row of 3 cards)
  • Tablet (576-991px): 2 columns + 1 below, or 3 columns
  • Mobile (<576px): 1 column (3 rows, stacked)

AC8: RTL Support

Given Arabic language is selected When viewing the posts section Then cards and text align right-to-left correctly

AC9: Dynamic Data

Given the posts section When rendered Then query posts from database:

Post::where('status', 'published')
    ->latest()
    ->take(3)
    ->get()

Technical Notes

Files to Modify

  • resources/views/pages/home.blade.php - Add posts section with data query
  • lang/en/home.php - Add translations
  • lang/ar/home.php - Add translations

Controller/Page Component

The home page needs to pass posts data. Options:

  1. Use a Livewire/Volt component to fetch posts
  2. Pass posts via route closure or controller

Option 1: Volt Component (Recommended)

Convert home.blade.php to a Volt component:

<?php
use App\Models\Post;
use Livewire\Volt\Component;

new class extends Component {
    public function with(): array
    {
        return [
            'latestPosts' => Post::where('status', 'published')
                ->latest()
                ->take(3)
                ->get(),
        ];
    }
}; ?>

Option 2: Route with data

// routes/web.php
Route::get('/', function () {
    return view('pages.home', [
        'latestPosts' => Post::where('status', 'published')
            ->latest()
            ->take(3)
            ->get(),
    ]);
})->name('home');

HTML Structure

@if($latestPosts->count() > 0)
<section id="posts" class="py-16 lg:py-20 bg-background">
    <div class="container mx-auto px-4">
        <div class="text-center mb-12">
            <h2 class="text-2xl lg:text-3xl font-semibold text-text mb-4">
                {{ __('home.posts_title') }}
            </h2>
        </div>

        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
            @foreach($latestPosts as $post)
                <article class="bg-card p-6 rounded-lg shadow-card">
                    <time class="text-xs text-body/70 mb-2 block">
                        {{ $post->created_at->translatedFormat('j F Y') }}
                    </time>
                    <h3 class="text-lg font-bold text-text mb-3">
                        <a href="{{ route('posts.show', $post) }}" class="hover:text-cta transition-colors">
                            {{ $post->title }}
                        </a>
                    </h3>
                    <p class="text-body text-sm mb-4 line-clamp-3">
                        {{ Str::limit(strip_tags($post->body), 150) }}
                    </p>
                    <a href="{{ route('posts.show', $post) }}" class="text-cta font-medium text-sm hover:text-cta-hover">
                        {{ __('home.read_more') }} →
                    </a>
                </article>
            @endforeach
        </div>

        <div class="text-center">
            <flux:button variant="ghost" href="{{ route('posts.index') }}">
                {{ __('home.view_all_posts') }}
            </flux:button>
        </div>
    </div>
</section>
@endif

Translation Keys

// lang/en/home.php (additions)
'posts_title' => 'Latest Articles',
'read_more' => 'Read More',
'view_all_posts' => 'View All Articles',
'posts_empty' => 'Articles coming soon',

// lang/ar/home.php (additions)
'posts_title' => 'أحدث المقالات',
'read_more' => 'اقرأ المزيد',
'view_all_posts' => 'عرض جميع المقالات',
'posts_empty' => 'المقالات قريباً',

Post Model Reference

Existing Post model fields:

  • title - Post title (bilingual stored via JSON or separate fields)
  • body - Post content
  • status - 'draft' or 'published'
  • created_at - Timestamp

Date Formatting

Use Laravel's translatedFormat() for bilingual dates:

$post->created_at->translatedFormat('j F Y')
// English: "15 January 2026"
// Arabic: "15 يناير 2026"

Dev Checklist

  • Determine data passing method (Volt component or route)
  • Create posts section HTML structure
  • Add section heading with translations
  • Implement post card design
  • Query and display 3 latest published posts
  • Add date formatting with translation support
  • Implement excerpt truncation
  • Add "Read More" links to individual posts
  • Add "View All" link to posts index
  • Handle empty state (hide section or show message)
  • Implement responsive grid layout
  • Test RTL layout
  • Verify links work correctly

Estimation

Complexity: Medium (requires data fetching) Risk: Low - Uses existing Post model and routes

Dependencies

  • Previous stories for page structure
  • Existing Post model
  • Posts routes (posts.index, posts.show)