libra/docs/stories/story-7.3-my-cases-timeline...

8.1 KiB

Story 7.3: My Cases/Timelines View (Dashboard Integration)

Epic Reference

Epic 7: Client Dashboard

User Story

As a client, I want to access my case timelines from the dashboard navigation, So that I can easily track the progress of my legal matters from one central location.

Story Context

Relationship to Story 4.5

Story 4.5 (docs/stories/story-4.5-client-timeline-view.md) already implements the complete timeline viewing functionality:

  • Routes: client.timelines.index and client.timelines.show
  • Components: pages/client/timelines/index.blade.php and show.blade.php
  • Active/archived separation with visual distinction
  • Individual timeline detail view with chronological updates
  • Authorization, tests, and translations

This story (7.3) focuses solely on dashboard navigation integration - ensuring clients can access the existing timeline views from the Epic 7 client dashboard structure.

Prerequisites

  • Story 4.5: Client Timeline View - MUST be complete (provides all timeline components)
  • Story 7.1: Client Dashboard Overview - MUST be complete (provides dashboard layout and navigation)

What This Story Does NOT Do

  • Does NOT recreate timeline list or detail views (use Story 4.5's components)
  • Does NOT add new timeline functionality
  • Does NOT modify existing timeline components

Acceptance Criteria

Navigation Integration

  • "My Cases" navigation item added to client dashboard sidebar/nav
  • Navigation links to route('client.timelines.index')
  • Active state shown when on timeline routes
  • Icon: folder or briefcase icon for cases

Dashboard Widget (on Story 7.1's dashboard)

  • "My Cases" widget card displays:
    • Count of active cases
    • Latest update preview (case name + date)
    • "View All" link to timeline index
  • Widget shows empty state if no cases exist

Layout Consistency

  • Timeline pages use client dashboard layout (consistent header/nav)
  • Breadcrumbs: Dashboard > My Cases (on index)
  • Breadcrumbs: Dashboard > My Cases > [Case Name] (on show)

Bilingual Support

  • Navigation label translated (AR/EN)
  • Widget content translated (AR/EN)

Technical Notes

File Structure

Files to Modify:
  resources/views/components/layouts/client.blade.php (add nav item)
  OR resources/views/livewire/pages/client/dashboard.blade.php (add widget)

Files from Story 4.5 (DO NOT MODIFY - just ensure they exist):
  resources/views/livewire/pages/client/timelines/index.blade.php
  resources/views/livewire/pages/client/timelines/show.blade.php

Tests to Create:
  tests/Feature/Client/DashboardTimelineIntegrationTest.php

Navigation Item Addition

Add to client dashboard navigation (location depends on Story 7.1's implementation):

{{-- In client layout/navigation component --}}
<flux:navbar.item
    href="{{ route('client.timelines.index') }}"
    :active="request()->routeIs('client.timelines.*')"
    icon="folder"
>
    {{ __('client.my_cases') }}
</flux:navbar.item>

Dashboard Widget Component

Add to Story 7.1's dashboard view:

{{-- My Cases Widget --}}
<div class="bg-white rounded-lg shadow-sm p-6">
    <div class="flex justify-between items-center mb-4">
        <h3 class="font-semibold text-charcoal">{{ __('client.my_cases') }}</h3>
        <flux:badge>{{ $activeTimelinesCount }} {{ __('client.active') }}</flux:badge>
    </div>

    @if($latestTimelineUpdate)
        <div class="text-sm text-charcoal/70 mb-4">
            <p class="font-medium">{{ $latestTimelineUpdate->timeline->case_name }}</p>
            <p class="text-xs">{{ __('client.last_update') }}: {{ $latestTimelineUpdate->created_at->diffForHumans() }}</p>
        </div>
    @else
        <p class="text-sm text-charcoal/50 mb-4">{{ __('client.no_cases_yet') }}</p>
    @endif

    <flux:button size="sm" href="{{ route('client.timelines.index') }}">
        {{ __('client.view_all_cases') }}
    </flux:button>
</div>

Data for Widget (add to Story 7.1's dashboard component)

// In dashboard component's with() method
'activeTimelinesCount' => auth()->user()->timelines()->active()->count(),
'latestTimelineUpdate' => TimelineUpdate::whereHas('timeline',
    fn($q) => $q->where('user_id', auth()->id())->active()
)
    ->with('timeline:id,case_name')
    ->latest()
    ->first(),

Required Translation Keys

// Add to resources/lang/en/client.php (if not already from 4.5)
'view_all_cases' => 'View All Cases',

// Add to resources/lang/ar/client.php
'view_all_cases' => 'عرض جميع القضايا',

Test Scenarios

<?php
// tests/Feature/Client/DashboardTimelineIntegrationTest.php

use App\Models\{User, Timeline, TimelineUpdate};

test('client dashboard has my cases navigation link', function () {
    $client = User::factory()->create(['user_type' => 'individual']);

    $this->actingAs($client)
        ->get(route('client.dashboard'))
        ->assertOk()
        ->assertSee(__('client.my_cases'))
        ->assertSee(route('client.timelines.index'));
});

test('client dashboard shows active cases count in widget', function () {
    $client = User::factory()->create(['user_type' => 'individual']);
    Timeline::factory()->count(3)->create([
        'user_id' => $client->id,
        'status' => 'active',
    ]);
    Timeline::factory()->create([
        'user_id' => $client->id,
        'status' => 'archived',
    ]);

    $this->actingAs($client)
        ->get(route('client.dashboard'))
        ->assertSee('3'); // Only active cases counted
});

test('client dashboard shows latest timeline update', function () {
    $client = User::factory()->create(['user_type' => 'individual']);
    $timeline = Timeline::factory()->create([
        'user_id' => $client->id,
        'case_name' => 'Property Dispute Case',
    ]);
    TimelineUpdate::factory()->create([
        'timeline_id' => $timeline->id,
        'created_at' => now(),
    ]);

    $this->actingAs($client)
        ->get(route('client.dashboard'))
        ->assertSee('Property Dispute Case');
});

test('client dashboard shows empty state when no cases', function () {
    $client = User::factory()->create(['user_type' => 'individual']);

    $this->actingAs($client)
        ->get(route('client.dashboard'))
        ->assertSee(__('client.no_cases_yet'));
});

test('my cases navigation is active on timeline routes', function () {
    $client = User::factory()->create(['user_type' => 'individual']);
    $timeline = Timeline::factory()->create(['user_id' => $client->id]);

    // Test on index
    $this->actingAs($client)
        ->get(route('client.timelines.index'))
        ->assertOk();

    // Test on show
    $this->actingAs($client)
        ->get(route('client.timelines.show', $timeline))
        ->assertOk();
});

test('timeline pages use client dashboard layout', function () {
    $client = User::factory()->create(['user_type' => 'individual']);

    $this->actingAs($client)
        ->get(route('client.timelines.index'))
        ->assertSee(__('client.my_cases')); // Nav item visible = layout applied
});

Definition of Done

  • "My Cases" navigation item added to client dashboard
  • Navigation links to existing client.timelines.index route
  • Active state shows on timeline routes
  • Dashboard widget displays active case count
  • Dashboard widget shows latest update preview
  • Dashboard widget links to timeline index
  • Empty state handled in widget
  • Translation keys added for new strings
  • Timeline pages render within client dashboard layout
  • All tests pass
  • Code formatted with Pint

Dependencies

  • Story 4.5: Client Timeline View (REQUIRED - provides timeline components and routes)
  • Story 7.1: Client Dashboard Overview (REQUIRED - provides dashboard layout)

Notes

This story is intentionally minimal because the heavy lifting was done in Story 4.5. The developer should:

  1. Verify Story 4.5 is complete and routes work
  2. Add navigation item to client layout
  3. Add widget to dashboard
  4. Ensure layout consistency
  5. Write integration tests

Do NOT duplicate or recreate the timeline components from Story 4.5.

Estimation

Complexity: Low | Effort: 1-2 hours