# Detailed Changes Summary - All Files

## 1. NEW FILE: Database Migration
**Path**: `database/migrations/2026_09_01_141422_add_password_changed_to_users.php`

**Action**: COPY THIS FILE TO LIVE SERVER

**Content**:
```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->timestamp('password_changed_at')->nullable()->after('password');
        });
    }

    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('password_changed_at');
        });
    }
};
```

**Then Run**: `php artisan migrate`

---

## 2. NEW FILE: Middleware
**Path**: `app/Http/Middleware/CheckPasswordChanged.php`

**Action**: COPY THIS FILE TO LIVE SERVER

**Content**: 26 lines
```php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckPasswordChanged
{
    public function handle(Request $request, Closure $next): Response
    {
        if (auth()->check() && auth()->user()->role === 'client') {
            if (is_null(auth()->user()->password_changed_at)) {
                if (!$request->is('client/change-password*')) {
                    return redirect()->route('client.change-password')
                        ->with('warning', 'For your security, please change your password on first login.');
                }
            }
        }

        return $next($request);
    }
}
```

---

## 3. UPDATED FILE: User Model
**Path**: `app/Models/User.php`

**Action**: COPY UPDATED FILE TO LIVE SERVER

**Changes Made**:

### Change 1 - Add to $fillable array (around line 50)
**OLD**:
```php
protected $fillable = [
    'name',
    'email',
    'password',
    ...
    'admin_otp_attempts',
];
```

**NEW**:
```php
protected $fillable = [
    'name',
    'email',
    'password',
    ...
    'admin_otp_attempts',
    'password_changed_at',  // ← ADD THIS LINE
];
```

### Change 2 - Add to casts array (around line 73)
**OLD**:
```php
protected function casts(): array
{
    return [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
        'permissions' => 'array',
        'two_factor_enabled' => 'boolean',
        'two_factor_expires_at' => 'datetime',
        'admin_2fa_enabled' => 'boolean',
        'admin_otp_expires_at' => 'datetime',
    ];
}
```

**NEW**:
```php
protected function casts(): array
{
    return [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
        'permissions' => 'array',
        'two_factor_enabled' => 'boolean',
        'two_factor_expires_at' => 'datetime',
        'admin_2fa_enabled' => 'boolean',
        'admin_otp_expires_at' => 'datetime',
        'password_changed_at' => 'datetime',  // ← ADD THIS LINE
    ];
}
```

---

## 4. UPDATED FILE: Routes
**Path**: `routes/web.php`

**Action**: COPY UPDATED FILE TO LIVE SERVER

**Changes Made**:

### Change 1 - Update middleware in client routes group (around line 1098)
**OLD**:
```php
Route::prefix('client')->middleware(['auth', 'role:client'])->group(function () {
```

**NEW**:
```php
Route::prefix('client')->middleware(['auth', 'role:client', \App\Http\Middleware\CheckPasswordChanged::class])->group(function () {
```

### Change 2 - Add password change routes (around line 1106)
**FIND THIS**:
```php
    Route::get('/', [ClientController::class, 'dashboard'])->name('client.dashboard');

    // Two-Factor Authentication settings
```

**ADD THESE LINES BETWEEN**:
```php
    Route::get('/', [ClientController::class, 'dashboard'])->name('client.dashboard');

    // Password Change Routes - Mandatory on first login
    Route::get('/change-password', [ClientController::class, 'showChangePassword'])->name('client.change-password');
    Route::post('/change-password', [ClientController::class, 'updatePassword'])->name('client.update-password');

    // Two-Factor Authentication settings
```

---

## 5. UPDATED FILE: ClientController
**Path**: `app/Http/Controllers/ClientController.php`

**Action**: COPY UPDATED FILE TO LIVE SERVER

**Changes Made**: Add 2 new methods at the end of the class (before final closing brace)

**ADD THIS CODE** (before the last `}` of the class):
```php
    /**
     * Show password change form
     */
    public function showChangePassword()
    {
        return view('client.change-password');
    }

    /**
     * Update password
     */
    public function updatePassword(Request $request)
    {
        $user = auth()->user();

        // Validate input
        $validated = $request->validate([
            'current_password' => ['required', 'string'],
            'password' => [
                'required',
                'string',
                'min:8',
                'regex:/[A-Z]/',  // At least one uppercase letter
                'regex:/[a-z]/',  // At least one lowercase letter
                'regex:/[0-9]/',  // At least one digit
                'confirmed',
            ],
        ], [
            'password.regex' => 'Password must contain uppercase letters, lowercase letters, and numbers.',
            'password.min' => 'Password must be at least 8 characters long.',
        ]);

        // Check if current password is correct
        if (!\Hash::check($request->current_password, $user->password)) {
            return back()->withErrors([
                'current_password' => 'The current password is incorrect.',
            ]);
        }

        // Update password
        $user->update([
            'password' => \Hash::make($validated['password']),
            'password_changed_at' => now(),
        ]);

        return redirect()->route('client.dashboard')->with('success', 'Password changed successfully! Your account is now secure.');
    }
```

---

## 6. NEW FILE: Change Password View
**Path**: `resources/views/client/change-password.blade.php`

**Action**: COPY THIS FILE TO LIVE SERVER

**Size**: 168 lines

**Content**: Complete Blade template with:
- Warning alert for first-time login
- Current password field
- New password field with visibility toggle
- Confirm password field with visibility toggle
- Real-time password matching validation
- Password requirements indicator
- Error message display
- Submit button
- JavaScript for password toggle and matching logic

---

## 📊 Summary Table

| File | Type | Lines | Action |
|------|------|-------|--------|
| `database/migrations/2026_09_01_141422_add_password_changed_to_users.php` | NEW | 26 | Copy & Run Migration |
| `app/Http/Middleware/CheckPasswordChanged.php` | NEW | 26 | Copy to Server |
| `app/Models/User.php` | UPDATED | 2 locations | Copy Updated File |
| `routes/web.php` | UPDATED | 2 locations | Copy Updated File |
| `app/Http/Controllers/ClientController.php` | UPDATED | ~55 new lines | Copy Updated File |
| `resources/views/client/change-password.blade.php` | NEW | 168 | Copy to Server |

---

## 🔍 How to Apply Changes (Manual Method)

If you prefer to apply changes manually instead of copying files:

### Step 1: Database Migration
1. Copy `database/migrations/2026_09_01_141422_add_password_changed_to_users.php` to live server
2. Run: `php artisan migrate`

### Step 2: Create Middleware
Create file: `app/Http/Middleware/CheckPasswordChanged.php`
Copy the 26-line middleware code (see section 2 above)

### Step 3: Update User Model
Edit: `app/Models/User.php`
- Add `'password_changed_at'` to $fillable array
- Add `'password_changed_at' => 'datetime'` to casts array

### Step 4: Update Routes
Edit: `routes/web.php`
- Update client middleware to include `CheckPasswordChanged::class`
- Add 2 password change routes

### Step 5: Update ClientController
Edit: `app/Http/Controllers/ClientController.php`
- Add `showChangePassword()` method
- Add `updatePassword()` method

### Step 6: Create View
Create file: `resources/views/client/change-password.blade.php`
Copy the 168-line view code (see section 6 above)

### Step 7: Clear Cache
Run on live server:
```bash
php artisan cache:clear
php artisan config:cache
php artisan route:cache
php artisan view:clear
```

---

## ✅ Verification Steps

After applying all changes, verify:

1. **Check Migration Status**
   ```bash
   php artisan migrate:status
   ```
   Should show: `2026_09_01_141422_add_password_changed_to_users ... Ran`

2. **Check Database Column**
   ```bash
   mysql -u user -p database_name
   DESC users;
   ```
   Should show `password_changed_at` column

3. **Check Routes**
   ```bash
   php artisan route:list | grep change-password
   ```
   Should show 2 routes

4. **Check Middleware**
   ```bash
   grep -n "CheckPasswordChanged" routes/web.php
   ```
   Should show middleware in client routes

---

## 🎯 Expected Behavior After Deployment

1. **New Client First Login**:
   - Client logs in with email + IC number
   - Middleware detects password_changed_at is NULL
   - Redirects to `/client/change-password`
   - Shows yellow warning alert
   - Client changes password
   - `password_changed_at` is set to current timestamp
   - Redirected to dashboard

2. **New Client Subsequent Logins**:
   - Client logs in with email + new password
   - `password_changed_at` is not NULL
   - Middleware allows access
   - No redirect

3. **Existing Clients**:
   - Unaffected (already have strong passwords)
   - Can change password anytime via `/client/change-password`

---

## 🆘 Common Issues & Fixes

| Issue | Solution |
|-------|----------|
| "Column already exists" error | Migration may have run before, check with `DESC users;` |
| Routes not showing | Run `php artisan route:cache` |
| Middleware not working | Check it's in routes/web.php and cache is cleared |
| View not rendering | Check file path and permissions |
| Password validation failing | Ensure regex patterns are correct in controller |

---

