16 KiB
Story 4.3: Timeline Archiving
Status
Ready for Review
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,isArchived()helper, and scopesresources/views/livewire/admin/timelines/show.blade.php- add archive/unarchive buttons, confirmation modal, disable update form when archived
Note: List filtering (admin/timelines/index.blade.php) and client view separation (client/timelines/index.blade.php) are implemented in Stories 4.4 and 4.5 respectively. This story focuses on the core archive/unarchive functionality and updating the existing show page.
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 (Deferred to Story 4.4/4.5)
The following filtering features require the admin dashboard (4.4) and client view (4.5):
- 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?")
This story provides the model methods and scopes that Stories 4.4/4.5 will use.
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 edge cases deferred to Story 4.4:
- 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: 'archive' or 'unarchive') - uses AdminLog
actionfield - Bilingual labels (AR/EN for buttons, messages)
- Feature tests covering all scenarios below
Test Scenarios
tests/Feature/Admin/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_audit_log_created_on_archive
- test_audit_log_created_on_unarchive
- test_guest_cannot_archive_timeline
- test_client_cannot_archive_timeline
- test_archived_timeline_shows_visual_indicator
- test_update_form_disabled_on_archived_timeline
Deferred to Story 4.4/4.5:
- 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_client_can_view_archived_timeline (4.5)
Technical Notes
Timeline Model Methods
Add these methods to app/Models/Timeline.php:
use App\Enums\TimelineStatus;
class Timeline extends Model
{
// ... existing code ...
public function archive(): void
{
$this->update(['status' => TimelineStatus::Archived]);
}
public function unarchive(): void
{
$this->update(['status' => TimelineStatus::Active]);
}
public function isArchived(): bool
{
return $this->status === TimelineStatus::Archived;
}
public function isActive(): bool
{
return $this->status === TimelineStatus::Active;
}
public function scopeActive($query)
{
return $query->where('status', TimelineStatus::Active);
}
public function scopeArchived($query)
{
return $query->where('status', TimelineStatus::Archived);
}
}
Note: The Timeline model already casts
statustoTimelineStatus::classenum. Always use enum values, not strings.
Volt Component Actions
Add these methods to resources/views/livewire/admin/timelines/show.blade.php:
use App\Models\AdminLog;
public function archive(): void
{
if ($this->timeline->isArchived()) {
return; // No-op for already archived
}
$this->timeline->archive();
AdminLog::create([
'admin_id' => auth()->id(),
'action' => 'archive', // Note: field is 'action', not 'action_type'
'target_type' => 'timeline',
'target_id' => $this->timeline->id,
'ip_address' => request()->ip(),
'created_at' => now(),
]);
session()->flash('success', __('messages.timeline_archived'));
}
public function unarchive(): void
{
if ($this->timeline->isActive()) {
return; // No-op for already active
}
$this->timeline->unarchive();
AdminLog::create([
'admin_id' => auth()->id(),
'action' => 'unarchive', // Note: field is 'action', not 'action_type'
'target_type' => 'timeline',
'target_id' => $this->timeline->id,
'ip_address' => request()->ip(),
'created_at' => now(),
]);
session()->flash('success', __('messages.timeline_unarchived'));
}
Note: The AdminLog model uses
actionfield (notaction_type). Also requirescreated_atsince$timestamps = falseon the model.
Bulk Archive (Deferred to Story 4.4)
The bulkArchive() method will be implemented in Story 4.4 when the admin timeline index view is created.
Tasks / Subtasks
-
Task 1: Add Timeline model methods (AC: Archive/Unarchive)
- Add
archive()method usingTimelineStatus::Archivedenum - Add
unarchive()method usingTimelineStatus::Activeenum - Add
isArchived()helper method - Add
isActive()helper method - Add
scopeActive()query scope - Add
scopeArchived()query scope
- Add
-
Task 2: Update show.blade.php component (AC: Archive/Unarchive buttons)
- Add
archive()action method with AdminLog - Add
unarchive()action method with AdminLog - Add archive button (visible when timeline is active)
- Add unarchive button (visible when timeline is archived)
- Add confirmation modal using
<flux:modal>for archive action - Show archived status badge using
<flux:badge>
- Add
-
Task 3: Disable updates on archived timelines (AC: No updates on archived)
- Conditionally disable/hide update form when
$timeline->isArchived() - Show tooltip or message explaining why updates are disabled
- Apply muted styling to archived timeline view
- Conditionally disable/hide update form when
-
Task 4: Add translation keys (AC: Bilingual)
- Add EN/AR keys to
lang/*/messages.php - Add EN/AR keys to
lang/*/timelines.php
- Add EN/AR keys to
-
Task 5: Write feature tests (AC: Quality Requirements)
- Create
tests/Feature/Admin/TimelineArchivingTest.php - Implement all test scenarios listed above
- Verify all tests pass
- Create
-
Task 6: Code quality
- Run
vendor/bin/pint --dirty - Verify no regressions in existing tests
- Run
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
- Visual indicators (badge + muted styling) display correctly
- Audit log entries created for all archive/unarchive actions
- All test scenarios in
tests/Feature/Admin/TimelineArchivingTest.phppass - Code formatted with Pint
Deferred to Story 4.4/4.5:
- Status filter dropdown functional (Admin Dashboard)
- Bulk archive with checkbox selection (Admin Dashboard)
- Client can still view archived timelines in separate section (Client View)
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
Dev Notes
Source Tree Reference
app/
├── Models/
│ └── Timeline.php # Add archive/unarchive methods and scopes
├── Enums/
│ └── TimelineStatus.php # Already exists with Active/Archived values
resources/
└── views/
└── livewire/
└── admin/
└── timelines/
└── show.blade.php # Add archive/unarchive UI and actions
tests/
└── Feature/
└── Admin/
└── TimelineArchivingTest.php # New test file
Required Translation Keys
lang/en/messages.php:
'timeline_archived' => 'Timeline has been archived.',
'timeline_unarchived' => 'Timeline has been unarchived.',
'cannot_update_archived_timeline' => 'Updates cannot be added to archived timelines.',
lang/ar/messages.php:
'timeline_archived' => 'تم أرشفة الجدول الزمني.',
'timeline_unarchived' => 'تم إلغاء أرشفة الجدول الزمني.',
'cannot_update_archived_timeline' => 'لا يمكن إضافة تحديثات إلى الجداول الزمنية المؤرشفة.',
lang/en/timelines.php:
'archive' => 'Archive',
'unarchive' => 'Unarchive',
'archive_confirm_title' => 'Archive Timeline',
'archive_confirm_message' => 'Are you sure you want to archive this timeline? No further updates can be added until it is unarchived.',
'archived_notice' => 'This timeline is archived. Updates are disabled.',
lang/ar/timelines.php:
'archive' => 'أرشفة',
'unarchive' => 'إلغاء الأرشفة',
'archive_confirm_title' => 'أرشفة الجدول الزمني',
'archive_confirm_message' => 'هل أنت متأكد من أرشفة هذا الجدول الزمني؟ لن يمكن إضافة تحديثات حتى يتم إلغاء الأرشفة.',
'archived_notice' => 'هذا الجدول الزمني مؤرشف. التحديثات معطلة.',
Flux UI Components to Use
<flux:button variant="danger">- Archive button<flux:button variant="primary">- Unarchive button<flux:modal>- Confirmation dialog<flux:badge color="amber">- Archived status indicator<flux:tooltip>- Disabled update form explanation
Testing
Test File: tests/Feature/Admin/TimelineArchivingTest.php
Testing Pattern: Use Pest with Volt::test() for component testing (matches Stories 4.1/4.2)
Required Factories:
Timeline::factory()with default active statusTimeline::factory()->archived()state (may need to add)User::factory()->admin()for admin userUser::factory()->client()for authorization tests
Example Test Structure:
use App\Models\Timeline;
use App\Models\User;
use App\Models\AdminLog;
use Livewire\Volt\Volt;
beforeEach(function () {
$this->admin = User::factory()->admin()->create();
$this->client = User::factory()->individual()->create();
$this->timeline = Timeline::factory()->for($this->client)->create();
});
test('admin can archive active timeline', function () {
Volt::test('admin.timelines.show', ['timeline' => $this->timeline])
->actingAs($this->admin)
->call('archive')
->assertHasNoErrors();
expect($this->timeline->fresh()->isArchived())->toBeTrue();
});
Dev Agent Record
Agent Model Used
Claude Opus 4.5
File List
| File | Action |
|---|---|
app/Models/Timeline.php |
Modified - Added archive/unarchive methods and scopes |
resources/views/livewire/admin/timelines/show.blade.php |
Modified - Added archive/unarchive actions, buttons, modal, visual indicators |
lang/en/messages.php |
Modified - Added archive-related translation keys |
lang/ar/messages.php |
Modified - Added archive-related translation keys |
lang/en/timelines.php |
Modified - Added archive UI translation keys |
lang/ar/timelines.php |
Modified - Added archive UI translation keys |
tests/Feature/Admin/TimelineArchivingTest.php |
Created - 17 test scenarios for archiving functionality |
Debug Log References
None - Implementation completed without errors.
Completion Notes
- All 17 archiving tests pass
- All 312 Admin feature tests pass (no regressions)
- Code formatted with Pint
- Bilingual support complete (AR/EN)
- Archive confirmation modal uses Flux UI components
- Muted styling applied to archived timelines
- Update form hidden with callout notice on archived timelines
Change Log
| Date | Version | Description | Author |
|---|---|---|---|
| 2025-12-27 | 1.0 | Initial story draft | PM |
| 2025-12-27 | 1.1 | Validation fixes: re-scoped to exclude deferred features (filtering, bulk, client view), fixed AdminLog field name, fixed enum comparisons, added Tasks/Subtasks section, added translation keys, corrected test file path | Claude Opus 4.5 |
| 2025-12-27 | 1.2 | Implementation complete: Added Timeline model methods, archive/unarchive UI, translations, 17 passing tests | Claude Opus 4.5 |
QA Results
Review Date: 2025-12-27
Reviewed By: Quinn (Test Architect)
Code Quality Assessment
Excellent implementation following Laravel/Livewire best practices. The code is clean, well-organized, and follows the established patterns in the codebase. Key strengths:
- Model Methods: Proper use of TimelineStatus enum throughout, idempotent archive/unarchive operations
- Volt Component: Clean separation of archive/unarchive actions with proper AdminLog audit entries
- UI Implementation: Good UX with confirmation modal, visual indicators (badge + muted styling), and clear archived state messaging
- Tests: Comprehensive coverage with 17 tests covering all acceptance criteria including edge cases
Refactoring Performed
None required. The implementation is clean and follows project conventions.
Compliance Check
- Coding Standards: ✓ Pint passes with no issues
- Project Structure: ✓ Follows existing Volt component patterns
- Testing Strategy: ✓ Pest with Volt::test() as per codebase conventions
- All ACs Met: ✓ All acceptance criteria verified
Improvements Checklist
All items addressed by developer:
- Model methods (archive, unarchive, isArchived, isActive, scopes) implemented correctly
- Archive button with confirmation modal implemented
- Unarchive button implemented
- Visual indicator (amber badge) for archived status
- Muted styling (opacity-75/60) applied to archived timeline sections
- Update form hidden with callout notice when archived
- Edit buttons hidden on archived timeline updates
- AdminLog audit entries created for archive/unarchive actions
- Bilingual translations (EN/AR) complete
- Factory state
archived()available for testing - All 17 test scenarios pass
Security Review
No security concerns. Authorization is properly handled:
- Admin middleware protects the route
- Guest and client access correctly returns 401/403
- Archive/unarchive actions only available to authenticated admins
Performance Considerations
No performance concerns. Simple status updates with minimal database operations.
Files Modified During Review
None - no modifications required.
Gate Status
Gate: PASS → docs/qa/gates/4.3-timeline-archiving.yml
Recommended Status
✓ Ready for Done
All acceptance criteria met, comprehensive test coverage, no security or performance concerns.