generated sotries
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
# Story 2.4: Account Lifecycle Management
|
||||
|
||||
## Epic Reference
|
||||
**Epic 2:** User Management System
|
||||
|
||||
## User Story
|
||||
As an **admin**,
|
||||
I want **to deactivate, reactivate, and permanently delete client accounts**,
|
||||
So that **I can manage the full lifecycle of client relationships**.
|
||||
|
||||
## Story Context
|
||||
|
||||
### Existing System Integration
|
||||
- **Integrates with:** Users table, consultations, timelines, notifications
|
||||
- **Technology:** Livewire Volt, confirmation modals
|
||||
- **Follows pattern:** Soft deactivation, hard deletion with cascade
|
||||
- **Touch points:** User model, all related models
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
### Deactivate Account
|
||||
- [ ] "Deactivate" button on user profile and list
|
||||
- [ ] Confirmation dialog explaining consequences
|
||||
- [ ] Effects of deactivation:
|
||||
- User cannot log in
|
||||
- All data retained (consultations, timelines)
|
||||
- Status changes to 'deactivated'
|
||||
- Can be reactivated by admin
|
||||
- [ ] Visual indicator in user list (grayed out, badge)
|
||||
- [ ] Audit log entry created
|
||||
|
||||
### Reactivate Account
|
||||
- [ ] "Reactivate" button on deactivated profiles
|
||||
- [ ] Confirmation dialog
|
||||
- [ ] Effects of reactivation:
|
||||
- Restore login ability
|
||||
- Status changes to 'active'
|
||||
- All data intact
|
||||
- [ ] Email notification sent to user
|
||||
- [ ] Audit log entry created
|
||||
|
||||
### Delete Account (Permanent)
|
||||
- [ ] "Delete" button (with danger styling)
|
||||
- [ ] Confirmation dialog with strong warning:
|
||||
- "This action cannot be undone"
|
||||
- Lists what will be deleted
|
||||
- Requires typing confirmation (e.g., user email)
|
||||
- [ ] Effects of deletion:
|
||||
- User record permanently removed
|
||||
- Cascades to: consultations, timelines, timeline_updates, notifications
|
||||
- Cannot be recovered
|
||||
- [ ] Audit log entry preserved (for audit trail)
|
||||
- [ ] No email sent (user no longer exists)
|
||||
|
||||
### Password Reset
|
||||
- [ ] "Reset Password" action on user profile
|
||||
- [ ] Options:
|
||||
- Generate random password
|
||||
- Set specific password manually
|
||||
- [ ] Email new credentials to client
|
||||
- [ ] Force password change on next login (optional)
|
||||
- [ ] Audit log entry created
|
||||
|
||||
### Quality Requirements
|
||||
- [ ] All actions logged in admin_logs table
|
||||
- [ ] Bilingual confirmation messages
|
||||
- [ ] Clear visual states for account status
|
||||
- [ ] Tests for all lifecycle operations
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### User Status Management
|
||||
```php
|
||||
// User model
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->status === 'active';
|
||||
}
|
||||
|
||||
public function isDeactivated(): bool
|
||||
{
|
||||
return $this->status === 'deactivated';
|
||||
}
|
||||
|
||||
public function deactivate(): void
|
||||
{
|
||||
$this->update(['status' => 'deactivated']);
|
||||
}
|
||||
|
||||
public function reactivate(): void
|
||||
{
|
||||
$this->update(['status' => 'active']);
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Check
|
||||
```php
|
||||
// In FortifyServiceProvider or custom auth
|
||||
Fortify::authenticateUsing(function (Request $request) {
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if ($user &&
|
||||
$user->isActive() &&
|
||||
Hash::check($request->password, $user->password)) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
// Optionally: different error for deactivated
|
||||
return null;
|
||||
});
|
||||
```
|
||||
|
||||
### Cascade Deletion
|
||||
```php
|
||||
// In User model
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::deleting(function (User $user) {
|
||||
// Log before deletion
|
||||
AdminLog::create([
|
||||
'admin_id' => auth()->id(),
|
||||
'action_type' => 'delete',
|
||||
'target_type' => 'user',
|
||||
'target_id' => $user->id,
|
||||
'old_values' => $user->toArray(),
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
// Cascade delete (or use foreign key CASCADE)
|
||||
$user->consultations()->delete();
|
||||
$user->timelines->each(function ($timeline) {
|
||||
$timeline->updates()->delete();
|
||||
$timeline->delete();
|
||||
});
|
||||
$user->notifications()->delete();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Volt Component for Lifecycle Actions
|
||||
```php
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Livewire\Volt\Component;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
new class extends Component {
|
||||
public User $user;
|
||||
public string $deleteConfirmation = '';
|
||||
public bool $showDeleteModal = false;
|
||||
|
||||
public function deactivate(): void
|
||||
{
|
||||
$this->user->deactivate();
|
||||
|
||||
AdminLog::create([
|
||||
'admin_id' => auth()->id(),
|
||||
'action_type' => 'deactivate',
|
||||
'target_type' => 'user',
|
||||
'target_id' => $this->user->id,
|
||||
'old_values' => ['status' => 'active'],
|
||||
'new_values' => ['status' => 'deactivated'],
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
session()->flash('success', __('messages.user_deactivated'));
|
||||
}
|
||||
|
||||
public function reactivate(): void
|
||||
{
|
||||
$this->user->reactivate();
|
||||
|
||||
// Send notification
|
||||
$this->user->notify(new AccountReactivatedNotification());
|
||||
|
||||
// Log action
|
||||
AdminLog::create([...]);
|
||||
|
||||
session()->flash('success', __('messages.user_reactivated'));
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->deleteConfirmation !== $this->user->email) {
|
||||
$this->addError('deleteConfirmation', __('validation.email_confirmation'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->user->delete();
|
||||
|
||||
session()->flash('success', __('messages.user_deleted'));
|
||||
$this->redirect(route('admin.users.index'));
|
||||
}
|
||||
|
||||
public function resetPassword(): void
|
||||
{
|
||||
$newPassword = Str::random(12);
|
||||
|
||||
$this->user->update([
|
||||
'password' => Hash::make($newPassword),
|
||||
]);
|
||||
|
||||
// Send new credentials email
|
||||
$this->user->notify(new PasswordResetByAdminNotification($newPassword));
|
||||
|
||||
// Log action
|
||||
AdminLog::create([
|
||||
'admin_id' => auth()->id(),
|
||||
'action_type' => 'password_reset',
|
||||
'target_type' => 'user',
|
||||
'target_id' => $this->user->id,
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
session()->flash('success', __('messages.password_reset_sent'));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Delete Confirmation Modal
|
||||
```blade
|
||||
<flux:modal wire:model="showDeleteModal">
|
||||
<flux:heading>{{ __('messages.confirm_delete') }}</flux:heading>
|
||||
|
||||
<flux:callout variant="danger">
|
||||
{{ __('messages.delete_warning') }}
|
||||
</flux:callout>
|
||||
|
||||
<p>{{ __('messages.will_be_deleted') }}</p>
|
||||
<ul class="list-disc ps-5 mt-2">
|
||||
<li>{{ __('messages.all_consultations') }}</li>
|
||||
<li>{{ __('messages.all_timelines') }}</li>
|
||||
<li>{{ __('messages.all_notifications') }}</li>
|
||||
</ul>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('messages.type_email_to_confirm', ['email' => $user->email]) }}</flux:label>
|
||||
<flux:input wire:model="deleteConfirmation" />
|
||||
<flux:error name="deleteConfirmation" />
|
||||
</flux:field>
|
||||
|
||||
<div class="flex gap-3 mt-4">
|
||||
<flux:button wire:click="$set('showDeleteModal', false)">
|
||||
{{ __('messages.cancel') }}
|
||||
</flux:button>
|
||||
<flux:button variant="danger" wire:click="delete">
|
||||
{{ __('messages.delete_permanently') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</flux:modal>
|
||||
```
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] Deactivate prevents login but preserves data
|
||||
- [ ] Reactivate restores login ability
|
||||
- [ ] Delete permanently removes all user data
|
||||
- [ ] Delete requires email confirmation
|
||||
- [ ] Password reset sends new credentials
|
||||
- [ ] Visual indicators show account status
|
||||
- [ ] Audit logging for all actions
|
||||
- [ ] Email notifications sent appropriately
|
||||
- [ ] Bilingual support complete
|
||||
- [ ] Tests for all lifecycle states
|
||||
- [ ] Code formatted with Pint
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Story 2.1:** Individual client management
|
||||
- **Story 2.2:** Company client management
|
||||
- **Epic 3:** Consultations (cascade delete)
|
||||
- **Epic 4:** Timelines (cascade delete)
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
- **Primary Risk:** Accidental permanent deletion
|
||||
- **Mitigation:** Strong confirmation dialog, email confirmation, audit log
|
||||
- **Rollback:** Not possible for delete - warn user clearly
|
||||
|
||||
## Estimation
|
||||
|
||||
**Complexity:** Medium
|
||||
**Estimated Effort:** 4-5 hours
|
||||
Reference in New Issue
Block a user