libra/docs/stories/story-5.3-post-deletion.md

190 lines
4.8 KiB
Markdown

# Story 5.3: Post Deletion
## Epic Reference
**Epic 5:** Posts/Blog System
## User Story
As an **admin**,
I want **to permanently delete posts**,
So that **I can remove outdated or incorrect content from the website**.
## Story Context
### Existing System Integration
- **Integrates with:** posts table
- **Technology:** Livewire Volt
- **Follows pattern:** Permanent delete pattern
- **Touch points:** Post management dashboard
- **Files to modify:** Post management Volt component from Story 5.2
## Acceptance Criteria
### Delete Functionality
- [ ] Delete button on post list (primary location)
- [ ] Delete button on post edit page (secondary location)
- [ ] Confirmation modal dialog before deletion
- [ ] Permanent deletion (no soft delete per PRD)
- [ ] Success message after deletion
- [ ] Redirect to post list after deletion from edit page
### Restrictions
- [ ] Admin-only access (middleware protection)
### Audit Trail
- [ ] Audit log entry preserved
- [ ] Old values stored in log
### Quality Requirements
- [ ] Clear warning in confirmation
- [ ] Bilingual messages
- [ ] Tests for deletion
## Technical Notes
### Delete Confirmation Modal
```blade
<flux:modal wire:model="showDeleteModal">
<flux:heading>{{ __('admin.delete_post') }}</flux:heading>
<flux:callout variant="danger">
{{ __('admin.delete_post_warning') }}
</flux:callout>
<p class="my-4">
{{ __('admin.deleting_post', ['title' => $postToDelete?->title]) }}
</p>
<div class="flex gap-3">
<flux:button wire:click="$set('showDeleteModal', false)">
{{ __('common.cancel') }}
</flux:button>
<flux:button variant="danger" wire:click="confirmDelete">
{{ __('admin.delete_permanently') }}
</flux:button>
</div>
</flux:modal>
```
### Delete Button on Edit Page
Add delete button to the post edit form (from Story 5.1) when editing an existing post:
```blade
@if($post?->exists)
<flux:button variant="danger" wire:click="delete({{ $post->id }})">
{{ __('admin.delete_post') }}
</flux:button>
@endif
```
### Delete Logic
```php
public ?Post $postToDelete = null;
public bool $showDeleteModal = false;
public function delete(int $id): void
{
$this->postToDelete = Post::findOrFail($id);
$this->showDeleteModal = true;
}
public function confirmDelete(): void
{
if (!$this->postToDelete) {
return;
}
// Create audit log BEFORE deletion
AdminLog::create([
'admin_id' => auth()->id(),
'action_type' => 'delete',
'target_type' => 'post',
'target_id' => $this->postToDelete->id,
'old_values' => $this->postToDelete->toArray(),
'ip_address' => request()->ip(),
]);
// Permanently delete
$this->postToDelete->delete();
$this->showDeleteModal = false;
$this->postToDelete = null;
session()->flash('success', __('messages.post_deleted'));
}
```
### Testing
```php
use App\Models\Post;
use App\Models\AdminLog;
use App\Models\User;
use Livewire\Volt\Volt;
it('permanently deletes post', function () {
$admin = User::factory()->create();
$post = Post::factory()->create();
Volt::test('admin.posts.index')
->actingAs($admin)
->call('delete', $post->id)
->call('confirmDelete');
expect(Post::find($post->id))->toBeNull();
});
it('shows confirmation modal before deletion', function () {
$admin = User::factory()->create();
$post = Post::factory()->create();
Volt::test('admin.posts.index')
->actingAs($admin)
->call('delete', $post->id)
->assertSet('showDeleteModal', true)
->assertSet('postToDelete.id', $post->id);
});
it('creates audit log on deletion with old values', function () {
$admin = User::factory()->create();
$post = Post::factory()->create(['title_en' => 'Test Title']);
Volt::test('admin.posts.index')
->actingAs($admin)
->call('delete', $post->id)
->call('confirmDelete');
$log = AdminLog::where('target_type', 'post')
->where('target_id', $post->id)
->where('action_type', 'delete')
->first();
expect($log)->not->toBeNull()
->and($log->old_values)->toHaveKey('title_en', 'Test Title');
});
it('requires admin authentication to delete', function () {
$post = Post::factory()->create();
$this->delete(route('admin.posts.destroy', $post))
->assertRedirect(route('login'));
});
```
## Definition of Done
- [ ] Delete button shows confirmation
- [ ] Confirmation explains permanence
- [ ] Post deleted from database
- [ ] Audit log created with old values
- [ ] Success message displayed
- [ ] Tests pass
- [ ] Code formatted with Pint
## Dependencies
- **Story 5.1:** Post creation
- **Story 5.2:** Post management dashboard
## Estimation
**Complexity:** Low
**Estimated Effort:** 1-2 hours