libra/docs/stories/story-4.5-client-timeline-v...

4.8 KiB

Story 4.5: Client Timeline View

Epic Reference

Epic 4: Case Timeline System

User Story

As a client, I want to view my case timelines and updates, So that I can track the progress of my legal matters.

Story Context

Existing System Integration

  • Integrates with: timelines, timeline_updates tables
  • Technology: Livewire Volt (read-only)
  • Follows pattern: Client dashboard pattern
  • Touch points: Client portal navigation

Acceptance Criteria

Timeline List

  • Display all client's timelines
  • Active timelines prominently displayed
  • Archived timelines clearly separated
  • Visual distinction (color/icon) for status
  • Show for each:
    • Case name and reference
    • Status indicator
    • Last update date
    • Update count

Individual Timeline View

  • Case name and reference
  • Status indicator
  • All updates in chronological order
  • Each update shows:
    • Date and time
    • Update content (formatted)

Restrictions

  • Read-only (no edit/comment)
  • No ability to archive/delete
  • Only see own timelines

UX Features

  • Recent updates indicator (new since last view, optional)
  • Responsive design for mobile
  • Bilingual labels and dates

Technical Notes

Volt Component for List

<?php

use Livewire\Volt\Component;

new class extends Component {
    public function with(): array
    {
        return [
            'activeTimelines' => auth()->user()
                ->timelines()
                ->active()
                ->withCount('updates')
                ->with(['updates' => fn($q) => $q->latest()->limit(1)])
                ->latest('updated_at')
                ->get(),

            'archivedTimelines' => auth()->user()
                ->timelines()
                ->archived()
                ->withCount('updates')
                ->latest('updated_at')
                ->get(),
        ];
    }
};

Timeline Detail View

<?php

use App\Models\Timeline;
use Livewire\Volt\Component;

new class extends Component {
    public Timeline $timeline;

    public function mount(Timeline $timeline): void
    {
        // Ensure client owns this timeline
        abort_unless($timeline->user_id === auth()->id(), 403);

        $this->timeline = $timeline->load(['updates' => fn($q) => $q->oldest()]);
    }
};

Template

<div class="max-w-3xl mx-auto">
    <!-- Header -->
    <div class="flex justify-between items-start mb-6">
        <div>
            <flux:heading>{{ $timeline->case_name }}</flux:heading>
            @if($timeline->case_reference)
                <p class="text-charcoal/70">{{ __('client.reference') }}: {{ $timeline->case_reference }}</p>
            @endif
        </div>
        <flux:badge :variant="$timeline->status === 'active' ? 'success' : 'secondary'">
            {{ __('client.' . $timeline->status) }}
        </flux:badge>
    </div>

    <!-- Timeline Updates -->
    <div class="relative">
        <!-- Vertical line -->
        <div class="absolute {{ app()->getLocale() === 'ar' ? 'right-4' : 'left-4' }} top-0 bottom-0 w-0.5 bg-gold/30"></div>

        <div class="space-y-6">
            @forelse($timeline->updates as $update)
                <div class="relative {{ app()->getLocale() === 'ar' ? 'pr-12' : 'pl-12' }}">
                    <!-- Dot -->
                    <div class="absolute {{ app()->getLocale() === 'ar' ? 'right-2' : 'left-2' }} top-2 w-4 h-4 rounded-full bg-gold border-4 border-cream"></div>

                    <div class="bg-white p-4 rounded-lg shadow-sm">
                        <div class="text-sm text-charcoal/70 mb-2">
                            {{ $update->created_at->translatedFormat('l, d M Y - g:i A') }}
                        </div>
                        <div class="prose prose-sm">
                            {!! $update->update_text !!}
                        </div>
                    </div>
                </div>
            @empty
                <p class="text-center text-charcoal/70 py-8">
                    {{ __('client.no_updates_yet') }}
                </p>
            @endforelse
        </div>
    </div>

    <div class="mt-6">
        <flux:button href="{{ route('client.timelines.index') }}">
            {{ __('client.back_to_cases') }}
        </flux:button>
    </div>
</div>

Definition of Done

  • Client can view list of their timelines
  • Active/archived clearly separated
  • Can view individual timeline details
  • All updates displayed chronologically
  • Read-only (no edit capabilities)
  • Cannot view other clients' timelines
  • Mobile responsive
  • RTL support
  • Tests pass
  • Code formatted with Pint

Dependencies

  • Story 4.1-4.3: Timeline management
  • Epic 7: Client dashboard structure

Estimation

Complexity: Medium Estimated Effort: 3-4 hours