# Story 6.8: System Settings

## Epic Reference
**Epic 6:** Admin Dashboard

## User Story
As an **admin**,
I want **to configure system-wide settings**,
So that **I can customize the platform to my needs**.

## Dependencies
- **Story 1.2:** Authentication & Role System (admin auth, User model)
- **Story 8.1:** Email Infrastructure Setup (mail configuration for test email)

## Navigation Context
- Accessible from admin dashboard sidebar/navigation
- Route: `/admin/settings`
- Named route: `admin.settings`

## Acceptance Criteria

### Profile Settings
- [x] Admin name
- [x] Email
- [x] Password change
- [x] Preferred language

### Email Settings
- [x] Display current sender email from `config('mail.from.address')`
- [x] Display current sender name from `config('mail.from.name')`
- [x] "Send Test Email" button that sends a test email to admin's email address
- [x] Success/error feedback after test email attempt

### Notification Preferences (Future Enhancement - Not in Scope)
> **Note:** The following are documented for future consideration but are NOT required for this story's completion. All admin notifications are currently mandatory per PRD.
- Toggle admin notifications (future)
- Summary email frequency (future)

### Behavior
- [x] Settings saved and applied immediately
- [x] Validation for all inputs
- [x] Flash messages for success/error states
- [x] Password fields cleared after successful update

## Technical Notes

### Database Migration Required
Add `preferred_language` column to users table:

```php
// database/migrations/xxxx_add_preferred_language_to_users_table.php
Schema::table('users', function (Blueprint $table) {
    $table->string('preferred_language', 2)->default('ar')->after('remember_token');
});
```

### Files to Create/Modify

| File | Action | Purpose |
|------|--------|---------|
| `resources/views/livewire/admin/settings.blade.php` | Create | Main settings Volt component |
| `app/Mail/TestEmail.php` | Create | Test email mailable |
| `resources/views/emails/test.blade.php` | Create | Test email template |
| `database/migrations/xxxx_add_preferred_language_to_users_table.php` | Create | Migration |
| `routes/web.php` | Modify | Add admin settings route |
| `app/Models/User.php` | Modify | Add `preferred_language` to fillable |

### TestEmail Mailable

```php
// app/Mail/TestEmail.php
namespace App\Mail;

use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;

class TestEmail extends Mailable
{
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: __('messages.test_email_subject'),
        );
    }

    public function content(): Content
    {
        return new Content(
            view: 'emails.test',
        );
    }
}
```

### Volt Component Structure

```php
<?php
// resources/views/livewire/admin/settings.blade.php

use App\Mail\TestEmail;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rule;
use Livewire\Volt\Component;

new class extends Component {
    public string $name = '';
    public string $email = '';
    public string $current_password = '';
    public string $password = '';
    public string $password_confirmation = '';
    public string $preferred_language = 'ar';

    public function mount(): void
    {
        $user = auth()->user();
        $this->name = $user->name;
        $this->email = $user->email;
        $this->preferred_language = $user->preferred_language ?? 'ar';
    }

    public function updateProfile(): void
    {
        $this->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', Rule::unique('users')->ignore(auth()->id())],
            'preferred_language' => ['required', 'in:ar,en'],
        ]);

        auth()->user()->update([
            'name' => $this->name,
            'email' => $this->email,
            'preferred_language' => $this->preferred_language,
        ]);

        session()->flash('success', __('messages.profile_updated'));
    }

    public function updatePassword(): void
    {
        $this->validate([
            'current_password' => ['required', 'current_password'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);

        auth()->user()->update([
            'password' => Hash::make($this->password),
        ]);

        $this->reset(['current_password', 'password', 'password_confirmation']);
        session()->flash('success', __('messages.password_updated'));
    }

    public function sendTestEmail(): void
    {
        try {
            Mail::to(auth()->user())->send(new TestEmail());
            session()->flash('success', __('messages.test_email_sent'));
        } catch (\Exception $e) {
            session()->flash('error', __('messages.test_email_failed'));
        }
    }
}; ?>

<div>
    {{-- UI Template Here --}}
</div>
```

### Edge Cases & Error Handling
- **Wrong current password:** Validation rule `current_password` handles this automatically
- **Duplicate email:** `Rule::unique` with `ignore(auth()->id())` prevents self-collision
- **Email send failure:** Wrap in try/catch, show user-friendly error message
- **Empty preferred_language:** Default to 'ar' in mount() if null

## Testing Requirements

### Test File
`tests/Feature/Admin/SettingsTest.php`

### Test Scenarios

**Profile Update Tests:**
```php
test('admin can view settings page', function () {
    $admin = User::factory()->create();

    $this->actingAs($admin)
        ->get(route('admin.settings'))
        ->assertOk()
        ->assertSeeLivewire('admin.settings');
});

test('admin can update profile information', function () {
    $admin = User::factory()->create();

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->set('name', 'Updated Name')
        ->set('email', 'updated@example.com')
        ->set('preferred_language', 'en')
        ->call('updateProfile')
        ->assertHasNoErrors();

    expect($admin->fresh())
        ->name->toBe('Updated Name')
        ->email->toBe('updated@example.com')
        ->preferred_language->toBe('en');
});

test('profile update validates required fields', function () {
    $admin = User::factory()->create();

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->set('name', '')
        ->set('email', '')
        ->call('updateProfile')
        ->assertHasErrors(['name', 'email']);
});

test('profile update prevents duplicate email', function () {
    $existingUser = User::factory()->create(['email' => 'taken@example.com']);
    $admin = User::factory()->create();

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->set('email', 'taken@example.com')
        ->call('updateProfile')
        ->assertHasErrors(['email']);
});
```

**Password Update Tests:**
```php
test('admin can update password with correct current password', function () {
    $admin = User::factory()->create([
        'password' => Hash::make('old-password'),
    ]);

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->set('current_password', 'old-password')
        ->set('password', 'new-password')
        ->set('password_confirmation', 'new-password')
        ->call('updatePassword')
        ->assertHasNoErrors();

    expect(Hash::check('new-password', $admin->fresh()->password))->toBeTrue();
});

test('password update fails with wrong current password', function () {
    $admin = User::factory()->create([
        'password' => Hash::make('correct-password'),
    ]);

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->set('current_password', 'wrong-password')
        ->set('password', 'new-password')
        ->set('password_confirmation', 'new-password')
        ->call('updatePassword')
        ->assertHasErrors(['current_password']);
});

test('password update requires confirmation match', function () {
    $admin = User::factory()->create([
        'password' => Hash::make('old-password'),
    ]);

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->set('current_password', 'old-password')
        ->set('password', 'new-password')
        ->set('password_confirmation', 'different-password')
        ->call('updatePassword')
        ->assertHasErrors(['password']);
});
```

**Test Email Tests:**
```php
test('admin can send test email', function () {
    Mail::fake();
    $admin = User::factory()->create();

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->call('sendTestEmail')
        ->assertHasNoErrors();

    Mail::assertSent(TestEmail::class, fn ($mail) =>
        $mail->hasTo($admin->email)
    );
});

test('test email failure shows error message', function () {
    Mail::fake();
    Mail::shouldReceive('to->send')->andThrow(new \Exception('SMTP error'));

    $admin = User::factory()->create();

    Volt::test('admin.settings')
        ->actingAs($admin)
        ->call('sendTestEmail')
        ->assertSessionHas('error');
});
```

## Definition of Done
- [x] Migration created and run for `preferred_language` column
- [x] User model updated with `preferred_language` in fillable
- [x] Settings Volt component created at `resources/views/livewire/admin/settings.blade.php`
- [x] TestEmail mailable created at `app/Mail/TestEmail.php`
- [x] Test email template created at `resources/views/emails/test.blade.php`
- [x] Route added: `Route::get('/admin/settings', ...)->name('admin.settings')`
- [x] Profile update works with validation
- [x] Password change works with current password verification
- [x] Language preference persists across sessions
- [x] Test email sends successfully (or shows error on failure)
- [x] Email settings display current sender info from config
- [x] All flash messages display correctly (success/error)
- [x] UI follows Flux UI component patterns
- [x] All tests pass (`php artisan test --filter=SettingsTest`)
- [x] Code formatted with Pint

## Estimation
**Complexity:** Medium | **Effort:** 3-4 hours

---

## Dev Agent Record

### Status
Ready for Review

### Agent Model Used
Claude Opus 4.5

### File List
| File | Action | Purpose |
|------|--------|---------|
| `app/Mail/TestEmail.php` | Created | Test email mailable class |
| `resources/views/emails/test.blade.php` | Created | Test email template |
| `resources/views/livewire/settings/profile.blade.php` | Modified | Added preferred_language and test email (admin-only) |
| `lang/en/admin.php` | Modified | Added system settings translations |
| `lang/ar/admin.php` | Modified | Added system settings translations (Arabic) |
| `lang/en/emails.php` | Modified | Added test email translations |
| `lang/ar/emails.php` | Modified | Added test email translations (Arabic) |
| `tests/Feature/Settings/ProfileUpdateTest.php` | Modified | Added preferred_language and test email tests |

### Completion Notes
- Migration for `preferred_language` already existed in base users table migration
- User model already had `preferred_language` in fillable array
- All tests pass covering profile update, preferred language, and test email functionality
- Used Flux UI components for consistent design
- Bilingual support (AR/EN) implemented for all user-facing strings
- **Refactored:** Consolidated into existing `/settings/profile` instead of creating duplicate `/admin/settings`

### Change Log
| Date | Change | Reason |
|------|--------|--------|
| 2025-12-28 | Initial implementation | Story 6.8 development |
| 2025-12-28 | Refactored to use /settings/profile | QA review identified duplication with existing settings pages |

## QA Results

### Review Date: 2025-12-28

### Reviewed By: Quinn (Test Architect)

### Risk Assessment
- **Risk Level:** Low
- **Escalation Triggers:** None
- **Review Depth:** Standard

### Code Quality Assessment

**Overall Assessment: EXCELLENT**

After refactoring, the implementation extends the existing `/settings/profile` component cleanly:

1. **Volt Component (`resources/views/livewire/settings/profile.blade.php`):**
   - Extended existing component with preferred_language field
   - Test email section added with `@if(auth()->user()->isAdmin())` guard
   - Exception handling for email sending with user-friendly error messages
   - No code duplication

2. **TestEmail Mailable (`app/Mail/TestEmail.php`):**
   - Follows Laravel mailable conventions
   - Properly accepts locale parameter for bilingual support
   - Clean envelope/content separation

3. **Email Template (`resources/views/emails/test.blade.php`):**
   - Proper RTL support for Arabic locale
   - Uses markdown mail component

4. **Translations:**
   - Complete bilingual support (AR/EN) in both `lang/*/admin.php` and `lang/*/emails.php`

### Requirements Traceability (Given-When-Then)

| AC | Requirement | Test Coverage | Status |
|----|------------|---------------|--------|
| Profile Settings - Name | User can update name | `test profile information can be updated` | ✓ |
| Profile Settings - Email | User can update email | `test profile information can be updated` | ✓ |
| Profile Settings - Language | User can set preferred language | `test user can update preferred language`, `test preferred language validates allowed values` | ✓ |
| Email Settings - Sender Display | Admin sees current sender from config | `test admin sees email configuration section` | ✓ |
| Email Settings - Test Email | Admin can send test email | `test admin can send test email`, `test test email uses admin preferred language` | ✓ |
| Access Control | Test email is admin-only | `test non-admin cannot send test email` | ✓ |

### Refactoring Performed

**ARCH-001 Resolution:** Consolidated settings into existing `/settings/profile`

| Action | File | Change |
|--------|------|--------|
| Modified | `resources/views/livewire/settings/profile.blade.php` | Added preferred_language and test email (admin-only) |
| Deleted | `resources/views/livewire/admin/settings.blade.php` | Removed duplicate component |
| Modified | `routes/web.php` | Removed admin.settings route |
| Deleted | `tests/Feature/Admin/SettingsTest.php` | Removed, tests consolidated |
| Modified | `tests/Feature/Settings/ProfileUpdateTest.php` | Added new feature tests |

### Compliance Check

- Coding Standards: ✓ Pint passes with no issues
- Project Structure: ✓ Extends existing component, no duplication
- Testing Strategy: ✓ 12 tests in ProfileUpdateTest covering all acceptance criteria
- All ACs Met: ✓ All acceptance criteria are implemented and tested

### Improvements Checklist

- [x] Preferred language field added to profile
- [x] Test email section (admin-only) added
- [x] No code duplication
- [x] All tests pass
- [x] Bilingual support complete (AR/EN)
- [x] Flux UI components used consistently

### Security Review

- **Authentication:** ✓ Profile page protected by `auth` and `active` middleware
- **Authorization:** ✓ Test email method checks `isAdmin()` before sending
- **Input Validation:** ✓ All inputs properly validated
- **No Security Concerns Found**

### Performance Considerations

- No performance issues identified
- Component is lightweight with minimal database queries

### Gate Status

Gate: **PASS** → docs/qa/gates/6.8-system-settings.yml

### Recommended Status

✓ **Ready for Done** - Architectural concern resolved through refactoring. All acceptance criteria met with clean, non-duplicated implementation.
