5.9 KiB
5.9 KiB
Story 4.3: Timeline Archiving
Epic Reference
Epic 4: Case Timeline System
User Story
As an admin, I want to archive completed cases and unarchive if needed, So that I can organize active and completed case timelines.
Story Context
Existing System Integration
- Integrates with: timelines table (status field from Story 4.1)
- Technology: Livewire Volt
- Follows pattern: Soft state change pattern
- Touch points: Timeline list views, timeline detail view
Prerequisites (from Story 4.1)
The timelines table includes:
$table->enum('status', ['active', 'archived'])->default('active');
Files to Modify
app/Models/Timeline.php- add archive/unarchive methods and scopesresources/views/livewire/admin/timelines/show.blade.php- add archive/unarchive buttonresources/views/livewire/admin/timelines/index.blade.php- add status filter dropdownresources/views/livewire/client/timelines/index.blade.php- separate archived timelines display
Acceptance Criteria
Archive Timeline
- Archive button on timeline detail view (admin only)
- Confirmation modal before archiving ("Are you sure?")
- Status changes to 'archived'
- Timeline remains visible to client (read-only as always)
- No further updates can be added (show disabled state with tooltip)
- Visual indicator shows archived status (badge + muted styling)
Unarchive Timeline
- Unarchive button on archived timelines
- Status returns to 'active'
- Updates can be added again
List Filtering
- Filter dropdown with options: Active (default), Archived, All
- Archived timelines shown in separate section in client view (below active)
- Bulk archive via checkbox selection + "Archive Selected" button
- Bulk archive shows count confirmation ("Archive 3 timelines?")
Edge Cases
- Archiving already-archived timeline: No-op, no error shown
- Unarchiving already-active timeline: No-op, no error shown
- Adding update to archived timeline: Prevented with error message
- Bulk archive mixed selection: Skip already-archived, archive only active ones
- Empty bulk selection: "Archive Selected" button disabled
Quality Requirements
- Audit log for status changes (action_type: 'archive' or 'unarchive')
- Bilingual labels (AR/EN for buttons, messages, filters)
- Feature tests covering all scenarios below
Test Scenarios
tests/Feature/Timeline/TimelineArchivingTest.php
- test_admin_can_archive_active_timeline
- test_admin_can_unarchive_archived_timeline
- test_archiving_archived_timeline_is_noop
- test_unarchiving_active_timeline_is_noop
- test_cannot_add_update_to_archived_timeline
- test_client_can_view_archived_timeline
- test_admin_can_filter_by_active_status
- test_admin_can_filter_by_archived_status
- test_admin_can_filter_all_statuses
- test_bulk_archive_updates_multiple_timelines
- test_bulk_archive_skips_already_archived
- test_audit_log_created_on_archive
- test_audit_log_created_on_unarchive
- test_guest_cannot_archive_timeline
Technical Notes
Timeline Model Methods
class Timeline extends Model
{
public function archive(): void
{
$this->update(['status' => 'archived']);
}
public function unarchive(): void
{
$this->update(['status' => 'active']);
}
public function isArchived(): bool
{
return $this->status === 'archived';
}
public function scopeActive($query)
{
return $query->where('status', 'active');
}
public function scopeArchived($query)
{
return $query->where('status', 'archived');
}
}
Volt Component Actions
public function archive(): void
{
if ($this->timeline->isArchived()) {
return;
}
$this->timeline->archive();
AdminLog::create([
'admin_id' => auth()->id(),
'action_type' => 'archive',
'target_type' => 'timeline',
'target_id' => $this->timeline->id,
'ip_address' => request()->ip(),
]);
session()->flash('success', __('messages.timeline_archived'));
}
public function unarchive(): void
{
if (!$this->timeline->isArchived()) {
return;
}
$this->timeline->unarchive();
AdminLog::create([
'admin_id' => auth()->id(),
'action_type' => 'unarchive',
'target_type' => 'timeline',
'target_id' => $this->timeline->id,
'ip_address' => request()->ip(),
]);
session()->flash('success', __('messages.timeline_unarchived'));
}
public function bulkArchive(array $ids): void
{
Timeline::whereIn('id', $ids)->update(['status' => 'archived']);
foreach ($ids as $id) {
AdminLog::create([
'admin_id' => auth()->id(),
'action_type' => 'archive',
'target_type' => 'timeline',
'target_id' => $id,
'ip_address' => request()->ip(),
]);
}
session()->flash('success', __('messages.timelines_archived', ['count' => count($ids)]));
}
Definition of Done
- Archive button on timeline detail view works with confirmation modal
- Unarchive button on archived timelines works
- Update form disabled/hidden on archived timelines with clear messaging
- Status filter dropdown functional (Active/Archived/All)
- Bulk archive with checkbox selection works
- Visual indicators (badge + muted styling) display correctly
- Client can still view archived timelines in separate section
- Audit log entries created for all archive/unarchive actions
- All test scenarios in
tests/Feature/Timeline/TimelineArchivingTest.phppass - Code formatted with Pint
Dependencies
- Story 4.1: Timeline creation (
docs/stories/story-4.1-timeline-creation.md) - provides Timeline model and schema - Story 4.2: Timeline updates (
docs/stories/story-4.2-timeline-updates-management.md) - provides update blocking context
Estimation
Complexity: Low Estimated Effort: 2 hours