# 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: ```php $table->enum('status', ['active', 'archived'])->default('active'); ``` ### Files to Modify - `app/Models/Timeline.php` - add archive/unarchive methods, `isArchived()` helper, and scopes - `resources/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 `action` field - [ ] 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`: ```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 `status` to `TimelineStatus::class` enum. Always use enum values, not strings. ### Volt Component Actions Add these methods to `resources/views/livewire/admin/timelines/show.blade.php`: ```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 `action` field (not `action_type`). Also requires `created_at` since `$timestamps = false` on 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 - [x] **Task 1: Add Timeline model methods** (AC: Archive/Unarchive) - [x] Add `archive()` method using `TimelineStatus::Archived` enum - [x] Add `unarchive()` method using `TimelineStatus::Active` enum - [x] Add `isArchived()` helper method - [x] Add `isActive()` helper method - [x] Add `scopeActive()` query scope - [x] Add `scopeArchived()` query scope - [x] **Task 2: Update show.blade.php component** (AC: Archive/Unarchive buttons) - [x] Add `archive()` action method with AdminLog - [x] Add `unarchive()` action method with AdminLog - [x] Add archive button (visible when timeline is active) - [x] Add unarchive button (visible when timeline is archived) - [x] Add confirmation modal using `` for archive action - [x] Show archived status badge using `` - [x] **Task 3: Disable updates on archived timelines** (AC: No updates on archived) - [x] Conditionally disable/hide update form when `$timeline->isArchived()` - [x] Show tooltip or message explaining why updates are disabled - [x] Apply muted styling to archived timeline view - [x] **Task 4: Add translation keys** (AC: Bilingual) - [x] Add EN/AR keys to `lang/*/messages.php` - [x] Add EN/AR keys to `lang/*/timelines.php` - [x] **Task 5: Write feature tests** (AC: Quality Requirements) - [x] Create `tests/Feature/Admin/TimelineArchivingTest.php` - [x] Implement all test scenarios listed above - [x] Verify all tests pass - [x] **Task 6: Code quality** - [x] Run `vendor/bin/pint --dirty` - [x] Verify no regressions in existing tests ## Definition of Done - [x] Archive button on timeline detail view works with confirmation modal - [x] Unarchive button on archived timelines works - [x] Update form disabled/hidden on archived timelines with clear messaging - [x] Visual indicators (badge + muted styling) display correctly - [x] Audit log entries created for all archive/unarchive actions - [x] All test scenarios in `tests/Feature/Admin/TimelineArchivingTest.php` pass - [x] 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:** ```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:** ```php 'timeline_archived' => 'تم أرشفة الجدول الزمني.', 'timeline_unarchived' => 'تم إلغاء أرشفة الجدول الزمني.', 'cannot_update_archived_timeline' => 'لا يمكن إضافة تحديثات إلى الجداول الزمنية المؤرشفة.', ``` **lang/en/timelines.php:** ```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:** ```php 'archive' => 'أرشفة', 'unarchive' => 'إلغاء الأرشفة', 'archive_confirm_title' => 'أرشفة الجدول الزمني', 'archive_confirm_message' => 'هل أنت متأكد من أرشفة هذا الجدول الزمني؟ لن يمكن إضافة تحديثات حتى يتم إلغاء الأرشفة.', 'archived_notice' => 'هذا الجدول الزمني مؤرشف. التحديثات معطلة.', ``` ### Flux UI Components to Use - `` - Archive button - `` - Unarchive button - `` - Confirmation dialog - `` - Archived status indicator - `` - 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 status - `Timeline::factory()->archived()` state (may need to add) - `User::factory()->admin()` for admin user - `User::factory()->client()` for authorization tests **Example Test Structure:** ```php 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: - [x] Model methods (archive, unarchive, isArchived, isActive, scopes) implemented correctly - [x] Archive button with confirmation modal implemented - [x] Unarchive button implemented - [x] Visual indicator (amber badge) for archived status - [x] Muted styling (opacity-75/60) applied to archived timeline sections - [x] Update form hidden with callout notice when archived - [x] Edit buttons hidden on archived timeline updates - [x] AdminLog audit entries created for archive/unarchive actions - [x] Bilingual translations (EN/AR) complete - [x] Factory state `archived()` available for testing - [x] 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.