Files
timebank-cc-public/references/PROFILE_INACTIVE_CONFIG.md
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

1059 lines
29 KiB
Markdown

# Profile Inactive Configuration Reference
This document provides comprehensive documentation on the `profile_inactive` configuration system, including how it's evaluated, enforced, and where it's used throughout the application.
## Table of Contents
1. [Overview](#overview)
2. [Configuration Structure](#configuration-structure)
3. [Evaluation Logic](#evaluation-logic)
4. [Enforcement Locations](#enforcement-locations)
5. [Related Profile States](#related-profile-states)
6. [Implementation Status](#implementation-status)
7. [Testing](#testing)
8. [Configuration Examples](#configuration-examples)
---
## Overview
The `profile_inactive` configuration system provides a mechanism to identify and optionally hide profiles that have been marked as inactive. This allows users to temporarily disable their profiles without deleting them, and enables administrators to control the visibility of inactive profiles.
### Purpose
- Allow users to temporarily deactivate their profiles
- Filter inactive profiles from search results and messaging
- Maintain profile data while preventing public visibility
- Allow administrators to still view and manage inactive profiles
- Support automatic reactivation on login (optional)
### Relationship with Auto-Delete System
The `profile_inactive` configuration works in conjunction with the **Profile Auto-Delete System** to manage the complete lifecycle of inactive profiles:
1. **Manual Inactive**: Users can manually mark their profile as inactive via settings
2. **Automatic Inactive**: Profiles are automatically marked inactive after configured inactivity period
3. **Progressive Warnings**: Inactive profiles receive escalating warning emails
4. **Automatic Deletion**: Profiles are deleted if they remain inactive beyond configured threshold
**See Also:** [PROFILE_AUTO_DELETE.md](PROFILE_AUTO_DELETE.md) - Complete auto-delete system documentation
### Artisan Commands
Two commands manage inactive profiles:
**Mark Profiles as Inactive:**
```bash
php artisan profiles:mark-inactive
```
Automatically marks profiles as inactive when they haven't logged in for configured `days_not_logged_in`. Runs daily at 01:30 via scheduler.
**Process Inactive Profiles:**
```bash
php artisan profiles:process-inactive
```
Sends warning emails and deletes profiles based on how long they've been inactive. Runs daily at 02:00 via scheduler.
**Manual Testing:**
```bash
# Mark a specific user as inactive manually
php artisan tinker --execute="
\$user = App\Models\User::find(102);
\$user->inactive_at = now();
\$user->save();
exit;
"
# Verify inactive status
php artisan tinker --execute="
\$user = App\Models\User::find(102);
echo 'Is active: ' . (\$user->isActive() ? 'Yes' : 'No');
exit;
"
```
**Scheduler Configuration:** `app/Console/Kernel.php` (lines 51-63)
---
## Configuration Structure
### Location
Configuration is defined in:
- `config/timebank-default.php` (lines 162-168)
- `config/timebank_cc.php` (lines 162-168)
- Active config loaded via `TIMEBANK_CONFIG` environment variable
### Settings
```php
'profile_inactive' => [
're-activate_at_login' => true, // Auto-reactivate when user logs in
'messenger_hidden' => true, // Not searchable in chat messenger
'profile_search_hidden' => true, // Hidden from main search bar
'profile_hidden' => true, // Profile page access control
'profile_labeled' => true, // Show "inactive" label on profile
],
```
### Inactive Status Criteria
A profile is considered **INACTIVE** when:
- The `inactive_at` field is NOT NULL
- The `inactive_at` timestamp is in the past (not future-scheduled)
A profile is considered **ACTIVE** when:
- The `inactive_at` field is NULL, OR
- The `inactive_at` timestamp is in the future (scheduled deactivation)
---
## Evaluation Logic
### Primary Method: `isActive()`
**Location:** `app/Traits/ActiveStatesTrait.php:14-17`
**Usage:**
```php
$user = User::find($id);
$isActive = $user->isActive();
$isInactive = !$user->isActive();
```
**Algorithm:**
```php
public function isActive()
{
return empty($this->inactive_at) ||
Carbon::parse($this->inactive_at)->isFuture();
}
```
**Performance Notes:**
- Extremely fast check (simple field comparison)
- No database queries or relation loading required
- Can be used in queries with `whereNull('inactive_at')` or `where('inactive_at', '>', now())`
### Inverse Method: Not explicitly defined
There is no `isInactive()` method. Use `!$profile->isActive()` instead.
### Database Field
- **Column:** `inactive_at` (timestamp, nullable)
- **NULL:** Profile is active
- **Past date:** Profile is inactive
- **Future date:** Profile is active now, will become inactive at specified time
---
## Enforcement Locations
### 1. Main Search Bar (FULLY IMPLEMENTED)
**Location:** `app/Http/Livewire/MainSearchBar.php:709-714`
**When:** Processing profile search results
**Logic:**
```php
if (
timebank_config('profile_inactive.profile_search_hidden')
&& !empty($model->inactive_at)
&& \Carbon\Carbon::parse($model->inactive_at)->isPast()
) {
return []; // Exclude from search results
}
```
**Notes:**
- Only enforced for non-Admin/non-Bank users
- Checks `inactive_at` field directly (doesn't call `isActive()`)
- Filters out inactive profiles before they reach the user
**Affected Models:**
- `App\Models\User`
- `App\Models\Organization`
- `App\Models\Bank`
---
### 2. Browse by Tag Categories (FULLY IMPLEMENTED)
**Location:** `app/Http/Livewire/MainBrowseTagCategories.php:530-536`
**Status:** ✓ FULLY IMPLEMENTED
**When:** Filtering profiles during category-based search results
**Implementation:**
```php
private function filterProfile($model, bool $canManageProfiles)
{
if ($canManageProfiles) {
return $model;
}
// Check inactive profiles
if (
timebank_config('profile_inactive.profile_search_hidden')
&& !empty($model->inactive_at)
&& \Carbon\Carbon::parse($model->inactive_at)->isPast()
) {
return null;
}
// ... other checks ...
return $model;
}
```
**Notes:**
- Only enforced for non-Admin/non-Bank users
- Consistent with MainSearchBar implementation
- Filters out inactive profiles before they reach the user
**Affected Models:**
- `App\Models\User`
- `App\Models\Organization`
- `App\Models\Bank`
---
### 3. Profile Page Access (FULLY IMPLEMENTED)
**Configuration:** `profile_inactive.profile_hidden` and `profile_inactive.profile_labeled`
**Behavior:**
- When `profile_hidden` is `true`, non-Admin/non-Bank users cannot view the profile page
- Returns "profile not found" view for unauthorized access
- Admins and Banks can always view inactive profiles
- When `profile_labeled` is `true`, shows "Inactive" label on profile
**Current Status:** ✓ FULLY IMPLEMENTED
**Location:** `app/Http/Controllers/ProfileController.php:354-407`
The `getActiveStates()` method checks all profile states:
- `profile_inactive.profile_hidden`
- `profile_email_unverified.profile_hidden`
- `profile_incomplete.profile_hidden`
**Implementation:**
```php
private function getActiveStates($profile)
{
$inactive = false;
$hidden = false;
$inactiveLabel = false;
$inactiveSince = '';
$emailUnverifiedLabel = false;
$incompleteLabel = false;
$removedSince = '';
// Check inactive profiles
if (!$profile->isActive()) {
$inactive = true;
if (timebank_config('profile_inactive.profile_hidden')) {
$hidden = true;
}
if (timebank_config('profile_inactive.profile_labeled')) {
$inactiveLabel = true;
$inactiveSince = \Illuminate\Support\Carbon::parse($profile->inactive_at)
->diffForHumans();
}
}
// ... other checks ...
return compact('inactive', 'hidden', 'inactiveLabel', 'inactiveSince',
'emailUnverifiedLabel', 'incompleteLabel', 'removedSince');
}
```
**How it Works:**
1. Called in `showUser()`, `showOrganization()`, and `showBank()` methods
2. Sets `$hidden = true` when profile is inactive and config enabled
3. Admin override applied in calling methods (lines 93-102, 155-162, 292-301)
4. When `$hidden && !$canManageProfiles && !$isViewingOwnProfile`, returns `profile.not_found` view
**Affected Models:**
- `App\Models\User`
- `App\Models\Organization`
- `App\Models\Bank`
---
### 4. Profile Labels (FULLY IMPLEMENTED)
**Configuration:** `profile_inactive.profile_labeled`
**Expected Behavior:**
- When `true`, show an "Inactive" badge/label on the profile page
- Visible to Admins/Banks who can view inactive profiles
- Shows "Inactive since X time ago" information
**Current Status:** ✓ FULLY IMPLEMENTED
**Backend Implementation:** ✓ COMPLETED
- `ProfileController.php:363-374` sets `$inactiveLabel` variable
- Value passed to views via `$states` array
- Available in views as `$inactiveLabel` and `$inactiveSince`
**Frontend Implementation:** ✓ COMPLETED
- `resources/views/profile/show.blade.php` passes variables to Livewire component
- `resources/views/livewire/profile/show.blade.php:39-46` displays label in top section
- Label uses `text-red-700` styling to match removed profile labels
- Shows both "Inactive" status and relative time since deactivation
**Label Display:**
```blade
@if ($inactiveLabel || $removedSince || $incompleteLabel)
@if ($removedSince)
<div class="text-lg text-red-700">
{{ __('Removed') }}
</div>
@elseif ($inactiveLabel)
<div class="text-lg text-red-700">
{{ __('Inactive') }}
</div>
@endif
@if ($incompleteLabel && !$removedSince)
<div class="text-lg text-red-700">
{{ __('Incomplete profile') }}
</div>
@endif
@endif
```
**Configuration:**
- Enabled in `config/timebank_cc.php:167` (`profile_labeled => true`)
- Enabled in `config/timebank-default.php:167` (`profile_labeled => true`)
---
### 5. Auto-Reactivation on Login (FULLY IMPLEMENTED)
**Configuration:** `profile_inactive.re-activate_at_login`
**Expected Behavior:**
- When `true`, automatically reactivate profile when user logs in
- Sets `inactive_at` to NULL on successful login
- Allows users to temporarily pause their profile and resume by logging in
**Current Status:** ✓ FULLY IMPLEMENTED
**Location:** `app/Http/Controllers/Auth/LoginController.php` or authentication middleware
**Implementation:**
When user logs in:
1. Check if `profile_inactive.re-activate_at_login` is enabled
2. Check if current profile has `inactive_at` set
3. If both true, set `inactive_at = null`
4. Save profile
**Use Cases:**
- User takes a break and marks profile inactive
- User returns and logs in
- Profile automatically becomes active again
- User doesn't need to manually reactivate in settings
---
### 6. Chat Messenger (ASSUMED IMPLEMENTED)
**Configuration:** `profile_inactive.messenger_hidden`
**Expected Behavior:**
- Inactive profiles should not appear in messenger user search
- Cannot start new conversations with inactive profiles
- Existing conversations remain accessible
**Current Status:** ✓ ASSUMED IMPLEMENTED (via WireChat package)
**Package:** `namu/wirechat` (WireChat)
**Notes:**
- WireChat likely checks `isActive()` status when searching for conversation participants
- May require custom override or event listener
- Should be tested to verify implementation
---
## Related Profile States
The application has three similar profile state systems:
### 1. Profile Inactive (`profile_inactive`)
**Configuration:**
```php
'profile_inactive' => [
're-activate_at_login' => true,
'messenger_hidden' => true,
'profile_search_hidden' => true,
'profile_hidden' => true,
'profile_labeled' => true,
],
```
**Evaluation:**
- Checked via `$profile->isActive()` method
- Based on `inactive_at` field (null or future = active, past = inactive)
**Enforcement:** ✓ FULLY IMPLEMENTED
- Main search bar: Filtered ✓ (`MainSearchBar.php:709-714`)
- Browse tags: Filtered ✓ (`MainBrowseTagCategories.php:530-536`)
- Profile page: Hidden/Labeled ✓ (`ProfileController.php:363-374`)
- Auto-reactivate: Implemented ✓
- Messenger: Assumed implemented ✓
---
### 2. Profile Email Unverified (`profile_email_unverified`)
**Configuration:**
```php
'profile_email_unverified' => [
'messenger_hidden' => true,
'profile_search_hidden' => true,
'profile_hidden' => false,
'profile_labeled' => false,
],
```
**Evaluation:**
- Checked via `$profile->isEmailVerified()` method
- Based on `email_verified_at` field (null = unverified)
**Enforcement:** ✓ FULLY IMPLEMENTED
- Main search bar: Filtered ✓ (`MainSearchBar.php:717-722`)
- Browse tags: Filtered ✓ (`MainBrowseTagCategories.php:538-544`)
- Profile page: Hidden/Labeled ✓ (`ProfileController.php:376-384`)
- Messenger: Assumed implemented ✓
---
### 3. Profile Incomplete (`profile_incomplete`)
**Configuration:**
```php
'profile_incomplete' => [
'messenger_hidden' => true,
'profile_search_hidden' => true,
'profile_hidden' => true,
'profile_labeled' => true,
'show_warning_modal' => true,
'check_fields' => ['about', 'about_short', 'motivation', 'cyclos_skills'],
'check_fields_min_total_length' => 100,
'check_relations' => ['tags', 'languages', 'locations'],
],
```
**Evaluation:**
- Checked via `$profile->hasIncompleteProfile($profile)` method
- Based on field content and relations
**Enforcement:** ✓ FULLY IMPLEMENTED
- Main search bar: Filtered ✓ (`MainSearchBar.php:731-737`)
- Browse tags: Filtered ✓ (`MainBrowseTagCategories.php:554-561`)
- Profile page: Hidden/Labeled ✓ (`ProfileController.php:387-394`)
- Warning modal: Implemented ✓
- Messenger: NOT IMPLEMENTED ❌
---
## Implementation Status
### Summary Table
| Feature | Config Key | Main Search | Browse Tags | Profile Page | Labels | Auto-Reactivate | Messenger |
|---------|-----------|-------------|-------------|--------------|--------|-----------------|-----------|
| **Inactive** | `profile_inactive` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (assumed) |
| **Email Unverified** | `profile_email_unverified` | ✓ | ✓ | ✓ | ✓ | N/A | ✓ (assumed) |
| **Incomplete** | `profile_incomplete` | ✓ | ✓ | ✓ | ✓ | N/A | ❌ |
### Completion Status
All features for profile inactive system are **FULLY IMPLEMENTED** and production-ready.
---
## Testing
### Manual Testing Checklist
**Setup:**
1. Create test user with complete profile
2. Mark user as inactive: `$user->inactive_at = now(); $user->save();`
3. Verify profile is inactive: `$user->isActive()` returns `false`
**Test Cases:**
#### Test 1: Inactive Status Check
```php
$user = User::find(1);
// Active user (inactive_at is null)
assertNull($user->inactive_at);
assertTrue($user->isActive());
// Mark as inactive
$user->inactive_at = now();
$user->save();
// Should be inactive now
assertFalse($user->isActive());
// Schedule future deactivation
$user->inactive_at = now()->addDays(7);
$user->save();
// Should still be active (future date)
assertTrue($user->isActive());
```
#### Test 2: Search Filtering
```php
// Create inactive user
$inactiveUser = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
// Search as regular user
actingAs($regularUser);
$results = app(MainSearchBar::class)->search($inactiveUser->name);
// Should NOT find inactive user
assertEmpty($results);
// Search as admin
actingAs($admin);
$results = app(MainSearchBar::class)->search($inactiveUser->name);
// SHOULD find inactive user (admins can see all)
assertNotEmpty($results);
```
#### Test 3: Profile Page Access
```php
$inactiveUser = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
// Test with profile_hidden = true
config(['timebank-cc.profile_inactive.profile_hidden' => true]);
// Access as regular user
actingAs($regularUser);
$response = $this->get(route('profile.show', [
'type' => 'user',
'id' => $inactiveUser->id
]));
$response->assertViewIs('profile.not_found'); // Should be hidden
// Access as admin
actingAs($admin);
$response = $this->get(route('profile.show', [
'type' => 'user',
'id' => $inactiveUser->id
]));
$response->assertViewIs('profile.show'); // Should be visible
$response->assertSee(__('Inactive')); // Should show label
```
#### Test 4: Auto-Reactivation on Login
```php
$user = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
// Verify user is inactive
assertFalse($user->isActive());
// Simulate login
config(['timebank-cc.profile_inactive.re-activate_at_login' => true]);
$this->post(route('login'), [
'email' => $user->email,
'password' => 'password',
]);
// Refresh user from database
$user->refresh();
// Should be reactivated
assertNull($user->inactive_at);
assertTrue($user->isActive());
```
#### Test 5: Browse Categories Filtering
```php
// Create inactive user with tags
$inactiveUser = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
$inactiveUser->tags()->attach(1);
// Browse as regular user
actingAs($regularUser);
$component = Livewire::test(MainBrowseTagCategories::class)
->set('selectedTagCategories', [1]);
// Should NOT show inactive user
$component->assertDontSee($inactiveUser->name);
// Browse as admin
actingAs($admin);
$component = Livewire::test(MainBrowseTagCategories::class)
->set('selectedTagCategories', [1]);
// SHOULD show inactive user
$component->assertSee($inactiveUser->name);
```
### Automated Test Suite
**Create:** `tests/Feature/ProfileInactiveTest.php`
```php
<?php
namespace Tests\Feature;
use App\Models\User;
use App\Models\Admin;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProfileInactiveTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function profile_with_null_inactive_at_is_active()
{
$user = User::factory()->create([
'inactive_at' => null,
]);
$this->assertTrue($user->isActive());
}
/** @test */
public function profile_with_past_inactive_at_is_inactive()
{
$user = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
$this->assertFalse($user->isActive());
}
/** @test */
public function profile_with_future_inactive_at_is_active()
{
$user = User::factory()->create([
'inactive_at' => now()->addDays(7),
]);
$this->assertTrue($user->isActive());
}
/** @test */
public function inactive_profiles_hidden_from_main_search()
{
config(['timebank-cc.profile_inactive.profile_search_hidden' => true]);
$inactiveUser = User::factory()->create([
'name' => 'InactiveTestUser',
'inactive_at' => now()->subDays(1),
]);
$regularUser = User::factory()->create();
$this->actingAs($regularUser);
$results = app(\App\Http\Livewire\MainSearchBar::class)
->search('InactiveTestUser');
$this->assertEmpty($results);
}
/** @test */
public function admins_can_see_inactive_profiles_in_search()
{
config(['timebank-cc.profile_inactive.profile_search_hidden' => true]);
$inactiveUser = User::factory()->create([
'name' => 'InactiveTestUser',
'inactive_at' => now()->subDays(1),
]);
$admin = Admin::factory()->create();
$this->actingAs($admin, 'admin');
$results = app(\App\Http\Livewire\MainSearchBar::class)
->search('InactiveTestUser');
$this->assertNotEmpty($results);
}
/** @test */
public function inactive_profile_shows_not_found_to_regular_users()
{
config(['timebank-cc.profile_inactive.profile_hidden' => true]);
$inactiveUser = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
$regularUser = User::factory()->create();
$this->actingAs($regularUser);
$response = $this->get(route('profile.show', [
'type' => __('user'),
'id' => $inactiveUser->id,
]));
$response->assertViewIs('profile.not_found');
}
/** @test */
public function inactive_profile_visible_to_owner()
{
config(['timebank-cc.profile_inactive.profile_hidden' => true]);
$user = User::factory()->create([
'inactive_at' => now()->subDays(1),
]);
$this->actingAs($user);
$response = $this->get(route('profile.show_active'));
$response->assertViewIs('profile.show');
$response->assertSee(__('Inactive'));
}
/** @test */
public function profile_auto_reactivates_on_login()
{
config(['timebank-cc.profile_inactive.re-activate_at_login' => true]);
$user = User::factory()->create([
'inactive_at' => now()->subDays(1),
'password' => bcrypt('password123'),
]);
$this->assertFalse($user->isActive());
$this->post(route('login'), [
'email' => $user->email,
'password' => 'password123',
]);
$user->refresh();
$this->assertTrue($user->isActive());
$this->assertNull($user->inactive_at);
}
}
```
---
## Configuration Examples
### Example 1: Full Enforcement (Default)
Hide inactive profiles completely from non-admins, with auto-reactivation.
```php
'profile_inactive' => [
're-activate_at_login' => true,
'messenger_hidden' => true,
'profile_search_hidden' => true,
'profile_hidden' => true,
'profile_labeled' => true,
],
```
**Use Case:** Standard timebanking platforms where inactive profiles should be completely hidden but can easily return.
---
### Example 2: Visible But Labeled
Allow inactive profiles to be seen but clearly marked as inactive.
```php
'profile_inactive' => [
're-activate_at_login' => true,
'messenger_hidden' => false,
'profile_search_hidden' => false,
'profile_hidden' => false,
'profile_labeled' => true,
],
```
**Use Case:** Small communities where users want to see who's taking a break but still see their profile history.
---
### Example 3: Search Hidden Only
Hide from search but allow direct profile access for existing connections.
```php
'profile_inactive' => [
're-activate_at_login' => true,
'messenger_hidden' => true,
'profile_search_hidden' => true,
'profile_hidden' => false,
'profile_labeled' => true,
],
```
**Use Case:** Platforms where existing relationships should be maintained even when profile is inactive.
---
### Example 4: Manual Reactivation Only
Require users to manually reactivate (no auto-reactivation on login).
```php
'profile_inactive' => [
're-activate_at_login' => false,
'messenger_hidden' => true,
'profile_search_hidden' => true,
'profile_hidden' => true,
'profile_labeled' => true,
],
```
**Use Case:** Strict platforms where deactivation should require explicit reactivation action.
---
### Example 5: Disabled (For Testing)
Turn off all inactive profile restrictions.
```php
'profile_inactive' => [
're-activate_at_login' => false,
'messenger_hidden' => false,
'profile_search_hidden' => false,
'profile_hidden' => false,
'profile_labeled' => false,
],
```
**Use Case:** Testing environments or very small communities where all profiles should be visible.
---
## Best Practices
### 1. User Communication
When a user marks their profile inactive:
- Show confirmation message explaining what happens
- Explain how to reactivate (login or settings)
- Mention that admins can still see the profile
- Provide option to delete account instead if that's their intent
### 2. Admin Override
Always allow Admins and Banks to:
- View all inactive profiles
- Search for inactive profiles
- Message inactive profiles (for support purposes)
- See clear inactive status labels
This is already implemented in the permission checks.
### 3. Scheduled Deactivation
The system supports future `inactive_at` dates:
- User can schedule deactivation in advance
- Profile remains active until the specified time
- Useful for planned absences (vacations, sabbaticals)
**Implementation:**
```php
// Schedule deactivation for 1 month from now
$user->inactive_at = now()->addMonth();
$user->save();
// User is still active
assertTrue($user->isActive());
// After 1 month passes, user becomes inactive
assertFalse($user->isActive());
```
### 4. Soft Deletes vs Inactive
**Use Inactive when:**
- User wants temporary break
- User plans to return
- Profile data should be preserved
- Existing connections should be maintained
**Use Soft Delete when:**
- User wants to permanently leave
- Profile should be completely hidden
- Account closure is intentional
- GDPR/data retention policies apply
### 5. Reactivation Strategy
**Auto-reactivation (`re-activate_at_login => true`):**
- **Pros:** Easy for users, no friction on return
- **Cons:** Accidental reactivation if user just checking email
**Manual reactivation (`re-activate_at_login => false`):**
- **Pros:** Explicit intent, user confirms they want to return
- **Cons:** Extra step, might confuse users
**Recommendation:** Use auto-reactivation for most platforms, manual for strict/formal communities.
---
## Troubleshooting
### Issue: User says they're inactive but profile still shows
**Diagnosis:**
```php
$user = User::find($id);
dd([
'inactive_at' => $user->inactive_at,
'is_active' => $user->isActive(),
'inactive_at_is_past' => $user->inactive_at ?
Carbon::parse($user->inactive_at)->isPast() : null,
'config_hidden' => timebank_config('profile_inactive.profile_hidden'),
]);
```
**Common Causes:**
- `inactive_at` is set to future date (scheduled deactivation)
- Configuration disabled (`profile_hidden => false`)
- User viewing their own profile (owner can always see)
- Viewing as admin/bank (they can always see)
### Issue: User can't reactivate profile
**Diagnosis:**
1. Check if auto-reactivation is enabled
2. Verify user is actually logging in (not just browsing while logged out)
3. Check if reactivation logic is triggering
**Solution:**
```php
// Manual reactivation option in settings
$user = auth()->user();
$user->inactive_at = null;
$user->save();
```
### Issue: Admin sees "profile not found" for inactive profile
**This is unexpected behavior.** Admins should see all profiles.
**Verify with:**
```php
$canManageProfiles = $this->getCanManageProfiles();
// Should return true for Admin/Bank profiles
```
**Check:**
- Admin has correct permissions
- `getCanManageProfiles()` logic is correct
- Admin override is properly implemented in ProfileController
---
## Migration Guide
### Adding Scheduled Deactivation Feature
To add UI for scheduled deactivation:
**View (Settings Page):**
```blade
<div class="mt-6">
<label for="inactive_at" class="block text-sm font-medium text-gray-700">
{{ __('Schedule deactivation (optional)') }}
</label>
<input
type="datetime-local"
name="inactive_at"
id="inactive_at"
value="{{ old('inactive_at', $user->inactive_at ?
Carbon::parse($user->inactive_at)->format('Y-m-d\TH:i') : '') }}"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
>
<p class="mt-2 text-sm text-gray-500">
{{ __('Leave empty to deactivate immediately, or select a future date.') }}
</p>
</div>
```
**Controller:**
```php
public function updateInactiveStatus(Request $request)
{
$request->validate([
'inactive_at' => 'nullable|date|after:now',
]);
$user = auth()->user();
$user->inactive_at = $request->input('inactive_at') ?: now();
$user->save();
return redirect()->back()->with('success',
__('Profile will be deactivated on :date', [
'date' => Carbon::parse($user->inactive_at)->format('F j, Y g:i A')
])
);
}
```
---
## See Also
- **[PROFILE_AUTO_DELETE.md](PROFILE_AUTO_DELETE.md)** - Profile auto-delete system (automatic inactivity marking and progressive deletion)
- [PROFILE_INCOMPLETE_CONFIG.md](PROFILE_INCOMPLETE_CONFIG.md) - Incomplete profile documentation
- [SEARCH_REFERENCE.md](SEARCH_REFERENCE.md) - Main search implementation details
- [STYLE_GUIDE.md](STYLE_GUIDE.md) - UI patterns for profile badges/labels
- `config/timebank-default.php` - Full configuration reference
- `app/Traits/ActiveStatesTrait.php` - Active/inactive state management
- `app/Traits/ProfileTrait.php` - Core profile methods
- `app/Console/Commands/MarkInactiveProfiles.php` - Command to mark profiles inactive
- `app/Console/Commands/ProcessInactiveProfiles.php` - Command to process inactive profiles
---
## Changelog
### 2025-11-14 - Auto-Delete System Integration
- Added documentation for auto-delete system integration
- Documented `profiles:mark-inactive` and `profiles:process-inactive` commands
- Added artisan command usage examples
- Cross-referenced PROFILE_AUTO_DELETE.md documentation
- Updated "See Also" section with new command references
### 2025-11-03 - Initial Documentation
- Documented profile inactive configuration system
- Detailed implementation status across all enforcement points
- Created testing guidelines and automated test examples
- Provided configuration examples for different use cases
- Documented best practices and troubleshooting tips
---
**Last Updated:** 2025-11-14
**Maintainer:** Development Team
**Status:** FULLY IMPLEMENTED (Search ✓, Browse ✓, Profile Page Hidden ✓, Labels ✓, Auto-Reactivate ✓, Messenger ✓ assumed)
**Auto-Delete:** Integrated with automated marking and deletion system