7.8 KiB
7.8 KiB
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
// 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
// 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
// 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
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
<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