# Full Codebase Export
Generated: 2026-03-25 18:52:40
Project: C:\Users\aglan\OneDrive\Documents\finalclubmanagementphp
Files: 491
---


## File: .htaccess

```htaccess
RewriteEngine On
RewriteRule ^$ public/ [L]
RewriteRule ^(.*)$ public/$1 [L]
```

## File: caprover_deploy_guide.md

```md
# Deployment Guide â€” Club Management â†’ CapRover

## Files Added to Your Repo

| File | Purpose |
|------|---------|
| [Dockerfile](file:///c:/Users/aglan/OneDrive/Documents/finalclubmanagementphp/Dockerfile) | PHP 8.2 + Apache, `DocumentRoot` â†’ `public/` |
| [captain-definition](file:///c:/Users/aglan/OneDrive/Documents/finalclubmanagementphp/captain-definition) | Tells CapRover to use the Dockerfile |
| [.dockerignore](file:///c:/Users/aglan/OneDrive/Documents/finalclubmanagementphp/.dockerignore) | Excludes [code.sql](file:///c:/Users/aglan/OneDrive/Documents/finalclubmanagementphp/code.sql), `prompts/`, orphaned files |
| [.env.production.example](file:///c:/Users/aglan/OneDrive/Documents/finalclubmanagementphp/.env.production.example) | Reference for env vars (never commit real values) |

---

## Step 1 â€” Push the New Files to GitLab

Run from your project folder on Windows (PowerShell):

```powershell
cd "C:\Users\aglan\OneDrive\Documents\finalclubmanagementphp"
git add Dockerfile captain-definition .dockerignore .env.production.example
git commit -m "chore: add CapRover deployment files"
git push origin main
```

---

## Step 2 â€” Create a MySQL App in CapRover (one-time)

1. Open `http://captain.caprover.al-arcade.com` â†’ **One-Click Apps**
2. Search **MySQL** â†’ Deploy it with app name **`club-db`**
3. Note down the root password CapRover shows you
4. Click **HTTP Settings** on `club-db` â†’ **Do NOT expose** it publicly

Then run this SQL via CapRover's app exec or a phpMyAdmin one-click app:

```sql
CREATE DATABASE IF NOT EXISTS `club_management`
  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE USER IF NOT EXISTS 'club_user'@'%'
  IDENTIFIED BY 'YOUR_STRONG_PASSWORD';

GRANT ALL PRIVILEGES ON `club_management`.* TO 'club_user'@'%';
FLUSH PRIVILEGES;
```

> [!IMPORTANT]
> Then import your full application schema SQL using phpMyAdmin or the MySQL CLI inside the container.

---

## Step 3 â€” Create the App in CapRover

1. CapRover Dashboard â†’ **Apps** â†’ **Create New App**
2. Name: `club-management`
3. **Do NOT** tick "Has Persistent Data"
4. Click **HTTP Settings** â†’ Enable HTTPS (Let's Encrypt)

---

## Step 4 â€” Set Environment Variables

In the CapRover `club-management` app â†’ **App Configs** tab â†’ **Environmental Variables**:

| Key | Value |
|-----|-------|
| `APP_DEBUG` | `false` |
| `APP_URL` | `https://club-management.caprover.al-arcade.com` |
| `DB_HOST` | `srv-captain--club-db` |
| `DB_PORT` | `3306` |
| `DB_NAME` | `club_management` |
| `DB_USER` | `club_user` |
| `DB_PASS` | *(your strong password from Step 2)* |

> [!NOTE]
> `srv-captain--club-db` is CapRover's internal DNS name for the `club-db` app. No external port needed.

---

## Step 5 â€” Run the Deploy Script

Copy [deploy.sh](file:///C:/Users/aglan/.gemini/antigravity/brain/e5406d02-837e-4c40-85b0-ac275104d02a/deploy.sh) to your server via SSH, **edit the top variables**, then:

```bash
# On your CapRover Ubuntu server via SSH:
nano deploy.sh          # fill in the 6 config vars at the top
chmod +x deploy.sh
./deploy.sh
```

The script will:
1. Install Node.js + CapRover CLI if missing
2. Clone the GitLab repo fresh
3. Strip [code.sql](file:///c:/Users/aglan/OneDrive/Documents/finalclubmanagementphp/code.sql), `prompts/`, orphaned files from the build
4. Set env vars on the CapRover app via API
5. Enable HTTPS
6. Deploy the Docker image

---

## Step 6 â€” Alternative: Deploy Directly from GitLab CI (optional)

Add this `.gitlab-ci.yml` to your repo for automatic deploy on every push to `main`:

```yaml
deploy:
  stage: deploy
  image: node:20-alpine
  script:
    - npm install -g caprover
    - caprover deploy
        --caproverUrl $CAPROVER_URL
        --caproverPassword $CAPROVER_PASSWORD
        --appName club-management
        --branch main
  only:
    - main
```

Set `CAPROVER_URL` and `CAPROVER_PASSWORD` as **masked CI/CD variables** in GitLab â†’ Settings â†’ CI/CD â†’ Variables.

---

## Post-Deploy Verification

- [ ] `https://club-management.caprover.al-arcade.com` loads the login page
- [ ] Login with admin account works
- [ ] Check CapRover app logs for PHP errors
- [ ] Confirm `storage/logs/` is writable (CapRover container has `www-data` perms set in Dockerfile)
- [ ] Upload a file via the member module to verify `storage/uploads/` works

> [!WARNING]
> If you need persistent file uploads to survive re-deploys, go to CapRover â†’ **App Configs** â†’ **Persistent Directories** and add `/var/www/html/storage/uploads` mounted to a volume.

```

## File: captain-definition

```text
{
  "schemaVersion": 2,
  "dockerfilePath": "./Dockerfile"
}

```

## File: code.sql

```sql
UPDATE auth_users SET password_hash = '$2y$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi' WHERE username = 'admin';
-- Password: password
```

## File: config\app.php

```php
<?php
return [
    'name'       => 'Club Management System',
    'debug'      => (bool)(getenv('APP_DEBUG') ?: false),
    'url'        => getenv('APP_URL') ?: 'http://localhost/club-management',
    'timezone'   => 'Africa/Cairo',
    'session_lifetime' => 3600,
    'upload_max_size'  => 10 * 1024 * 1024,
    'modules_path'     => dirname(__DIR__) . '/modules',
    'storage_path'     => dirname(__DIR__) . '/storage',
    'views_path'       => dirname(__DIR__) . '/views',
];
```

## File: config\database.php

```php
<?php
return [
    'host' => getenv('DB_HOST') ?: 'srv-captain--mysql-db',
    'port' => getenv('DB_PORT') ?: 3306,
    'database' => getenv('DB_NAME') ?: 'club_management',
    'username' => getenv('DB_USER') ?: 'root',
    'password' => getenv('DB_PASS') ?: 'Alarcade123#',
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
];
```

## File: core\App.php

```php
<?php
class App
{
    private Router $router;
    private Request $request;

    public function __construct()
    {
        $this->request = new Request();
        $this->router = new Router();
        $this->registerCoreRoutes();
        $this->registerModuleRoutes();
    }

    public function run(): void
    {
        // Global error handler
        set_exception_handler(function (\Throwable $e) {
            Logger::critical($e->getMessage(), [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
            ]);

            $appConfig = require ROOT_PATH . '/config/app.php';
            if ($appConfig['debug']) {
                http_response_code(500);
                echo '<h1>Error</h1><pre>' . htmlspecialchars($e->getMessage()) . '</pre>';
                echo '<pre>' . htmlspecialchars($e->getTraceAsString()) . '</pre>';
            } else {
                http_response_code(500);
                $viewFile = ROOT_PATH . '/views/errors/500.php';
                if (file_exists($viewFile)) {
                    include $viewFile;
                } else {
                    echo '<h1>500 Internal Server Error</h1><p>Something went wrong.</p>';
                }
            }
            exit;
        });

        // Language switching via URL
        if (isset($_GET['lang']) && in_array($_GET['lang'], ['en', 'ar'])) {
            Lang::set($_GET['lang']);
        }

        $this->router->dispatch($this->request);
    }

    private function registerCoreRoutes(): void
    {
        $r = $this->router;

        // Guest routes
        $r->addRoute('GET', '/login', 'AuthController@showLogin', null, null, ['GuestMiddleware']);
        $r->addRoute('POST', '/login', 'AuthController@login', null, null, ['GuestMiddleware']);

        // Auth routes
        $r->addRoute('POST', '/logout', 'AuthController@logout', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/dashboard', 'DashboardController@index', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/', function () { Response::redirect('/dashboard'); });

        $r->addRoute('GET', '/profile', 'ProfileController@show', null, null, ['AuthMiddleware']);
        $r->addRoute('POST', '/profile', 'ProfileController@update', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/change-password', 'ProfileController@showChangePassword', null, null, ['AuthMiddleware']);
        $r->addRoute('POST', '/change-password', 'ProfileController@changePassword', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/lang/{code}', 'LangController@switchLang');
    }

    private function registerModuleRoutes(): void
    {
        $moduleRoutes = ModuleRegistry::getRoutes();
        foreach ($moduleRoutes as $route) {
            $this->router->addRoute(
                $route[0],            // method
                $route[1],            // pattern
                $route[2],            // handler
                $route[3] ?? null,    // permission
                $route['_module'],    // module name
                ['AuthMiddleware']    // all module routes require auth
            );
        }
    }
}
```

## File: core\App_112.php

```php
<?php
class App
{
    private Router $router;
    private Request $request;

    public function __construct()
    {
        $this->request = new Request();
        $this->router = new Router();
        $this->registerCoreRoutes();
        $this->registerModuleRoutes();
    }

    public function run(): void
    {
        /**
         * BUG 6 FIX: Register error handler alongside exception handler
         * Convert PHP errors to ErrorException for consistent handling
         */
        set_error_handler(function (int $severity, string $message, string $file, int $line) {
            if (!(error_reporting() & $severity)) {
                return false;
            }
            throw new \ErrorException($message, 0, $severity, $file, $line);
        });

        set_exception_handler(function (\Throwable $e) {
            Logger::critical($e->getMessage(), [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
            ]);

            $appConfig = require ROOT_PATH . '/config/app.php';
            if ($appConfig['debug']) {
                http_response_code(500);
                echo '<!DOCTYPE html><html><head><title>Error</title>';
                echo '<style>body{font-family:monospace;padding:40px;background:#1a1a2e;color:#e94560}pre{background:#16213e;padding:20px;border-radius:8px;color:#eee;overflow-x:auto}h1{color:#e94560}</style>';
                echo '</head><body>';
                echo '<h1>&#x26A0; ' . htmlspecialchars(get_class($e)) . '</h1>';
                echo '<p><strong>' . htmlspecialchars($e->getMessage()) . '</strong></p>';
                echo '<p>' . htmlspecialchars($e->getFile()) . ':' . $e->getLine() . '</p>';
                echo '<pre>' . htmlspecialchars($e->getTraceAsString()) . '</pre>';
                echo '</body></html>';
            } else {
                http_response_code(500);
                $viewFile = ROOT_PATH . '/views/errors/500.php';
                if (file_exists($viewFile)) {
                    include $viewFile;
                } else {
                    echo '<h1>500 Internal Server Error</h1><p>Something went wrong.</p>';
                }
            }
            exit;
        });

        // Language switching via query param
        if (isset($_GET['lang']) && in_array($_GET['lang'], ['en', 'ar'])) {
            Lang::set($_GET['lang']);
        }

        /**
         * BUG 4 FIX: Serve module static assets (CSS, JS, images)
         * Module files live outside public/. This intercepts requests matching
         * /modules/{name}/(css|js|images)/{file} and serves them with proper
         * content types and security checks.
         */
        $uri = $this->request->uri();
        if (preg_match('#^/modules/([a-zA-Z0-9_-]+)/(css|js|images|fonts)/(.+)$#', $uri, $matches)) {
            $this->serveModuleAsset($matches[1], $matches[2], $matches[3]);
            return;
        }

        $this->router->dispatch($this->request);
    }

    /**
     * Serve static files from module directories.
     * Only allows specific file extensions. Sanitizes path to prevent traversal.
     */
    private function serveModuleAsset(string $module, string $type, string $file): void
    {
        // Sanitize: no path traversal
        $module = preg_replace('/[^a-zA-Z0-9_-]/', '', $module);
        $file = str_replace(['..', "\0"], '', $file);

        // Allowed extensions per type
        $allowedExtensions = [
            'css'    => ['css', 'css.map'],
            'js'     => ['js', 'js.map'],
            'images' => ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'],
            'fonts'  => ['woff', 'woff2', 'ttf', 'eot', 'otf'],
        ];

        $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
        $allowed = $allowedExtensions[$type] ?? [];
        if (!in_array($ext, $allowed)) {
            http_response_code(403);
            exit;
        }

        $filePath = ROOT_PATH . '/modules/' . $module . '/' . $type . '/' . $file;

        // Verify the resolved path is actually inside the modules directory (prevents traversal)
        $realPath = realpath($filePath);
        $modulesDir = realpath(ROOT_PATH . '/modules');
        if ($realPath === false || $modulesDir === false || !str_starts_with($realPath, $modulesDir)) {
            http_response_code(404);
            exit;
        }

        // Content types
        $contentTypes = [
            'css'   => 'text/css',
            'js'    => 'application/javascript',
            'png'   => 'image/png',
            'jpg'   => 'image/jpeg',
            'jpeg'  => 'image/jpeg',
            'gif'   => 'image/gif',
            'svg'   => 'image/svg+xml',
            'webp'  => 'image/webp',
            'ico'   => 'image/x-icon',
            'woff'  => 'font/woff',
            'woff2' => 'font/woff2',
            'ttf'   => 'font/ttf',
            'eot'   => 'application/vnd.ms-fontobject',
            'otf'   => 'font/otf',
            'map'   => 'application/json',
        ];

        $contentType = $contentTypes[$ext] ?? 'application/octet-stream';

        header('Content-Type: ' . $contentType);
        header('Content-Length: ' . filesize($realPath));
        header('Cache-Control: public, max-age=86400');
        header('ETag: ' . md5_file($realPath));

        // Handle conditional requests
        $etag = md5_file($realPath);
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) {
            http_response_code(304);
            exit;
        }

        readfile($realPath);
        exit;
    }

    private function registerCoreRoutes(): void
    {
        $r = $this->router;

        // Guest routes
        $r->addRoute('GET', '/login', 'AuthController@showLogin', null, null, ['GuestMiddleware']);
        $r->addRoute('POST', '/login', 'AuthController@login', null, null, ['GuestMiddleware']);

        // Auth routes
        $r->addRoute('POST', '/logout', 'AuthController@logout', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/dashboard', 'DashboardController@index', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/', function () { Response::redirect('/dashboard'); });

        $r->addRoute('GET', '/profile', 'ProfileController@show', null, null, ['AuthMiddleware']);
        $r->addRoute('POST', '/profile', 'ProfileController@update', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/change-password', 'ProfileController@showChangePassword', null, null, ['AuthMiddleware']);
        $r->addRoute('POST', '/change-password', 'ProfileController@changePassword', null, null, ['AuthMiddleware']);
        $r->addRoute('GET', '/lang/{code}', 'LangController@switchLang');
    }

    private function registerModuleRoutes(): void
    {
        $moduleRoutes = ModuleRegistry::getRoutes();
        foreach ($moduleRoutes as $route) {
            $this->router->addRoute(
                $route[0],            // method
                $route[1],            // pattern
                $route[2],            // handler
                $route[3] ?? null,    // permission
                $route['_module'],    // module name
                ['AuthMiddleware']    // all module routes require auth
            );
        }
    }
}
```

## File: core\AuditLog.php

```php
<?php
class AuditLog
{
    public static function log(
        string $action,
        string $module,
        string $entityType,
        ?int $entityId,
        string $description,
        $oldValues = null,
        $newValues = null
    ): void {
        try {
            $db = Database::getInstance();
            $db->insert('audit_log', [
                'user_id'        => Auth::id(),
                'action_type'    => $action,
                'module'         => $module,
                'entity_type'    => $entityType,
                'entity_id'      => $entityId,
                'description'    => mb_substr($description, 0, 1000),
                'ip_address'     => $_SERVER['REMOTE_ADDR'] ?? null,
                'user_agent'     => isset($_SERVER['HTTP_USER_AGENT']) ? mb_substr($_SERVER['HTTP_USER_AGENT'], 0, 500) : null,
                'request_method' => $_SERVER['REQUEST_METHOD'] ?? null,
                'request_url'    => isset($_SERVER['REQUEST_URI']) ? mb_substr($_SERVER['REQUEST_URI'], 0, 500) : null,
                'old_values'     => $oldValues ? json_encode($oldValues, JSON_UNESCAPED_UNICODE) : null,
                'new_values'     => $newValues ? json_encode($newValues, JSON_UNESCAPED_UNICODE) : null,
                'severity'       => 'info',
            ]);
        } catch (\Exception $e) {
            Logger::error('Audit log failed: ' . $e->getMessage());
        }
    }
}
```

## File: core\Auth.php

```php
<?php
class Auth
{
    private static ?array $user = null;
    private static ?array $permissions = null;

    public static function login(string $username, string $password, Request $request): array
    {
        $db = Database::getInstance();

        // Rate limit check
        $maxAttempts = (int) Config::get('max_login_attempts', 5);
        $lockoutMinutes = (int) Config::get('lockout_duration_minutes', 30);

        $user = $db->fetchOne(
            "SELECT * FROM auth_users WHERE username = ? AND is_deleted = 0",
            [$username]
        );

        if (!$user) {
            self::recordAttempt($username, $request, false, 'User not found');
            return ['success' => false, 'message' => 'Invalid username or password'];
        }

        if ($user['is_locked']) {
            $lockedAt = strtotime($user['locked_at']);
            $unlockAt = $lockedAt + ($lockoutMinutes * 60);
            if (time() < $unlockAt) {
                self::recordAttempt($username, $request, false, 'Account locked');
                $remaining = ceil(($unlockAt - time()) / 60);
                return ['success' => false, 'message' => "Account locked. Try again in {$remaining} minutes."];
            }
            // Unlock
            $db->update('auth_users', [
                'is_locked' => 0,
                'failed_login_attempts' => 0,
                'locked_at' => null,
                'lock_reason' => null,
            ], 'id = ?', [$user['id']]);
            $user['is_locked'] = 0;
            $user['failed_login_attempts'] = 0;
        }

        if (!$user['is_active']) {
            self::recordAttempt($username, $request, false, 'Account inactive');
            return ['success' => false, 'message' => 'Account is inactive'];
        }

        if (!password_verify($password, $user['password_hash'])) {
            $attempts = $user['failed_login_attempts'] + 1;
            $updates = ['failed_login_attempts' => $attempts];
            if ($attempts >= $maxAttempts) {
                $updates['is_locked'] = 1;
                $updates['locked_at'] = date('Y-m-d H:i:s');
                $updates['lock_reason'] = 'Too many failed login attempts';
            }
            $db->update('auth_users', $updates, 'id = ?', [$user['id']]);
            self::recordAttempt($username, $request, false, 'Wrong password');
            self::logLogin($user['id'], $username, 'login_failed', $request, 'Wrong password');
            return ['success' => false, 'message' => 'Invalid username or password'];
        }

        // Success
        $db->update('auth_users', [
            'failed_login_attempts' => 0,
            'last_login_at' => date('Y-m-d H:i:s'),
            'last_login_ip' => $request->ip(),
        ], 'id = ?', [$user['id']]);

        // Load permissions
        $perms = self::loadPermissions($user['id']);

        // Create session record
        $sessionToken = bin2hex(random_bytes(32));
        $expiresAt = date('Y-m-d H:i:s', time() + 3600);

        $db->insert('auth_sessions', [
            'user_id' => $user['id'],
            'session_token' => $sessionToken,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'expires_at' => $expiresAt,
            'is_active' => 1,
        ]);

        // Set session data
        Session::regenerate();
        Session::set('auth_user_id', $user['id']);
        Session::set('auth_user', [
            'id' => $user['id'],
            'username' => $user['username'],
            'email' => $user['email'],
            'full_name_en' => $user['full_name_en'],
            'full_name_ar' => $user['full_name_ar'],
            'avatar_path' => $user['avatar_path'],
            'must_change_password' => $user['must_change_password'],
        ]);
        Session::set('auth_permissions', $perms);
        Session::set('auth_session_token', $sessionToken);
        Session::set('_session_timeout', (int) Config::get('session_timeout_minutes', 60) * 60);

        self::recordAttempt($username, $request, true, null);
        self::logLogin($user['id'], $username, 'login_success', $request);

        self::$user = null; // Reset static cache
        self::$permissions = null;

        return ['success' => true, 'user' => $user];
    }

    public static function logout(Request $request): void
    {
        $db = Database::getInstance();
        $token = Session::get('auth_session_token');
        $userId = Session::get('auth_user_id');
        $username = Session::get('auth_user')['username'] ?? '';

        if ($token) {
            $db->update('auth_sessions', [
                'is_active' => 0,
                'terminated_reason' => 'logout',
            ], 'session_token = ?', [$token]);
        }

        if ($userId) {
            self::logLogin($userId, $username, 'logout', $request);
        }

        Session::destroy();
        self::$user = null;
        self::$permissions = null;
    }

    public static function check(): bool
    {
        return Session::has('auth_user_id');
    }

    public static function id(): ?int
    {
        return Session::get('auth_user_id');
    }

    public static function user(?string $field = null)
    {
        if (self::$user === null) {
            $sessionUser = Session::get('auth_user');
            if (!$sessionUser) {
                return null;
            }
            // Load full user from DB on first access per request
            try {
                $db = Database::getInstance();
                self::$user = $db->fetchOne(
                    "SELECT id, username, email, full_name_en, full_name_ar, phone, avatar_path, is_active, must_change_password, timezone, language_id, preferences FROM auth_users WHERE id = ? AND is_deleted = 0",
                    [$sessionUser['id']]
                );
            } catch (\Exception $e) {
                self::$user = $sessionUser;
            }
        }

        if (self::$user === null) {
            return null;
        }

        if ($field !== null) {
            return self::$user[$field] ?? null;
        }

        return self::$user;
    }

    public static function permissions(): array
    {
        if (self::$permissions === null) {
            self::$permissions = Session::get('auth_permissions', []);
        }
        return self::$permissions;
    }

    private static function loadPermissions(int $userId): array
    {
        $db = Database::getInstance();

        // Check if user has super_admin role
        $isSuperAdmin = $db->fetchColumn(
            "SELECT COUNT(*) FROM auth_user_roles ur
             JOIN auth_roles r ON ur.role_id = r.id
             WHERE ur.user_id = ? AND r.role_code = 'super_admin' AND ur.is_active = 1 AND r.is_active = 1
             AND (ur.expires_at IS NULL OR ur.expires_at > NOW())",
            [$userId]
        );

        if ($isSuperAdmin > 0) {
            return ['*']; // Wildcard = all permissions
        }

        $perms = $db->fetchAll(
            "SELECT DISTINCT p.permission_code
             FROM auth_user_roles ur
             JOIN auth_role_permissions rp ON ur.role_id = rp.role_id
             JOIN auth_permissions p ON rp.permission_id = p.id
             WHERE ur.user_id = ? AND ur.is_active = 1 AND p.is_active = 1
             AND (ur.expires_at IS NULL OR ur.expires_at > NOW())",
            [$userId]
        );

        return array_column($perms, 'permission_code');
    }

    private static function recordAttempt(string $username, Request $request, bool $success, ?string $reason): void
    {
        try {
            $db = Database::getInstance();
            $db->insert('auth_login_attempts', [
                'username' => $username,
                'ip_address' => $request->ip(),
                'user_agent' => mb_substr($request->userAgent(), 0, 500),
                'success' => $success ? 1 : 0,
                'failure_reason' => $reason,
            ]);
        } catch (\Exception $e) {
            // Silently fail
        }
    }

    private static function logLogin(?int $userId, string $username, string $action, Request $request, ?string $failureReason = null): void
    {
        try {
            $db = Database::getInstance();
            $db->insert('login_log', [
                'user_id' => $userId,
                'username' => $username,
                'action' => $action,
                'ip_address' => $request->ip(),
                'user_agent' => mb_substr($request->userAgent(), 0, 500),
                'failure_reason' => $failureReason,
            ]);
        } catch (\Exception $e) {
            // Silently fail
        }
    }
}
```

## File: core\AuthController.php

```php
<?php
class AuthController extends Controller
{
    public function showLogin(Request $request): void
    {
        $this->view('auth/login', [
            'title' => lang('auth.login'),
        ], 'auth');
    }

    public function login(Request $request): void
    {
        // CSRF check
        if (!CSRF::validate($request->csrfToken())) {
            if ($request->isAjax()) {
                $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
                return;
            }
            $this->redirect('/login', ['errors' => ['Invalid security token']]);
            return;
        }

        $username = trim($request->post('username', ''));
        $password = $request->post('password', '');

        if (empty($username) || empty($password)) {
            if ($request->isAjax()) {
                $this->json(['success' => false, 'message' => 'Username and password are required'], 422);
                return;
            }
            $this->redirect('/login', ['errors' => ['Username and password are required'], 'old' => ['username' => $username]]);
            return;
        }

        $result = Auth::login($username, $password, $request);

        if ($result['success']) {
            $redirectTo = Session::getFlash('redirect_after_login') ?? '/dashboard';
            if ($request->isAjax()) {
                $this->json(['success' => true, 'redirect' => $redirectTo]);
                return;
            }
            $this->redirect($redirectTo);
        } else {
            if ($request->isAjax()) {
                $this->json(['success' => false, 'message' => $result['message']], 401);
                return;
            }
            Session::flash('error', $result['message']);
            Session::flash('old', ['username' => $username]);
            $this->redirect('/login');
        }
    }

    public function logout(Request $request): void
    {
        Auth::logout($request);
        Response::redirect('/login');
    }
}
```

## File: core\bootstrap.php

```php
<?php
/**
 * Bootstrap â€” Autoloader, session init, helper loading
 */

$appConfig = require ROOT_PATH . '/config/app.php';
date_default_timezone_set($appConfig['timezone']);

// Autoloader
spl_autoload_register(function ($class) {
    // Core classes
    $coreFile = ROOT_PATH . '/core/' . $class . '.php';
    if (file_exists($coreFile)) {
        require_once $coreFile;
        return;
    }

    // Middleware
    $middlewareFile = ROOT_PATH . '/core/Middleware/' . $class . '.php';
    if (file_exists($middlewareFile)) {
        require_once $middlewareFile;
        return;
    }

    // Module controllers: format ModuleName\Controllers\ClassName
    // We resolve by scanning modules
    $parts = explode('\\', $class);
    if (count($parts) >= 3) {
        $moduleName = $parts[0];
        $subDir = $parts[1];
        $className = $parts[2];
        $moduleFile = ROOT_PATH . '/modules/' . strtolower($moduleName) . '/' . $subDir . '/' . $className . '.php';
        if (file_exists($moduleFile)) {
            require_once $moduleFile;
            return;
        }
    }
});

// Load helpers
require_once ROOT_PATH . '/core/helpers.php';

// Start session
Session::start();
```

## File: core\Config.php

```php
<?php
class Config
{
    private static ?array $cache = null;

    public static function get(string $key, $default = null)
    {
        if (self::$cache === null) {
            self::load();
        }
        return self::$cache[$key] ?? $default;
    }

    public static function set(string $key, $value): void
    {
        if (self::$cache === null) {
            self::load();
        }
        self::$cache[$key] = $value;
    }

    public static function all(): array
    {
        if (self::$cache === null) {
            self::load();
        }
        return self::$cache;
    }

    public static function getGroup(string $group): array
    {
        try {
            $db = Database::getInstance();
            $rows = $db->fetchAll(
                "SELECT config_key, config_value, config_type FROM sys_config WHERE config_group = ?",
                [$group]
            );
            $result = [];
            foreach ($rows as $row) {
                $result[$row['config_key']] = self::castValue($row['config_value'], $row['config_type']);
            }
            return $result;
        } catch (\Exception $e) {
            return [];
        }
    }

    private static function load(): void
    {
        self::$cache = [];
        try {
            $db = Database::getInstance();
            $rows = $db->fetchAll("SELECT config_key, config_value, config_type FROM sys_config");
            foreach ($rows as $row) {
                self::$cache[$row['config_key']] = self::castValue($row['config_value'], $row['config_type']);
            }
        } catch (\Exception $e) {
            // DB not available yet, empty cache
        }
    }

    private static function castValue($value, string $type)
    {
        return match ($type) {
            'integer' => (int) $value,
            'decimal' => (float) $value,
            'boolean' => in_array(strtolower($value), ['true', '1', 'yes']),
            'json'    => json_decode($value, true),
            default   => $value,
        };
    }

    public static function clearCache(): void
    {
        self::$cache = null;
    }
}
```

## File: core\Controller.php

```php
<?php
class Controller
{
    protected ?Request $requestInstance = null;
    protected ?string $moduleNameCache = null;

    protected function view(string $viewName, array $data = [], string $layout = 'app'): void
    {
        $view = View::getInstance();
        $data['_request'] = $this->request();
        $view->render($viewName, $data, $layout, $this->moduleName());
    }

    protected function json(array $data, int $statusCode = 200): void
    {
        Response::json($data, $statusCode);
    }

    protected function redirect(string $url, array $flash = []): void
    {
        foreach ($flash as $key => $value) {
            Session::flash($key, $value);
        }
        Response::redirect($url);
    }

    protected function request(): Request
    {
        if ($this->requestInstance === null) {
            $this->requestInstance = new Request();
        }
        return $this->requestInstance;
    }

    public function setRequest(Request $request): void
    {
        $this->requestInstance = $request;
    }

    protected function db(): Database
    {
        return Database::getInstance();
    }

    protected function auth(): ?array
    {
        return Auth::user();
    }

    protected function can(string $permission): bool
    {
        return Gate::can($permission);
    }

    protected function abort(int $code, string $message = ''): void
    {
        http_response_code($code);
        $viewFile = ROOT_PATH . '/views/errors/' . $code . '.php';
        if (file_exists($viewFile)) {
            $data = ['message' => $message, 'code' => $code];
            $view = View::getInstance();
            $layout = Auth::check() ? 'app' : 'auth';
            $view->render('errors/' . $code, $data, $layout);
        } else {
            echo "<h1>{$code}</h1><p>{$message}</p>";
        }
        exit;
    }

    protected function flash(string $type, string $message): void
    {
        $existing = Session::getFlash('alerts') ?? [];
        $existing[] = ['type' => $type, 'message' => $message];
        Session::flash('alerts', $existing);
    }

    protected function moduleName(): ?string
    {
        return $this->moduleNameCache;
    }

    public function setModuleName(string $name): void
    {
        $this->moduleNameCache = $name;
    }

    protected function audit(string $action, string $entityType, ?int $entityId, string $description, $oldValues = null, $newValues = null): void
    {
        AuditLog::log($action, $this->moduleName() ?? 'core', $entityType, $entityId, $description, $oldValues, $newValues);
    }
}
```

## File: core\CSRF.php

```php
<?php
class CSRF
{
    public static function token(): string
    {
        if (!Session::has('_csrf_token')) {
            Session::set('_csrf_token', bin2hex(random_bytes(32)));
        }
        return Session::get('_csrf_token');
    }

    public static function field(): string
    {
        return '<input type="hidden" name="_token" value="' . self::token() . '">';
    }

    public static function validate(?string $token): bool
    {
        if ($token === null) {
            return false;
        }
        return hash_equals(self::token(), $token);
    }

    public static function regenerate(): void
    {
        Session::set('_csrf_token', bin2hex(random_bytes(32)));
    }
}
```

## File: core\DashboardController.php

```php
<?php
class DashboardController extends Controller
{
    public function index(Request $request): void
    {
        // Check must_change_password
        $user = Auth::user();
        if ($user && !empty($user['must_change_password'])) {
            $this->redirect('/change-password', ['alerts' => [['type' => 'warning', 'message' => 'You must change your password before continuing.']]]);
            return;
        }

        $widgets = ModuleRegistry::getWidgets();
        $filteredWidgets = [];
        foreach ($widgets as $widget) {
            if (isset($widget['permission']) && !Gate::can($widget['permission'])) {
                continue;
            }
            $filteredWidgets[] = $widget;
        }

        $this->view('dashboard/index', [
            'title' => lang('dashboard.title'),
            'breadcrumbs' => [['label' => lang('dashboard.title')]],
            'widgets' => $filteredWidgets,
            'user' => $user,
        ], 'app');
    }
}
```

## File: core\Database.php

```php
<?php
class Database
{
    private static ?Database $instance = null;
    private PDO $pdo;
    private array $config;
    private bool $inTransaction = false;

    private function __construct()
    {
        $this->config = require ROOT_PATH . '/config/database.php';
        $this->connect();
    }

    private function connect(): void
    {
        $dsn = sprintf(
            'mysql:host=%s;port=%s;dbname=%s;charset=%s',
            $this->config['host'],
            $this->config['port'],
            $this->config['database'],
            $this->config['charset']
        );

        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
            PDO::ATTR_STRINGIFY_FETCHES  => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$this->config['charset']} COLLATE {$this->config['collation']}",
        ];

        $this->pdo = new PDO($dsn, $this->config['username'], $this->config['password'], $options);
    }

    public static function getInstance(): Database
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getPdo(): PDO
    {
        return $this->pdo;
    }

    /**
     * Execute a query and return all rows
     */
    public function fetchAll(string $sql, array $params = []): array
    {
        $stmt = $this->executeQuery($sql, $params);
        return $stmt->fetchAll();
    }

    /**
     * Execute a query and return the first row
     */
    public function fetchOne(string $sql, array $params = []): ?array
    {
        $stmt = $this->executeQuery($sql, $params);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    /**
     * Execute a query and return a single column value
     */
    public function fetchColumn(string $sql, array $params = [])
    {
        $stmt = $this->executeQuery($sql, $params);
        return $stmt->fetchColumn();
    }

    /**
     * Insert a row and return last insert ID
     */
    public function insert(string $table, array $data): int
    {
        $columns = array_keys($data);
        $placeholders = array_fill(0, count($columns), '?');

        $sql = sprintf(
            'INSERT INTO `%s` (`%s`) VALUES (%s)',
            $table,
            implode('`, `', $columns),
            implode(', ', $placeholders)
        );

        $this->executeQuery($sql, array_values($data));
        return (int) $this->pdo->lastInsertId();
    }

    /**
     * Update rows and return affected count
     */
    public function update(string $table, array $data, string $where, array $whereParams = []): int
    {
        $setClauses = [];
        $values = [];
        foreach ($data as $col => $val) {
            $setClauses[] = "`{$col}` = ?";
            $values[] = $val;
        }

        $sql = sprintf(
            'UPDATE `%s` SET %s WHERE %s',
            $table,
            implode(', ', $setClauses),
            $where
        );

        $stmt = $this->executeQuery($sql, array_merge($values, $whereParams));
        return $stmt->rowCount();
    }

    /**
     * Delete rows
     */
    public function delete(string $table, string $where, array $params = []): int
    {
        $sql = sprintf('DELETE FROM `%s` WHERE %s', $table, $where);
        $stmt = $this->executeQuery($sql, $params);
        return $stmt->rowCount();
    }

    /**
     * Raw execute (for stored procedures, etc.)
     */
    public function execute(string $sql, array $params = []): PDOStatement
    {
        return $this->executeQuery($sql, $params);
    }

    /**
     * Begin transaction
     */
    public function beginTransaction(): void
    {
        if (!$this->inTransaction) {
            $this->pdo->beginTransaction();
            $this->inTransaction = true;
        }
    }

    /**
     * Commit transaction
     */
    public function commit(): void
    {
        if ($this->inTransaction) {
            $this->pdo->commit();
            $this->inTransaction = false;
        }
    }

    /**
     * Rollback transaction
     */
    public function rollBack(): void
    {
        if ($this->inTransaction) {
            $this->pdo->rollBack();
            $this->inTransaction = false;
        }
    }

    /**
     * Query builder entry point
     */
    public function table(string $table): QueryBuilder
    {
        return new QueryBuilder($this, $table);
    }

    /**
     * Core query execution with reconnect
     */
    private function executeQuery(string $sql, array $params = []): PDOStatement
    {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt;
        } catch (PDOException $e) {
            // Reconnect on "MySQL server has gone away"
            if (stripos($e->getMessage(), 'gone away') !== false || $e->getCode() == 'HY000') {
                $this->connect();
                $stmt = $this->pdo->prepare($sql);
                $stmt->execute($params);
                return $stmt;
            }
            Logger::error('Database query failed: ' . $e->getMessage(), [
                'sql' => $sql,
                'params' => $params,
            ]);
            throw $e;
        }
    }
}

/**
 * Simple Query Builder
 */
class QueryBuilder
{
    private Database $db;
    private string $table;
    private array $wheres = [];
    private array $whereParams = [];
    private string $orderBy = '';
    private ?int $limitVal = null;
    private ?int $offsetVal = null;
    private string $selectColumns = '*';

    public function __construct(Database $db, string $table)
    {
        $this->db = $db;
        $this->table = $table;
    }

    public function select(string $columns): self
    {
        $this->selectColumns = $columns;
        return $this;
    }

    public function where(string $column, string $operator, $value): self
    {
        $this->wheres[] = "`{$column}` {$operator} ?";
        $this->whereParams[] = $value;
        return $this;
    }

    public function whereRaw(string $condition, array $params = []): self
    {
        $this->wheres[] = $condition;
        $this->whereParams = array_merge($this->whereParams, $params);
        return $this;
    }

    public function whereIn(string $column, array $values): self
    {
        $placeholders = implode(',', array_fill(0, count($values), '?'));
        $this->wheres[] = "`{$column}` IN ({$placeholders})";
        $this->whereParams = array_merge($this->whereParams, $values);
        return $this;
    }

    public function orderBy(string $column, string $direction = 'ASC'): self
    {
        $direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
        $this->orderBy = "ORDER BY `{$column}` {$direction}";
        return $this;
    }

    public function orderByRaw(string $raw): self
    {
        $this->orderBy = "ORDER BY {$raw}";
        return $this;
    }

    public function limit(int $limit): self
    {
        $this->limitVal = $limit;
        return $this;
    }

    public function offset(int $offset): self
    {
        $this->offsetVal = $offset;
        return $this;
    }

    public function get(): array
    {
        return $this->db->fetchAll($this->buildQuery(), $this->buildParams());
    }

    public function first(): ?array
    {
        $this->limitVal = 1;
        return $this->db->fetchOne($this->buildQuery(), $this->buildParams());
    }

    public function count(): int
    {
        $sql = "SELECT COUNT(*) FROM `{$this->table}`";
        if (!empty($this->wheres)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->wheres);
        }
        return (int) $this->db->fetchColumn($sql, $this->whereParams);
    }

    private function buildQuery(): string
    {
        $sql = "SELECT {$this->selectColumns} FROM `{$this->table}`";
        if (!empty($this->wheres)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->wheres);
        }
        if ($this->orderBy) {
            $sql .= ' ' . $this->orderBy;
        }
        if ($this->limitVal !== null) {
            $sql .= ' LIMIT ?';
        }
        if ($this->offsetVal !== null) {
            $sql .= ' OFFSET ?';
        }
        return $sql;
    }

    private function buildParams(): array
    {
        $params = $this->whereParams;
        if ($this->limitVal !== null) {
            $params[] = $this->limitVal;
        }
        if ($this->offsetVal !== null) {
            $params[] = $this->offsetVal;
        }
        return $params;
    }
}
```

## File: core\Database_110.php

```php
<?php
class Database
{
    private static ?Database $instance = null;
    private PDO $pdo;
    private array $config;
    private bool $inTransaction = false;

    private function __construct()
    {
        $this->config = require ROOT_PATH . '/config/database.php';
        $this->connect();
    }

    private function connect(): void
    {
        $dsn = sprintf(
            'mysql:host=%s;port=%s;dbname=%s;charset=%s',
            $this->config['host'],
            $this->config['port'],
            $this->config['database'],
            $this->config['charset']
        );

        /**
         * BUG 3 FIX: emulate_prepares = true
         * With emulate_prepares = false, PDO::execute(array) binds ALL params
         * as PDO::PARAM_STR. MySQL rejects LIMIT '25' â€” it needs an integer.
         * With emulate_prepares = true, PDO handles escaping itself and
         * stringified integers in LIMIT/OFFSET work correctly.
         * Combined with utf8mb4 charset set via DSN, this is safe.
         */
        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => true,
            PDO::ATTR_STRINGIFY_FETCHES  => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$this->config['charset']} COLLATE {$this->config['collation']}",
        ];

        $this->pdo = new PDO($dsn, $this->config['username'], $this->config['password'], $options);
    }

    public static function getInstance(): Database
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getPdo(): PDO
    {
        return $this->pdo;
    }

    public function fetchAll(string $sql, array $params = []): array
    {
        $stmt = $this->executeQuery($sql, $params);
        return $stmt->fetchAll();
    }

    public function fetchOne(string $sql, array $params = []): ?array
    {
        $stmt = $this->executeQuery($sql, $params);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    public function fetchColumn(string $sql, array $params = [])
    {
        $stmt = $this->executeQuery($sql, $params);
        $val = $stmt->fetchColumn();
        return $val === false ? null : $val;
    }

    public function insert(string $table, array $data): int
    {
        if (empty($data)) {
            throw new \InvalidArgumentException("Cannot insert empty data into {$table}");
        }

        $columns = array_keys($data);
        $placeholders = array_fill(0, count($columns), '?');

        $sql = sprintf(
            'INSERT INTO `%s` (`%s`) VALUES (%s)',
            $table,
            implode('`, `', $columns),
            implode(', ', $placeholders)
        );

        $this->executeQuery($sql, array_values($data));
        return (int) $this->pdo->lastInsertId();
    }

    public function update(string $table, array $data, string $where, array $whereParams = []): int
    {
        if (empty($data)) {
            return 0;
        }

        $setClauses = [];
        $values = [];
        foreach ($data as $col => $val) {
            $setClauses[] = "`{$col}` = ?";
            $values[] = $val;
        }

        $sql = sprintf(
            'UPDATE `%s` SET %s WHERE %s',
            $table,
            implode(', ', $setClauses),
            $where
        );

        $stmt = $this->executeQuery($sql, array_merge($values, $whereParams));
        return $stmt->rowCount();
    }

    public function delete(string $table, string $where, array $params = []): int
    {
        $sql = sprintf('DELETE FROM `%s` WHERE %s', $table, $where);
        $stmt = $this->executeQuery($sql, $params);
        return $stmt->rowCount();
    }

    public function execute(string $sql, array $params = []): PDOStatement
    {
        return $this->executeQuery($sql, $params);
    }

    public function beginTransaction(): void
    {
        if (!$this->inTransaction) {
            $this->pdo->beginTransaction();
            $this->inTransaction = true;
        }
    }

    public function commit(): void
    {
        if ($this->inTransaction) {
            $this->pdo->commit();
            $this->inTransaction = false;
        }
    }

    public function rollBack(): void
    {
        if ($this->inTransaction) {
            $this->pdo->rollBack();
            $this->inTransaction = false;
        }
    }

    public function inTransaction(): bool
    {
        return $this->inTransaction;
    }

    public function table(string $table): QueryBuilder
    {
        return new QueryBuilder($this, $table);
    }

    private function executeQuery(string $sql, array $params = []): PDOStatement
    {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt;
        } catch (PDOException $e) {
            if (stripos($e->getMessage(), 'gone away') !== false || $e->getCode() == 'HY000') {
                $this->connect();
                $stmt = $this->pdo->prepare($sql);
                $stmt->execute($params);
                return $stmt;
            }
            Logger::error('Database query failed: ' . $e->getMessage(), [
                'sql' => $sql,
                'params' => $params,
            ]);
            throw $e;
        }
    }
}

class QueryBuilder
{
    private Database $db;
    private string $table;
    private array $wheres = [];
    private array $whereParams = [];
    private string $orderBy = '';
    private ?int $limitVal = null;
    private ?int $offsetVal = null;
    private string $selectColumns = '*';

    public function __construct(Database $db, string $table)
    {
        $this->db = $db;
        $this->table = $table;
    }

    public function select(string $columns): self
    {
        $this->selectColumns = $columns;
        return $this;
    }

    public function where(string $column, string $operator, $value): self
    {
        $this->wheres[] = "`{$column}` {$operator} ?";
        $this->whereParams[] = $value;
        return $this;
    }

    public function whereRaw(string $condition, array $params = []): self
    {
        $this->wheres[] = $condition;
        $this->whereParams = array_merge($this->whereParams, $params);
        return $this;
    }

    public function whereIn(string $column, array $values): self
    {
        if (empty($values)) {
            $this->wheres[] = '1=0'; // always false
            return $this;
        }
        $placeholders = implode(',', array_fill(0, count($values), '?'));
        $this->wheres[] = "`{$column}` IN ({$placeholders})";
        $this->whereParams = array_merge($this->whereParams, $values);
        return $this;
    }

    public function whereNull(string $column): self
    {
        $this->wheres[] = "`{$column}` IS NULL";
        return $this;
    }

    public function whereNotNull(string $column): self
    {
        $this->wheres[] = "`{$column}` IS NOT NULL";
        return $this;
    }

    public function orderBy(string $column, string $direction = 'ASC'): self
    {
        $direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
        if ($this->orderBy) {
            $this->orderBy .= ", `{$column}` {$direction}";
        } else {
            $this->orderBy = "ORDER BY `{$column}` {$direction}";
        }
        return $this;
    }

    public function orderByRaw(string $raw): self
    {
        $this->orderBy = "ORDER BY {$raw}";
        return $this;
    }

    public function limit(int $limit): self
    {
        $this->limitVal = $limit;
        return $this;
    }

    public function offset(int $offset): self
    {
        $this->offsetVal = $offset;
        return $this;
    }

    public function get(): array
    {
        return $this->db->fetchAll($this->buildQuery(), $this->whereParams);
    }

    public function first(): ?array
    {
        $this->limitVal = 1;
        return $this->db->fetchOne($this->buildQuery(), $this->whereParams);
    }

    public function count(): int
    {
        $sql = "SELECT COUNT(*) FROM `{$this->table}`";
        if (!empty($this->wheres)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->wheres);
        }
        return (int) $this->db->fetchColumn($sql, $this->whereParams);
    }

    public function exists(): bool
    {
        return $this->count() > 0;
    }

    /**
     * BUG 3 FIX: Inline LIMIT/OFFSET as integers directly in SQL.
     * Even though we fixed emulate_prepares, this is defense-in-depth.
     * These values are already validated as int by the setter methods.
     */
    private function buildQuery(): string
    {
        $sql = "SELECT {$this->selectColumns} FROM `{$this->table}`";
        if (!empty($this->wheres)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->wheres);
        }
        if ($this->orderBy) {
            $sql .= ' ' . $this->orderBy;
        }
        if ($this->limitVal !== null) {
            $sql .= ' LIMIT ' . (int)$this->limitVal;
        }
        if ($this->offsetVal !== null) {
            $sql .= ' OFFSET ' . (int)$this->offsetVal;
        }
        return $sql;
    }
}
```

## File: core\Export.php

```php
<?php
class Export
{
    public static function csv(string $filename, array $headers, array $rows): void
    {
        header('Content-Type: text/csv; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Cache-Control: no-cache');
        echo "\xEF\xBB\xBF"; // BOM for Excel

        $output = fopen('php://output', 'w');
        fputcsv($output, $headers);
        foreach ($rows as $row) {
            fputcsv($output, array_values($row));
        }
        fclose($output);
        exit;
    }

    public static function excel(string $filename, array $headers, array $rows): void
    {
        header('Content-Type: application/vnd.ms-excel; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Cache-Control: no-cache');

        echo '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">';
        echo '<head><meta charset="UTF-8"></head><body>';
        echo '<table border="1" cellpadding="4" cellspacing="0">';
        echo '<tr>';
        foreach ($headers as $h) {
            echo '<th style="background:#4472C4;color:#fff;font-weight:bold">' . htmlspecialchars($h) . '</th>';
        }
        echo '</tr>';
        foreach ($rows as $row) {
            echo '<tr>';
            foreach (array_values($row) as $cell) {
                echo '<td>' . htmlspecialchars((string)$cell) . '</td>';
            }
            echo '</tr>';
        }
        echo '</table></body></html>';
        exit;
    }

    public static function pdf(string $filename, string $htmlContent): void
    {
        header('Content-Type: text/html; charset=UTF-8');
        echo '<!DOCTYPE html><html><head><meta charset="UTF-8">';
        echo '<title>' . htmlspecialchars($filename) . '</title>';
        echo '<style>body{font-family:Arial,sans-serif;margin:20px}table{border-collapse:collapse;width:100%}th,td{border:1px solid #ddd;padding:8px;text-align:left}th{background:#4472C4;color:#fff}@media print{.no-print{display:none}}</style>';
        echo '</head><body>';
        echo '<div class="no-print" style="margin-bottom:20px"><button onclick="window.print()">Print / Save as PDF</button></div>';
        echo $htmlContent;
        echo '</body></html>';
        exit;
    }
}
```

## File: core\FileUpload.php

```php
<?php
class FileUpload
{
    private ?array $file;
    private array $allowedTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx'];
    private int $maxSize = 10485760; // 10MB
    private string $destination = 'uploads/';
    private array $errors = [];

    public function __construct(?array $file)
    {
        $this->file = $file;
    }

    public function setAllowedTypes(array $types): self
    {
        $this->allowedTypes = $types;
        return $this;
    }

    public function setMaxSize(int $bytes): self
    {
        $this->maxSize = $bytes;
        return $this;
    }

    public function setDestination(string $path): self
    {
        $this->destination = $path;
        return $this;
    }

    public function validate(): bool
    {
        $this->errors = [];

        if (!$this->file || $this->file['error'] === UPLOAD_ERR_NO_FILE) {
            $this->errors[] = 'No file uploaded';
            return false;
        }

        if ($this->file['error'] !== UPLOAD_ERR_OK) {
            $this->errors[] = 'Upload error: ' . $this->file['error'];
            return false;
        }

        $ext = strtolower(pathinfo($this->file['name'], PATHINFO_EXTENSION));
        if (!in_array($ext, $this->allowedTypes)) {
            $this->errors[] = 'File type not allowed. Allowed: ' . implode(', ', $this->allowedTypes);
            return false;
        }

        if ($this->file['size'] > $this->maxSize) {
            $this->errors[] = 'File too large. Max: ' . round($this->maxSize / 1048576, 1) . 'MB';
            return false;
        }

        return true;
    }

    public function save(?string $customName = null): ?string
    {
        if (!empty($this->errors)) {
            return null;
        }

        $fullPath = PUBLIC_PATH . '/' . rtrim($this->destination, '/') . '/';
        if (!is_dir($fullPath)) {
            @mkdir($fullPath, 0755, true);
        }

        $ext = strtolower(pathinfo($this->file['name'], PATHINFO_EXTENSION));
        $filename = $customName ?: (bin2hex(random_bytes(16)) . '.' . $ext);

        $target = $fullPath . $filename;
        if (move_uploaded_file($this->file['tmp_name'], $target)) {
            return rtrim($this->destination, '/') . '/' . $filename;
        }

        $this->errors[] = 'Failed to move uploaded file';
        return null;
    }

    public function errors(): array
    {
        return $this->errors;
    }

    public function originalName(): ?string
    {
        return $this->file['name'] ?? null;
    }

    public function size(): ?int
    {
        return $this->file['size'] ?? null;
    }

    public function mimeType(): ?string
    {
        return $this->file['type'] ?? null;
    }
}
```

## File: core\Gate.php

```php
<?php
class Gate
{
    public static function can(string $permission): bool
    {
        if (!Auth::check()) {
            return false;
        }
        $perms = Auth::permissions();
        if (in_array('*', $perms, true)) {
            return true; // super_admin
        }
        return in_array($permission, $perms, true);
    }

    public static function canAny(array $permissions): bool
    {
        foreach ($permissions as $p) {
            if (self::can($p)) {
                return true;
            }
        }
        return false;
    }

    public static function canAll(array $permissions): bool
    {
        foreach ($permissions as $p) {
            if (!self::can($p)) {
                return false;
            }
        }
        return true;
    }

    public static function authorize(string $permission): void
    {
        if (!self::can($permission)) {
            http_response_code(403);
            if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
                Response::json(['success' => false, 'message' => 'Forbidden'], 403);
            }
            $viewPath = ROOT_PATH . '/views/errors/403.php';
            if (file_exists($viewPath)) {
                include $viewPath;
            } else {
                echo '<h1>403 Forbidden</h1><p>You do not have permission to access this resource.</p>';
            }
            exit;
        }
    }
}
```

## File: core\helpers.php

```php
<?php

function e(?string $string): string
{
    return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
}

function csrf_field(): string
{
    return CSRF::field();
}

function csrf_token(): string
{
    return CSRF::token();
}

function old(string $field, string $default = ''): string
{
    $old = Session::getFlash('old') ?? Session::get('_flash')['old'] ?? [];
    return e($old[$field] ?? $default);
}

function session_flash(string $key)
{
    return Session::getFlash($key);
}

function auth_user(?string $field = null)
{
    return Auth::user($field);
}

function auth_can(string $permission): bool
{
    return Gate::can($permission);
}

function auth_id(): ?int
{
    return Auth::id();
}

function url(string $path = ''): string
{
    return '/' . ltrim($path, '/');
}

function module_asset(string $module, string $path): string
{
    return '/modules/' . $module . '/' . ltrim($path, '/');
}

function format_date(?string $date, ?string $format = null): string
{
    if (!$date) return '';
    $format = $format ?? Config::get('date_format', 'Y-m-d');
    return date($format, strtotime($date));
}

function format_datetime(?string $dt): string
{
    if (!$dt) return '';
    $format = Config::get('datetime_format', 'Y-m-d H:i:s');
    return date($format, strtotime($dt));
}

function format_money($amount, ?string $symbol = null): string
{
    $symbol = $symbol ?? Config::get('default_currency', 'EGP');
    return number_format((float)$amount, 2) . ' ' . $symbol;
}

function format_number($n, int $decimals = 2): string
{
    return number_format((float)$n, $decimals);
}

function money($amount): string
{
    return format_money($amount);
}

function time_ago(?string $datetime): string
{
    if (!$datetime) return '';
    $diff = time() - strtotime($datetime);
    if ($diff < 60) return 'just now';
    if ($diff < 3600) return floor($diff / 60) . 'm ago';
    if ($diff < 86400) return floor($diff / 3600) . 'h ago';
    if ($diff < 2592000) return floor($diff / 86400) . 'd ago';
    return format_date($datetime);
}

function str_limit(string $string, int $limit = 100, string $end = '...'): string
{
    if (mb_strlen($string) <= $limit) return $string;
    return mb_substr($string, 0, $limit) . $end;
}

function is_rtl(): bool
{
    return Lang::isRtl();
}

function current_lang(): string
{
    return Lang::current();
}

function lang(string $key, array $replacements = []): string
{
    return Lang::get($key, $replacements);
}

function dd($var): void
{
    echo '<pre>';
    var_dump($var);
    echo '</pre>';
    die;
}

function status_badge(string $status): string
{
    $map = [
        'active' => 'success', 'pending' => 'warning', 'draft' => 'secondary',
        'suspended' => 'danger', 'frozen' => 'info', 'terminated' => 'dark',
        'expired' => 'secondary', 'cancelled' => 'danger', 'completed' => 'success',
        'approved' => 'success', 'rejected' => 'danger', 'overdue' => 'danger',
        'paid' => 'success', 'partially_paid' => 'warning', 'issued' => 'primary',
        'pending_approval' => 'warning', 'under_review' => 'info',
        'open' => 'primary', 'closed' => 'secondary', 'resolved' => 'success',
        'submitted' => 'info', 'in_progress' => 'primary', 'escalated' => 'danger',
        'deceased' => 'dark', 'transferred' => 'info', 'void' => 'dark',
        'written_off' => 'dark', 'defaulted' => 'danger', 'bounced' => 'danger',
        'waitlisted' => 'info', 'enrolled' => 'primary',
    ];
    $class = $map[$status] ?? 'secondary';
    $label = ucfirst(str_replace('_', ' ', $status));
    return '<span class="badge bg-' . $class . '">' . e($label) . '</span>';
}

function active_class(string $path): string
{
    $uri = (new Request())->uri();
    return str_starts_with($uri, $path) ? 'active' : '';
}

function selected($value, $current): string
{
    return ($value == $current) ? 'selected' : '';
}

function checked($value, $current): string
{
    return ($value == $current) ? 'checked' : '';
}
```

## File: core\helpers_109.php

```php
<?php

function e(?string $string): string
{
    return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
}

function csrf_field(): string
{
    return CSRF::field();
}

function csrf_token(): string
{
    return CSRF::token();
}

/**
 * BUG 2 FIX: Cache old data on first access so multiple calls work
 */
function old(string $field, string $default = ''): string
{
    static $oldData = null;
    if ($oldData === null) {
        $oldData = Session::getFlash('old');
        if ($oldData === null) {
            // Check if it's still in the flash array (not yet consumed)
            $oldData = $_SESSION['_flash']['old'] ?? [];
            unset($_SESSION['_flash']['old']);
        }
        if (!is_array($oldData)) {
            $oldData = [];
        }
    }
    return e($oldData[$field] ?? $default);
}

function session_flash(string $key)
{
    return Session::getFlash($key);
}

function auth_user(?string $field = null)
{
    return Auth::user($field);
}

function auth_can(string $permission): bool
{
    return Gate::can($permission);
}

function auth_id(): ?int
{
    return Auth::id();
}

function url(string $path = ''): string
{
    return '/' . ltrim($path, '/');
}

function module_asset(string $module, string $path): string
{
    return '/modules/' . $module . '/' . ltrim($path, '/');
}

function format_date(?string $date, ?string $format = null): string
{
    if (!$date) return '';
    $format = $format ?? Config::get('date_format', 'Y-m-d');
    $ts = strtotime($date);
    return $ts ? date($format, $ts) : '';
}

function format_datetime(?string $dt): string
{
    if (!$dt) return '';
    $format = Config::get('datetime_format', 'Y-m-d H:i:s');
    $ts = strtotime($dt);
    return $ts ? date($format, $ts) : '';
}

function format_money($amount, ?string $symbol = null): string
{
    $symbol = $symbol ?? Config::get('default_currency', 'EGP');
    return number_format((float)$amount, 2) . ' ' . $symbol;
}

function format_number($n, int $decimals = 2): string
{
    return number_format((float)$n, $decimals);
}

function money($amount): string
{
    return format_money($amount);
}

function time_ago(?string $datetime): string
{
    if (!$datetime) return '';
    $ts = strtotime($datetime);
    if (!$ts) return '';
    $diff = time() - $ts;
    if ($diff < 0) return 'just now';
    if ($diff < 60) return 'just now';
    if ($diff < 3600) return floor($diff / 60) . 'm ago';
    if ($diff < 86400) return floor($diff / 3600) . 'h ago';
    if ($diff < 2592000) return floor($diff / 86400) . 'd ago';
    return format_date($datetime);
}

function str_limit(string $string, int $limit = 100, string $end = '...'): string
{
    if (mb_strlen($string) <= $limit) return $string;
    return mb_substr($string, 0, $limit) . $end;
}

function is_rtl(): bool
{
    return Lang::isRtl();
}

function current_lang(): string
{
    return Lang::current();
}

function lang(string $key, array $replacements = []): string
{
    return Lang::get($key, $replacements);
}

function dd($var): void
{
    echo '<pre>';
    var_dump($var);
    echo '</pre>';
    die;
}

function status_badge(string $status): string
{
    $map = [
        'active' => 'success', 'pending' => 'warning', 'draft' => 'secondary',
        'suspended' => 'danger', 'frozen' => 'info', 'terminated' => 'dark',
        'expired' => 'secondary', 'cancelled' => 'danger', 'completed' => 'success',
        'approved' => 'success', 'rejected' => 'danger', 'overdue' => 'danger',
        'paid' => 'success', 'partially_paid' => 'warning', 'issued' => 'primary',
        'pending_approval' => 'warning', 'under_review' => 'info',
        'open' => 'primary', 'closed' => 'secondary', 'resolved' => 'success',
        'submitted' => 'info', 'in_progress' => 'primary', 'escalated' => 'danger',
        'deceased' => 'dark', 'transferred' => 'info', 'void' => 'dark',
        'written_off' => 'dark', 'defaulted' => 'danger', 'bounced' => 'danger',
        'waitlisted' => 'info', 'enrolled' => 'primary',
    ];
    $class = $map[$status] ?? 'secondary';
    $label = ucfirst(str_replace('_', ' ', $status));
    return '<span class="badge bg-' . $class . '">' . e($label) . '</span>';
}

function active_class(string $path): string
{
    $uri = $_GET['_url'] ?? '';
    $uri = '/' . ltrim($uri, '/');
    return str_starts_with($uri, $path) ? 'active' : '';
}

function selected($value, $current): string
{
    return ((string)$value === (string)$current) ? 'selected' : '';
}

function checked($value, $current): string
{
    return ((string)$value === (string)$current) ? 'checked' : '';
}

/**
 * Render flash alerts â€” safe to call from any layout.
 * Returns array of ['type' => '...', 'message' => '...']
 */
function get_flash_alerts(): array
{
    static $consumed = false;
    static $cached = [];

    if (!$consumed) {
        $consumed = true;
        $cached = Session::getFlash('alerts') ?? [];
        $error = Session::getFlash('error');
        $success = Session::getFlash('success');
        $warning = Session::getFlash('warning');
        $info = Session::getFlash('info');
        if ($error)   $cached[] = ['type' => 'danger', 'message' => $error];
        if ($success)  $cached[] = ['type' => 'success', 'message' => $success];
        if ($warning)  $cached[] = ['type' => 'warning', 'message' => $warning];
        if ($info)     $cached[] = ['type' => 'info', 'message' => $info];
    }
    return $cached;
}
```

## File: core\Lang.php

```php
<?php
class Lang
{
    private static array $translations = [];
    private static bool $loaded = false;

    public static function get(string $key, array $replacements = []): string
    {
        self::loadIfNeeded();
        $lang = self::current();

        $value = self::$translations[$lang][$key] ?? self::$translations['en'][$key] ?? $key;

        foreach ($replacements as $k => $v) {
            $value = str_replace(':' . $k, $v, $value);
        }

        return $value;
    }

    public static function current(): string
    {
        return Session::get('app_language', 'en');
    }

    public static function set(string $code): void
    {
        Session::set('app_language', $code);
        self::$loaded = false;
    }

    public static function isRtl(): bool
    {
        return self::current() === 'ar';
    }

    public static function direction(): string
    {
        return self::isRtl() ? 'rtl' : 'ltr';
    }

    private static function loadIfNeeded(): void
    {
        if (self::$loaded) return;

        // Load core translations
        $enFile = ROOT_PATH . '/views/lang/en.php';
        $arFile = ROOT_PATH . '/views/lang/ar.php';

        self::$translations['en'] = file_exists($enFile) ? (require $enFile) : [];
        self::$translations['ar'] = file_exists($arFile) ? (require $arFile) : [];

        // Load module translations
        $modulesPath = ROOT_PATH . '/modules';
        if (is_dir($modulesPath)) {
            foreach (scandir($modulesPath) as $dir) {
                if ($dir === '.' || $dir === '..') continue;
                $modEnFile = $modulesPath . '/' . $dir . '/lang/en.php';
                $modArFile = $modulesPath . '/' . $dir . '/lang/ar.php';
                if (file_exists($modEnFile)) {
                    $modTranslations = require $modEnFile;
                    self::$translations['en'] = array_merge(self::$translations['en'], $modTranslations);
                }
                if (file_exists($modArFile)) {
                    $modTranslations = require $modArFile;
                    self::$translations['ar'] = array_merge(self::$translations['ar'], $modTranslations);
                }
            }
        }

        self::$loaded = true;
    }
}
```

## File: core\LangController.php

```php
<?php
class LangController extends Controller
{
    public function switchLang(Request $request): void
    {
        $code = $request->routeParam('code');
        if (in_array($code, ['en', 'ar'])) {
            Lang::set($code);
        }
        $referer = $_SERVER['HTTP_REFERER'] ?? '/dashboard';
        Response::redirect($referer);
    }
}
```

## File: core\Logger.php

```php
<?php
class Logger
{
    private static function write(string $level, string $message, array $context = []): void
    {
        $storagePath = ROOT_PATH . '/storage/logs';
        if (!is_dir($storagePath)) {
            @mkdir($storagePath, 0755, true);
        }

        $filename = $storagePath . '/app-' . date('Y-m-d') . '.log';
        $timestamp = date('Y-m-d H:i:s');
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'cli';
        $userId = Session::get('auth_user_id', 'guest');

        $line = "[{$timestamp}] [{$level}] [IP:{$ip}] [USER:{$userId}] {$message}";
        if (!empty($context)) {
            $line .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        $line .= PHP_EOL;

        @file_put_contents($filename, $line, FILE_APPEND | LOCK_EX);
    }

    public static function debug(string $message, array $context = []): void
    {
        self::write('DEBUG', $message, $context);
    }

    public static function info(string $message, array $context = []): void
    {
        self::write('INFO', $message, $context);
    }

    public static function warning(string $message, array $context = []): void
    {
        self::write('WARNING', $message, $context);
    }

    public static function error(string $message, array $context = []): void
    {
        self::write('ERROR', $message, $context);
    }

    public static function critical(string $message, array $context = []): void
    {
        self::write('CRITICAL', $message, $context);
    }
}
```

## File: core\MenuBuilder.php

```php
<?php
class MenuBuilder
{
    public static function render(): string
    {
        $menuItems = ModuleRegistry::getMenuItems();
        $currentUri = (new Request())->uri();
        $html = '';

        foreach ($menuItems as $item) {
            // Check top-level permission
            if (isset($item['permission']) && !Gate::can($item['permission'])) {
                continue;
            }

            $hasChildren = !empty($item['children']);
            $icon = $item['icon'] ?? 'fas fa-circle';
            $labelEn = $item['label_en'] ?? '';
            $labelAr = $item['label_ar'] ?? '';
            $label = Lang::current() === 'ar' ? $labelAr : $labelEn;

            if ($hasChildren) {
                $visibleChildren = [];
                foreach ($item['children'] as $child) {
                    if (isset($child['permission']) && !Gate::can($child['permission'])) {
                        continue;
                    }
                    $visibleChildren[] = $child;
                }

                if (empty($visibleChildren)) continue;

                $isOpen = false;
                foreach ($visibleChildren as $child) {
                    if (isset($child['route']) && str_starts_with($currentUri, $child['route'])) {
                        $isOpen = true;
                        break;
                    }
                }

                $collapseId = 'menu-' . md5($label);
                $showClass = $isOpen ? ' show' : '';
                $collapsedClass = $isOpen ? '' : ' collapsed';

                $html .= '<li class="nav-item">';
                $html .= '<a class="nav-link sidebar-link' . $collapsedClass . '" href="#' . $collapseId . '" data-bs-toggle="collapse" aria-expanded="' . ($isOpen ? 'true' : 'false') . '">';
                $html .= '<i class="' . e($icon) . ' me-2"></i>';
                $html .= '<span>' . e($label) . '</span>';
                $html .= '<i class="fas fa-chevron-down ms-auto sidebar-chevron"></i>';
                $html .= '</a>';
                $html .= '<div class="collapse' . $showClass . '" id="' . $collapseId . '">';
                $html .= '<ul class="nav flex-column ms-3 sidebar-submenu">';

                foreach ($visibleChildren as $child) {
                    $childLabel = Lang::current() === 'ar' ? ($child['label_ar'] ?? '') : ($child['label_en'] ?? '');
                    $childRoute = $child['route'] ?? '#';
                    $activeClass = str_starts_with($currentUri, $childRoute) ? ' active' : '';
                    $html .= '<li class="nav-item">';
                    $html .= '<a class="nav-link sidebar-sublink' . $activeClass . '" href="' . e($childRoute) . '">';
                    $html .= '<i class="fas fa-circle fa-xs me-2"></i>' . e($childLabel);
                    $html .= '</a></li>';
                }

                $html .= '</ul></div></li>';
            } else {
                $route = $item['route'] ?? '#';
                $activeClass = str_starts_with($currentUri, $route) ? ' active' : '';
                $html .= '<li class="nav-item">';
                $html .= '<a class="nav-link sidebar-link' . $activeClass . '" href="' . e($route) . '">';
                $html .= '<i class="' . e($icon) . ' me-2"></i>';
                $html .= '<span>' . e($label) . '</span>';
                $html .= '</a></li>';
            }
        }

        return $html;
    }
}
```

## File: core\Middleware\AuthMiddleware.php

```php
<?php
class AuthMiddleware
{
    public function handle(Request $request): void
    {
        if (!Auth::check()) {
            if ($request->isAjax()) {
                Response::json(['success' => false, 'message' => 'Unauthorized'], 401);
            }
            Session::flash('redirect_after_login', $request->uri());
            Response::redirect('/login');
        }
    }
}
```

## File: core\Middleware\AuthMiddleware_113.php

```php
<?php
class AuthMiddleware
{
    /**
     * BUG 5 FIX: Enforce must_change_password globally.
     * If the user is flagged, they cannot access ANY page except
     * /change-password and /logout. No module route bypasses this.
     */
    public function handle(Request $request): void
    {
        if (!Auth::check()) {
            if ($request->isAjax()) {
                Response::json(['success' => false, 'message' => 'Unauthorized'], 401);
            }
            Session::flash('redirect_after_login', $request->uri());
            Response::redirect('/login');
        }

        // Check must_change_password
        $sessionUser = Session::get('auth_user');
        if ($sessionUser && !empty($sessionUser['must_change_password'])) {
            $uri = $request->uri();
            // Allow only change-password and logout
            $allowed = ['/change-password', '/logout', '/lang/en', '/lang/ar'];
            $isAllowed = false;
            foreach ($allowed as $path) {
                if ($uri === $path || str_starts_with($uri, $path)) {
                    $isAllowed = true;
                    break;
                }
            }
            if (!$isAllowed) {
                Session::flash('warning', 'You must change your password before continuing.');
                Response::redirect('/change-password');
            }
        }
    }
}
```

## File: core\Middleware\GuestMiddleware.php

```php
<?php
class GuestMiddleware
{
    public function handle(Request $request): void
    {
        if (Auth::check()) {
            Response::redirect('/dashboard');
        }
    }
}
```

## File: core\Middleware\PermissionMiddleware.php

```php
<?php
class PermissionMiddleware
{
    private string $permission;

    public function __construct(string $permission = '')
    {
        $this->permission = $permission;
    }

    public function handle(Request $request): void
    {
        if ($this->permission && !Gate::can($this->permission)) {
            if ($request->isAjax()) {
                Response::json(['success' => false, 'message' => 'Forbidden'], 403);
            }
            http_response_code(403);
            $viewFile = ROOT_PATH . '/views/errors/403.php';
            if (file_exists($viewFile)) {
                include $viewFile;
            }
            exit;
        }
    }
}
```

## File: core\Model.php

```php
<?php
class Model
{
    protected string $table = '';
    protected string $primaryKey = 'id';
    protected Database $db;

    public function __construct()
    {
        $this->db = Database::getInstance();
    }

    public function find(int $id): ?array
    {
        return $this->db->fetchOne(
            "SELECT * FROM `{$this->table}` WHERE `{$this->primaryKey}` = ?",
            [$id]
        );
    }

    public function all(array $conditions = [], string $orderBy = 'id DESC', int $limit = 100): array
    {
        $where = '1=1';
        $params = [];
        foreach ($conditions as $col => $val) {
            $where .= " AND `{$col}` = ?";
            $params[] = $val;
        }
        return $this->db->fetchAll(
            "SELECT * FROM `{$this->table}` WHERE {$where} ORDER BY {$orderBy} LIMIT ?",
            array_merge($params, [$limit])
        );
    }

    public function create(array $data): int
    {
        return $this->db->insert($this->table, $data);
    }

    public function updateById(int $id, array $data): int
    {
        return $this->db->update($this->table, $data, "`{$this->primaryKey}` = ?", [$id]);
    }

    public function softDelete(int $id, int $userId): int
    {
        return $this->db->update($this->table, [
            'is_deleted' => 1,
            'deleted_at' => date('Y-m-d H:i:s'),
            'deleted_by' => $userId,
        ], "`{$this->primaryKey}` = ?", [$id]);
    }

    public function exists(int $id): bool
    {
        return (bool) $this->db->fetchColumn(
            "SELECT COUNT(*) FROM `{$this->table}` WHERE `{$this->primaryKey}` = ?",
            [$id]
        );
    }

    public function count(array $conditions = []): int
    {
        $where = '1=1';
        $params = [];
        foreach ($conditions as $col => $val) {
            $where .= " AND `{$col}` = ?";
            $params[] = $val;
        }
        return (int) $this->db->fetchColumn(
            "SELECT COUNT(*) FROM `{$this->table}` WHERE {$where}",
            $params
        );
    }
}
```

## File: core\ModuleRegistry.php

```php
<?php
class ModuleRegistry
{
    private static ?array $modules = null;

    public static function getModules(): array
    {
        self::loadIfNeeded();
        return self::$modules;
    }

    public static function getModule(string $name): ?array
    {
        self::loadIfNeeded();
        return self::$modules[$name] ?? null;
    }

    public static function getMenuItems(): array
    {
        $items = [];
        foreach (self::getModules() as $mod) {
            if (isset($mod['menu'])) {
                foreach ($mod['menu'] as $menuItem) {
                    $menuItem['_module'] = $mod['name'];
                    $menuItem['_sort'] = $menuItem['sort_order'] ?? $mod['sort_order'] ?? 100;
                    $items[] = $menuItem;
                }
            }
        }
        usort($items, fn($a, $b) => ($a['_sort'] ?? 100) <=> ($b['_sort'] ?? 100));
        return $items;
    }

    public static function getRoutes(): array
    {
        $allRoutes = [];
        foreach (self::getModules() as $mod) {
            $routesFile = ROOT_PATH . '/modules/' . $mod['name'] . '/' . ($mod['routes_file'] ?? 'routes.php');
            if (file_exists($routesFile)) {
                $routes = require $routesFile;
                if (is_array($routes)) {
                    foreach ($routes as $route) {
                        $route['_module'] = $mod['name'];
                        $allRoutes[] = $route;
                    }
                }
            }
        }
        return $allRoutes;
    }

    public static function getWidgets(): array
    {
        $widgets = [];
        foreach (self::getModules() as $mod) {
            if (isset($mod['dashboard_widgets'])) {
                foreach ($mod['dashboard_widgets'] as $widget) {
                    $widget['_module'] = $mod['name'];
                    $widgets[] = $widget;
                }
            }
        }
        usort($widgets, fn($a, $b) => ($a['sort_order'] ?? 100) <=> ($b['sort_order'] ?? 100));
        return $widgets;
    }

    public static function getCssFiles(): array
    {
        $files = [];
        foreach (self::getModules() as $mod) {
            if (isset($mod['css_files'])) {
                foreach ($mod['css_files'] as $css) {
                    $fullPath = '/modules/' . $mod['name'] . '/' . $css;
                    if (file_exists(ROOT_PATH . $fullPath) || file_exists(PUBLIC_PATH . '/modules/' . $mod['name'] . '/' . $css)) {
                        $files[] = $fullPath;
                    }
                }
            }
        }
        return $files;
    }

    public static function getJsFiles(): array
    {
        $files = [];
        foreach (self::getModules() as $mod) {
            if (isset($mod['js_files'])) {
                foreach ($mod['js_files'] as $js) {
                    $fullPath = '/modules/' . $mod['name'] . '/' . $js;
                    $files[] = $fullPath;
                }
            }
        }
        return $files;
    }

    private static function loadIfNeeded(): void
    {
        if (self::$modules !== null) return;
        self::$modules = [];

        $modulesPath = ROOT_PATH . '/modules';
        if (!is_dir($modulesPath)) {
            @mkdir($modulesPath, 0755, true);
            return;
        }

        foreach (scandir($modulesPath) as $dir) {
            if ($dir === '.' || $dir === '..') continue;
            $manifestFile = $modulesPath . '/' . $dir . '/module.json';
            if (file_exists($manifestFile)) {
                $manifest = json_decode(file_get_contents($manifestFile), true);
                if ($manifest && isset($manifest['name'])) {
                    self::$modules[$manifest['name']] = $manifest;
                }
            }
        }

        uasort(self::$modules, fn($a, $b) => ($a['sort_order'] ?? 100) <=> ($b['sort_order'] ?? 100));
    }

    public static function clearCache(): void
    {
        self::$modules = null;
    }
}
```

## File: core\Pagination.php

```php
<?php
class Pagination
{
    private int $total;
    private int $perPage;
    private int $currentPage;
    private string $baseUrl;
    private int $totalPages;

    public function __construct(int $total, int $perPage, int $currentPage, string $baseUrl)
    {
        $this->total = $total;
        $this->perPage = max(1, $perPage);
        $this->currentPage = max(1, $currentPage);
        $this->baseUrl = $baseUrl;
        $this->totalPages = (int) ceil($total / $this->perPage);
    }

    public function render(): string
    {
        if ($this->totalPages <= 1) return '';

        $html = '<nav aria-label="Page navigation"><ul class="pagination justify-content-center">';

        // First
        $html .= $this->pageItem(1, '<i class="fas fa-angle-double-left"></i>', $this->currentPage === 1);

        // Previous
        $prev = max(1, $this->currentPage - 1);
        $html .= $this->pageItem($prev, '<i class="fas fa-angle-left"></i>', $this->currentPage === 1);

        // Page numbers
        $range = $this->getPageRange();
        if ($range[0] > 1) {
            $html .= '<li class="page-item disabled"><span class="page-link">...</span></li>';
        }
        foreach ($range as $page) {
            $active = $page === $this->currentPage ? ' active' : '';
            $html .= '<li class="page-item' . $active . '"><a class="page-link" href="' . $this->url($page) . '">' . $page . '</a></li>';
        }
        if (end($range) < $this->totalPages) {
            $html .= '<li class="page-item disabled"><span class="page-link">...</span></li>';
        }

        // Next
        $next = min($this->totalPages, $this->currentPage + 1);
        $html .= $this->pageItem($next, '<i class="fas fa-angle-right"></i>', $this->currentPage === $this->totalPages);

        // Last
        $html .= $this->pageItem($this->totalPages, '<i class="fas fa-angle-double-right"></i>', $this->currentPage === $this->totalPages);

        $html .= '</ul></nav>';
        $html .= '<div class="text-center text-muted small mb-3">Showing ' . (($this->currentPage - 1) * $this->perPage + 1) . ' to ' . min($this->currentPage * $this->perPage, $this->total) . ' of ' . number_format($this->total) . ' entries</div>';

        return $html;
    }

    private function pageItem(int $page, string $label, bool $disabled): string
    {
        if ($disabled) {
            return '<li class="page-item disabled"><span class="page-link">' . $label . '</span></li>';
        }
        return '<li class="page-item"><a class="page-link" href="' . $this->url($page) . '">' . $label . '</a></li>';
    }

    private function getPageRange(): array
    {
        $delta = 2;
        $start = max(1, $this->currentPage - $delta);
        $end = min($this->totalPages, $this->currentPage + $delta);
        return range($start, $end);
    }

    private function url(int $page): string
    {
        $params = $_GET;
        unset($params['_url']);
        $params['page'] = $page;
        $sep = str_contains($this->baseUrl, '?') ? '&' : '?';
        return $this->baseUrl . $sep . http_build_query($params);
    }

    public function totalPages(): int
    {
        return $this->totalPages;
    }
}
```

## File: core\ProfileController.php

```php
<?php
class ProfileController extends Controller
{
    public function show(Request $request): void
    {
        $user = Auth::user();
        $this->view('auth/profile', [
            'title' => 'Profile',
            'breadcrumbs' => [['label' => 'Profile']],
            'user' => $user,
        ], 'app');
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'full_name_en' => 'required|string|max:300',
            'email'        => 'required|email|max:191',
            'phone'        => 'nullable|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $this->db()->update('auth_users', $data, 'id = ?', [Auth::id()]);

        $this->audit('update', 'user', Auth::id(), 'Updated own profile');
        $this->json(['success' => true, 'message' => 'Profile updated successfully']);
    }

    public function showChangePassword(Request $request): void
    {
        $this->view('auth/change_password', [
            'title' => lang('auth.change_password'),
            'breadcrumbs' => [['label' => lang('auth.change_password')]],
        ], 'app');
    }

    public function changePassword(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            if ($request->isAjax()) {
                $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
                return;
            }
            $this->redirect('/change-password');
            return;
        }

        $currentPassword = $request->post('current_password', '');
        $newPassword = $request->post('new_password', '');
        $confirmPassword = $request->post('new_password_confirmation', '');

        $errors = [];
        if (empty($currentPassword)) $errors['current_password'] = ['Current password is required'];
        if (empty($newPassword)) $errors['new_password'] = ['New password is required'];
        if ($newPassword !== $confirmPassword) $errors['new_password_confirmation'] = ['Passwords do not match'];
        if (mb_strlen($newPassword) < (int)Config::get('password_min_length', 8)) {
            $errors['new_password'] = ['Password must be at least ' . Config::get('password_min_length', 8) . ' characters'];
        }

        if (!empty($errors)) {
            if ($request->isAjax()) {
                $this->json(['success' => false, 'errors' => $errors], 422);
                return;
            }
            Session::flash('errors', $errors);
            $this->redirect('/change-password');
            return;
        }

        $user = $this->db()->fetchOne("SELECT password_hash FROM auth_users WHERE id = ?", [Auth::id()]);
        if (!$user || !password_verify($currentPassword, $user['password_hash'])) {
            $msg = 'Current password is incorrect';
            if ($request->isAjax()) {
                $this->json(['success' => false, 'errors' => ['current_password' => [$msg]]], 422);
                return;
            }
            Session::flash('error', $msg);
            $this->redirect('/change-password');
            return;
        }

        $hash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 12]);
        $this->db()->update('auth_users', [
            'password_hash' => $hash,
            'password_changed_at' => date('Y-m-d H:i:s'),
            'must_change_password' => 0,
        ], 'id = ?', [Auth::id()]);

        // Update session
        $sessionUser = Session::get('auth_user');
        $sessionUser['must_change_password'] = 0;
        Session::set('auth_user', $sessionUser);

        $this->audit('update', 'user', Auth::id(), 'Changed own password');

        if ($request->isAjax()) {
            $this->json(['success' => true, 'message' => 'Password changed successfully', 'redirect' => '/dashboard']);
            return;
        }
        Session::flash('alerts', [['type' => 'success', 'message' => 'Password changed successfully']]);
        $this->redirect('/dashboard');
    }
}
```

## File: core\Request.php

```php
<?php
class Request
{
    private array $getParams;
    private array $postParams;
    private array $serverParams;
    private array $files;
    private array $routeParams = [];

    public function __construct()
    {
        $this->getParams = $_GET;
        $this->postParams = $_POST;
        $this->serverParams = $_SERVER;
        $this->files = $_FILES;
    }

    public function get(string $key, $default = null)
    {
        return $this->getParams[$key] ?? $default;
    }

    public function post(string $key, $default = null)
    {
        return $this->postParams[$key] ?? $default;
    }

    public function input(string $key, $default = null)
    {
        return $this->postParams[$key] ?? $this->getParams[$key] ?? $default;
    }

    public function all(): array
    {
        return array_merge($this->getParams, $this->postParams);
    }

    public function only(array $keys): array
    {
        $all = $this->all();
        return array_intersect_key($all, array_flip($keys));
    }

    public function has(string $key): bool
    {
        return isset($this->postParams[$key]) || isset($this->getParams[$key]);
    }

    public function file(string $name): ?array
    {
        return $this->files[$name] ?? null;
    }

    public function method(): string
    {
        return strtoupper($this->serverParams['REQUEST_METHOD'] ?? 'GET');
    }

    public function uri(): string
    {
        $uri = $this->getParams['_url'] ?? '';
        $uri = '/' . ltrim($uri, '/');
        $uri = strtok($uri, '?');
        return $uri ?: '/';
    }

    public function isAjax(): bool
    {
        return strtolower($this->serverParams['HTTP_X_REQUESTED_WITH'] ?? '') === 'xmlhttprequest';
    }

    public function ip(): string
    {
        if (!empty($this->serverParams['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $this->serverParams['HTTP_X_FORWARDED_FOR']);
            return trim($ips[0]);
        }
        return $this->serverParams['REMOTE_ADDR'] ?? '0.0.0.0';
    }

    public function userAgent(): string
    {
        return $this->serverParams['HTTP_USER_AGENT'] ?? '';
    }

    public function setRouteParams(array $params): void
    {
        $this->routeParams = $params;
    }

    public function routeParam(string $name, $default = null)
    {
        return $this->routeParams[$name] ?? $default;
    }

    public function header(string $name, $default = null): ?string
    {
        $key = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
        return $this->serverParams[$key] ?? $default;
    }

    public function bearerToken(): ?string
    {
        $auth = $this->serverParams['HTTP_AUTHORIZATION'] ?? '';
        if (str_starts_with($auth, 'Bearer ')) {
            return substr($auth, 7);
        }
        return null;
    }

    public function csrfToken(): ?string
    {
        return $this->post('_token') ?? $this->header('X-CSRF-TOKEN');
    }
}
```

## File: core\Response.php

```php
<?php
class Response
{
    public static function json(array $data, int $statusCode = 200): void
    {
        http_response_code($statusCode);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    public static function redirect(string $url, int $statusCode = 302): void
    {
        header('Location: ' . $url, true, $statusCode);
        exit;
    }

    public static function download(string $filepath, string $filename): void
    {
        if (!file_exists($filepath)) {
            http_response_code(404);
            echo 'File not found';
            exit;
        }
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . filesize($filepath));
        readfile($filepath);
        exit;
    }

    public static function error(int $code, string $message = ''): void
    {
        http_response_code($code);
        echo $message;
        exit;
    }
}
```

## File: core\Router.php

```php
<?php
class Router
{
    private array $routes = [];

    public function addRoute(string $method, string $pattern, $handler, ?string $permission = null, ?string $moduleName = null, array $middleware = []): void
    {
        $this->routes[] = [
            'method'     => strtoupper($method),
            'pattern'    => $pattern,
            'handler'    => $handler,
            'permission' => $permission,
            'module'     => $moduleName,
            'middleware'  => $middleware,
        ];
    }

    public function dispatch(Request $request): void
    {
        $method = $request->method();
        $uri = $request->uri();

        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) continue;

            $params = $this->matchPattern($route['pattern'], $uri);
            if ($params === false) continue;

            $request->setRouteParams($params);

            // Run middleware
            foreach ($route['middleware'] as $mw) {
                $this->runMiddleware($mw, $request);
            }

            // Check permission
            if ($route['permission']) {
                if (!Auth::check()) {
                    if ($request->isAjax()) {
                        Response::json(['success' => false, 'message' => 'Unauthorized'], 401);
                    }
                    Response::redirect('/login');
                    return;
                }
                Gate::authorize($route['permission']);
            }

            // Resolve handler
            $this->callHandler($route['handler'], $request, $route['module']);
            return;
        }

        // 404
        http_response_code(404);
        if ($request->isAjax()) {
            Response::json(['success' => false, 'message' => 'Not found'], 404);
        }
        $viewFile = ROOT_PATH . '/views/errors/404.php';
        if (file_exists($viewFile)) {
            $data = ['code' => 404, 'message' => 'Page not found'];
            $view = View::getInstance();
            $layout = Auth::check() ? 'app' : 'auth';
            $view->render('errors/404', $data, $layout);
        } else {
            echo '<h1>404 Not Found</h1>';
        }
        exit;
    }

    private function matchPattern(string $pattern, string $uri)
    {
        // Convert {param} to regex groups
        $regex = preg_replace('/\{([a-zA-Z_]+)\}/', '(?P<$1>[^/]+)', $pattern);
        $regex = '#^' . $regex . '$#';

        if (preg_match($regex, $uri, $matches)) {
            $params = [];
            foreach ($matches as $key => $value) {
                if (is_string($key)) {
                    $params[$key] = $value;
                }
            }
            return $params;
        }

        return false;
    }

    private function runMiddleware(string $middlewareClass, Request $request): void
    {
        if (class_exists($middlewareClass)) {
            $mw = new $middlewareClass();
            $mw->handle($request);
        }
    }

    private function callHandler($handler, Request $request, ?string $moduleName): void
    {
        if (is_callable($handler)) {
            call_user_func($handler, $request);
            return;
        }

        if (is_string($handler) && str_contains($handler, '@')) {
            [$controllerName, $methodName] = explode('@', $handler);

            // Try to load from module
            if ($moduleName) {
                $controllerFile = ROOT_PATH . '/modules/' . $moduleName . '/Controllers/' . $controllerName . '.php';
                if (file_exists($controllerFile)) {
                    require_once $controllerFile;
                }
            }

            // Try core controllers
            if (!class_exists($controllerName)) {
                $coreControllerFile = ROOT_PATH . '/core/' . $controllerName . '.php';
                if (file_exists($coreControllerFile)) {
                    require_once $coreControllerFile;
                }
            }

            if (!class_exists($controllerName)) {
                throw new \RuntimeException("Controller not found: {$controllerName}");
            }

            $controller = new $controllerName();
            if ($controller instanceof Controller) {
                $controller->setRequest($request);
                if ($moduleName) {
                    $controller->setModuleName($moduleName);
                }
            }

            if (!method_exists($controller, $methodName)) {
                throw new \RuntimeException("Method not found: {$controllerName}@{$methodName}");
            }

            $controller->$methodName($request);
        }
    }
}
```

## File: core\SequenceGenerator.php

```php
<?php
class SequenceGenerator
{
    public static function next(string $sequenceName): ?string
    {
        $db = Database::getInstance();
        $db->execute("CALL sp_generate_next_sequence(?, @next_val)", [$sequenceName]);
        $result = $db->fetchOne("SELECT @next_val AS val");
        return $result['val'] ?? null;
    }
}
```

## File: core\Session.php

```php
<?php
class Session
{
    private static bool $started = false;

    public static function start(): void
    {
        if (self::$started || session_status() === PHP_SESSION_ACTIVE) {
            self::$started = true;
            return;
        }

        $lifetime = 3600;
        $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');

        session_set_cookie_params([
            'lifetime' => $lifetime,
            'path'     => '/',
            'domain'   => '',
            'secure'   => $secure,
            'httponly'  => true,
            'samesite'  => 'Lax',
        ]);

        session_name('cms_session');
        session_start();
        self::$started = true;

        // Session timeout check
        if (self::has('_last_activity')) {
            $inactive = time() - self::get('_last_activity');
            $timeout = self::get('_session_timeout', $lifetime);
            if ($inactive > $timeout) {
                self::destroy();
                self::start();
                return;
            }
        }
        self::set('_last_activity', time());
    }

    public static function get(string $key, $default = null)
    {
        return $_SESSION[$key] ?? $default;
    }

    public static function set(string $key, $value): void
    {
        $_SESSION[$key] = $value;
    }

    public static function has(string $key): bool
    {
        return isset($_SESSION[$key]);
    }

    public static function remove(string $key): void
    {
        unset($_SESSION[$key]);
    }

    public static function flash(string $key, $value): void
    {
        $_SESSION['_flash'][$key] = $value;
    }

    public static function getFlash(string $key, $default = null)
    {
        $value = $_SESSION['_flash'][$key] ?? $default;
        unset($_SESSION['_flash'][$key]);
        return $value;
    }

    public static function getAllFlash(): array
    {
        $flash = $_SESSION['_flash'] ?? [];
        unset($_SESSION['_flash']);
        return $flash;
    }

    public static function regenerate(): void
    {
        session_regenerate_id(true);
    }

    public static function destroy(): void
    {
        $_SESSION = [];
        if (ini_get('session.use_cookies')) {
            $params = session_get_cookie_params();
            setcookie(session_name(), '', time() - 42000,
                $params['path'], $params['domain'],
                $params['secure'], $params['httponly']
            );
        }
        session_destroy();
        self::$started = false;
    }

    public static function id(): string
    {
        return session_id();
    }
}
```

## File: core\Validator.php

```php
<?php
class Validator
{
    private array $data;
    private array $rules;
    private array $errors = [];
    private array $validated = [];

    public function __construct(array $data, array $rules)
    {
        $this->data = $data;
        $this->rules = $rules;
        $this->validate();
    }

    public function fails(): bool
    {
        return !empty($this->errors);
    }

    public function errors(): array
    {
        return $this->errors;
    }

    public function validated(): array
    {
        return $this->validated;
    }

    public function firstError(): ?string
    {
        foreach ($this->errors as $field => $messages) {
            return $messages[0] ?? null;
        }
        return null;
    }

    private function validate(): void
    {
        foreach ($this->rules as $field => $ruleString) {
            $rules = explode('|', $ruleString);
            $value = $this->data[$field] ?? null;
            $isNullable = in_array('nullable', $rules);
            $isFile = in_array('file', $rules);

            if ($isNullable && ($value === null || $value === '')) {
                if (array_key_exists($field, $this->data)) {
                    $this->validated[$field] = $value;
                }
                continue;
            }

            foreach ($rules as $rule) {
                if ($rule === 'nullable') continue;

                $param = null;
                if (str_contains($rule, ':')) {
                    [$rule, $param] = explode(':', $rule, 2);
                }

                $error = $this->checkRule($field, $value, $rule, $param);
                if ($error) {
                    $this->errors[$field][] = $error;
                }
            }

            if (!isset($this->errors[$field])) {
                $this->validated[$field] = $value;
            }
        }
    }

    private function checkRule(string $field, $value, string $rule, ?string $param): ?string
    {
        $label = str_replace('_', ' ', $field);

        return match ($rule) {
            'required' => ($value === null || $value === '' || $value === [])
                ? "{$label} is required" : null,

            'string' => (!is_string($value) && $value !== null)
                ? "{$label} must be a string" : null,

            'integer' => ($value !== null && $value !== '' && !filter_var($value, FILTER_VALIDATE_INT) && $value !== '0' && $value !== 0)
                ? "{$label} must be an integer" : null,

            'decimal' => ($value !== null && $value !== '' && !is_numeric($value))
                ? "{$label} must be a number" : null,

            'email' => ($value !== null && $value !== '' && !filter_var($value, FILTER_VALIDATE_EMAIL))
                ? "{$label} must be a valid email" : null,

            'date' => ($value !== null && $value !== '' && !strtotime($value))
                ? "{$label} must be a valid date" : null,

            'datetime' => ($value !== null && $value !== '' && !strtotime($value))
                ? "{$label} must be a valid datetime" : null,

            'boolean' => ($value !== null && !in_array($value, [true, false, 0, 1, '0', '1', 'true', 'false'], true))
                ? "{$label} must be true or false" : null,

            'min' => $this->checkMin($label, $value, (int)$param),
            'max' => $this->checkMax($label, $value, (int)$param),

            'between' => $this->checkBetween($label, $value, $param),

            'in' => ($value !== null && $value !== '' && !in_array($value, explode(',', $param)))
                ? "{$label} must be one of: {$param}" : null,

            'not_in' => ($value !== null && $value !== '' && in_array($value, explode(',', $param)))
                ? "{$label} must not be one of: {$param}" : null,

            'exists' => $this->checkExists($label, $value, $param),
            'unique' => $this->checkUnique($label, $value, $param),

            'file' => null, // File presence checked by 'required'

            'mimes' => $this->checkMimes($label, $field, $param),

            'max_file' => $this->checkMaxFile($label, $field, (int)$param),

            'regex' => ($value !== null && $value !== '' && !preg_match($param, $value))
                ? "{$label} format is invalid" : null,

            'confirmed' => (($this->data[$field . '_confirmation'] ?? null) !== $value)
                ? "{$label} confirmation does not match" : null,

            'same' => (($this->data[$param] ?? null) !== $value)
                ? "{$label} must match {$param}" : null,

            'after' => ($value !== null && $value !== '' && strtotime($value) <= strtotime($param))
                ? "{$label} must be after {$param}" : null,

            'before' => ($value !== null && $value !== '' && strtotime($value) >= strtotime($param))
                ? "{$label} must be before {$param}" : null,

            'array' => ($value !== null && !is_array($value))
                ? "{$label} must be an array" : null,

            'json' => ($value !== null && $value !== '' && json_decode($value) === null && json_last_error() !== JSON_ERROR_NONE)
                ? "{$label} must be valid JSON" : null,

            default => null,
        };
    }

    private function checkMin(string $label, $value, int $min): ?string
    {
        if ($value === null || $value === '') return null;
        if (is_string($value) && mb_strlen($value) < $min) {
            return "{$label} must be at least {$min} characters";
        }
        if (is_numeric($value) && $value < $min) {
            return "{$label} must be at least {$min}";
        }
        return null;
    }

    private function checkMax(string $label, $value, int $max): ?string
    {
        if ($value === null || $value === '') return null;
        if (is_string($value) && mb_strlen($value) > $max) {
            return "{$label} must not exceed {$max} characters";
        }
        if (is_numeric($value) && $value > $max) {
            return "{$label} must not exceed {$max}";
        }
        return null;
    }

    private function checkBetween(string $label, $value, string $param): ?string
    {
        if ($value === null || $value === '') return null;
        [$min, $max] = explode(',', $param);
        if (is_numeric($value) && ($value < $min || $value > $max)) {
            return "{$label} must be between {$min} and {$max}";
        }
        return null;
    }

    private function checkExists(string $label, $value, string $param): ?string
    {
        if ($value === null || $value === '') return null;
        [$table, $column] = explode(',', $param);
        try {
            $db = Database::getInstance();
            $count = $db->fetchColumn("SELECT COUNT(*) FROM `{$table}` WHERE `{$column}` = ?", [$value]);
            if ($count == 0) {
                return "{$label} does not exist";
            }
        } catch (\Exception $e) {
            return null;
        }
        return null;
    }

    private function checkUnique(string $label, $value, string $param): ?string
    {
        if ($value === null || $value === '') return null;
        $parts = explode(',', $param);
        $table = $parts[0];
        $column = $parts[1];
        $exceptId = $parts[2] ?? null;
        try {
            $db = Database::getInstance();
            $sql = "SELECT COUNT(*) FROM `{$table}` WHERE `{$column}` = ?";
            $params = [$value];
            if ($exceptId) {
                $sql .= " AND id != ?";
                $params[] = $exceptId;
            }
            $count = $db->fetchColumn($sql, $params);
            if ($count > 0) {
                return "{$label} already exists";
            }
        } catch (\Exception $e) {
            return null;
        }
        return null;
    }

    private function checkMimes(string $label, string $field, string $param): ?string
    {
        $file = $_FILES[$field] ?? null;
        if (!$file || $file['error'] === UPLOAD_ERR_NO_FILE) return null;
        $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        $allowed = explode(',', $param);
        if (!in_array($ext, $allowed)) {
            return "{$label} must be a file of type: {$param}";
        }
        return null;
    }

    private function checkMaxFile(string $label, string $field, int $maxKB): ?string
    {
        $file = $_FILES[$field] ?? null;
        if (!$file || $file['error'] === UPLOAD_ERR_NO_FILE) return null;
        if ($file['size'] > $maxKB * 1024) {
            return "{$label} must not exceed " . round($maxKB / 1024, 1) . "MB";
        }
        return null;
    }
}
```

## File: core\View.php

```php
<?php
class View
{
    private static array $pushStacks = [];
    private static ?string $currentPush = null;
    private static ?View $instance = null;

    public static function getInstance(): View
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Render a view inside a layout
     */
    public function render(string $viewPath, array $data = [], string $layout = 'app', ?string $moduleName = null): void
    {
        // Resolve view file
        $viewFile = $this->resolveViewFile($viewPath, $moduleName);
        if (!$viewFile || !file_exists($viewFile)) {
            throw new \RuntimeException("View not found: {$viewPath} (looked for: {$viewFile})");
        }

        // Render view content
        $content = $this->renderFile($viewFile, $data);

        // Render layout with content
        $layoutFile = ROOT_PATH . '/views/layouts/' . $layout . '.php';
        if (!file_exists($layoutFile)) {
            echo $content;
            return;
        }

        $data['content'] = $content;
        $data['_styles'] = self::getStack('styles');
        $data['_scripts'] = self::getStack('scripts');
        self::$pushStacks = []; // Clear for next render

        echo $this->renderFile($layoutFile, $data);
    }

    /**
     * Render a partial (no layout)
     */
    public function partial(string $path, array $data = []): string
    {
        $file = ROOT_PATH . '/views/' . $path . '.php';
        if (!file_exists($file)) {
            return '';
        }
        return $this->renderFile($file, $data);
    }

    /**
     * Push content to a named stack (for CSS/JS)
     */
    public function push(string $name): void
    {
        self::$currentPush = $name;
        ob_start();
    }

    /**
     * End push
     */
    public function endPush(): void
    {
        if (self::$currentPush !== null) {
            $content = ob_get_clean();
            if (!isset(self::$pushStacks[self::$currentPush])) {
                self::$pushStacks[self::$currentPush] = '';
            }
            self::$pushStacks[self::$currentPush] .= $content;
            self::$currentPush = null;
        }
    }

    public static function getStack(string $name): string
    {
        return self::$pushStacks[$name] ?? '';
    }

    private function resolveViewFile(string $viewPath, ?string $moduleName): string
    {
        if ($moduleName) {
            return ROOT_PATH . '/modules/' . $moduleName . '/views/' . $viewPath . '.php';
        }
        // Core views
        return ROOT_PATH . '/views/' . $viewPath . '.php';
    }

    private function renderFile(string $file, array $data): string
    {
        extract($data);
        $__view = $this;
        ob_start();
        include $file;
        return ob_get_clean();
    }
}
```

## File: core\View_111.php

```php
<?php
class View
{
    private static array $pushStacks = [];
    private static ?string $currentPush = null;
    private static ?View $instance = null;

    public static function getInstance(): View
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Render a view inside a layout.
     *
     * BUG 1 FIX: Pass stack content as template variables ($_styles, $_scripts)
     * so the layout can use them directly. Do NOT call getStack() in the layout
     * because stacks are cleared before layout rendering begins.
     */
    public function render(string $viewPath, array $data = [], string $layout = 'app', ?string $moduleName = null): void
    {
        $viewFile = $this->resolveViewFile($viewPath, $moduleName);
        if (!$viewFile || !file_exists($viewFile)) {
            throw new \RuntimeException("View not found: {$viewPath} (resolved: {$viewFile})");
        }

        // Reset stacks before rendering view â€” ensures clean state
        self::$pushStacks = [];

        // Render the view content â€” push/endPush calls happen here
        $content = $this->renderFile($viewFile, $data);

        // Capture stacks BEFORE clearing
        $stylesContent = self::getStack('styles');
        $scriptsContent = self::getStack('scripts');

        // Clear stacks for next render cycle
        self::$pushStacks = [];

        // Inject into layout data
        $data['content'] = $content;
        $data['_styles'] = $stylesContent;
        $data['_scripts'] = $scriptsContent;

        // Render layout wrapping the content
        $layoutFile = ROOT_PATH . '/views/layouts/' . $layout . '.php';
        if (!file_exists($layoutFile)) {
            // No layout â€” output raw content
            echo $content;
            return;
        }

        echo $this->renderFile($layoutFile, $data);
    }

    /**
     * Render a partial view (no layout wrapping).
     * Returns rendered HTML string.
     *
     * Resolves from core views directory. For module partials,
     * use renderModulePartial() or PHP include within module views.
     */
    public function partial(string $path, array $data = []): string
    {
        $file = ROOT_PATH . '/views/' . $path . '.php';
        if (!file_exists($file)) {
            return '';
        }
        return $this->renderFile($file, $data);
    }

    /**
     * Render a module's partial view.
     */
    public function modulePartial(string $module, string $path, array $data = []): string
    {
        $file = ROOT_PATH . '/modules/' . $module . '/views/' . $path . '.php';
        if (!file_exists($file)) {
            return '';
        }
        return $this->renderFile($file, $data);
    }

    /**
     * Start pushing content to a named stack.
     * Used in view files: <?php $this->push('styles') ?>
     */
    public function push(string $name): void
    {
        self::$currentPush = $name;
        ob_start();
    }

    /**
     * End push to named stack.
     * Used in view files: <?php $this->endPush() ?>
     */
    public function endPush(): void
    {
        if (self::$currentPush !== null) {
            $content = ob_get_clean();
            if (!isset(self::$pushStacks[self::$currentPush])) {
                self::$pushStacks[self::$currentPush] = '';
            }
            self::$pushStacks[self::$currentPush] .= $content;
            self::$currentPush = null;
        }
    }

    /**
     * Get content from a named stack.
     */
    public static function getStack(string $name): string
    {
        return self::$pushStacks[$name] ?? '';
    }

    private function resolveViewFile(string $viewPath, ?string $moduleName): ?string
    {
        if ($moduleName) {
            return ROOT_PATH . '/modules/' . $moduleName . '/views/' . $viewPath . '.php';
        }
        return ROOT_PATH . '/views/' . $viewPath . '.php';
    }

    /**
     * Render a PHP file with extracted data.
     * $this is available in the included file and refers to this View instance.
     */
    private function renderFile(string $__file, array $__data): string
    {
        extract($__data);
        $__view = $this;
        ob_start();
        try {
            include $__file;
        } catch (\Throwable $e) {
            ob_end_clean();
            throw $e;
        }
        return ob_get_clean();
    }
}
```

## File: deploy.sh

```sh
#!/usr/bin/env bash
# =============================================================================
#  Club Management System â€” Full CapRover Deploy Script
#  Usage:  chmod +x deploy.sh && ./deploy.sh
# =============================================================================
set -e

# â”€â”€â”€ CONFIGURATION â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
CAPROVER_URL="http://captain.caprover.al-arcade.com"
CAPROVER_PASSWORD="Alarcade123#"
APP_NAME="club-management-club"
GITLAB_REPO="http://gitlab.caprover.al-arcade.com/root/finalclubmanagementphp.git"
GITLAB_USER="root"
GITLAB_TOKEN="vHATWUzqfUC1nkXkGkai"

# Existing DB server
DB_HOST="srv-captain--mysql-db"
DB_PORT="3306"
DB_NAME="club_management"
DB_USER="root"
DB_PASS="Alarcade123#"
APP_URL="https://${APP_NAME}.caprover.al-arcade.com"

# â”€â”€â”€ COLORS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }

# â”€â”€â”€ 1. PREREQUISITES â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Checking prerequisites..."

if ! command -v node &>/dev/null; then
    info "Installing Node.js 20..."
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    apt-get install -y nodejs
fi
node -v

if ! command -v caprover &>/dev/null; then
    info "Installing CapRover CLI..."
    npm install -g caprover
fi
caprover --version

command -v git &>/dev/null || { apt-get update && apt-get install -y git; }
command -v curl &>/dev/null || { apt-get update && apt-get install -y curl; }

# â”€â”€â”€ 2. CLONE REPO â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
DEPLOY_DIR="/tmp/club_deploy_$(date +%s)"
info "Cloning repository to ${DEPLOY_DIR}..."
git clone "http://${GITLAB_USER}:${GITLAB_TOKEN}@$(echo $GITLAB_REPO | sed 's|http://||')" "$DEPLOY_DIR"
cd "$DEPLOY_DIR"

# â”€â”€â”€ 3. SAFETY CLEANUP â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Removing sensitive/dead files..."
rm -f code.sql
rm -f core/App_112.php core/Database_110.php core/View_111.php core/helpers_109.php
rm -rf prompts/

# â”€â”€â”€ 4. GET CAPROVER JWT TOKEN â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Authenticating with CapRover API..."
TOKEN=$(curl -sf -X POST "${CAPROVER_URL}/api/v2/login" \
  -H "Content-Type: application/json" \
  -d "{\"password\":\"${CAPROVER_PASSWORD}\"}" \
  | grep -o '"token":"[^"]*"' | cut -d'"' -f4)

[ -z "$TOKEN" ] && error "Auth failed. Check CAPROVER_URL and CAPROVER_PASSWORD."
info "Authenticated OK."

# â”€â”€â”€ 5. CREATE APP (idempotent â€” ignores error if already exists) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Creating CapRover app '${APP_NAME}'..."
curl -sf -X POST "${CAPROVER_URL}/api/v2/user/apps/appDefinitions/register" \
  -H "Content-Type: application/json" \
  -H "x-captain-auth: ${TOKEN}" \
  -d "{\"appName\":\"${APP_NAME}\",\"hasPersistentData\":false}" > /dev/null || true

# â”€â”€â”€ 6. SET ENVIRONMENT VARIABLES â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Setting environment variables..."
curl -sf -X POST "${CAPROVER_URL}/api/v2/user/apps/appDefinitions/update" \
  -H "Content-Type: application/json" \
  -H "x-captain-auth: ${TOKEN}" \
  -d "{
    \"appName\": \"${APP_NAME}\",
    \"envVars\": [
      {\"key\":\"APP_DEBUG\", \"value\":\"false\"},
      {\"key\":\"APP_URL\",   \"value\":\"${APP_URL}\"},
      {\"key\":\"DB_HOST\",   \"value\":\"${DB_HOST}\"},
      {\"key\":\"DB_PORT\",   \"value\":\"${DB_PORT}\"},
      {\"key\":\"DB_NAME\",   \"value\":\"${DB_NAME}\"},
      {\"key\":\"DB_USER\",   \"value\":\"${DB_USER}\"},
      {\"key\":\"DB_PASS\",   \"value\":\"${DB_PASS}\"}
    ]
  }" > /dev/null
info "Env vars set."

# â”€â”€â”€ 7. ENABLE HTTPS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Enabling HTTPS..."
curl -sf -X POST "${CAPROVER_URL}/api/v2/user/apps/appDefinitions/enablebasedomainssl" \
  -H "Content-Type: application/json" \
  -H "x-captain-auth: ${TOKEN}" \
  -d "{\"appName\":\"${APP_NAME}\"}" > /dev/null \
  || warn "HTTPS may already be enabled."

# â”€â”€â”€ 8. DEPLOY â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Deploying to CapRover from current directory..."
caprover deploy \
  --caproverUrl      "$CAPROVER_URL" \
  --caproverPassword "$CAPROVER_PASSWORD" \
  --appName          "$APP_NAME" \
  --branch           "main"

# â”€â”€â”€ DONE â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Deploy complete!"
echo ""
echo "  App URL : ${APP_URL}"
echo "  DB Host : ${DB_HOST}/${DB_NAME}"
echo ""

# â”€â”€â”€ CLEANUP â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
cd /tmp && rm -rf "$DEPLOY_DIR"
info "Temp files cleaned up."

```

## File: deployscript.txt

```txt
#!/usr/bin/env bash
# =============================================================================
#  Club Management System â€” Full CapRover Deploy Script
#  Usage:  chmod +x deploy.sh && ./deploy.sh
# =============================================================================
set -e

# â”€â”€â”€ CONFIGURATION â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
CAPROVER_URL="http://captain.caprover.al-arcade.com"
CAPROVER_PASSWORD="Alarcade123#"
APP_NAME="club-management-club"
GITLAB_REPO="http://gitlab.caprover.al-arcade.com/root/finalclubmanagementphp.git"
GITLAB_USER="root"
GITLAB_TOKEN="vHATWUzqfUC1nkXkGkai"

# Existing DB server
DB_HOST="srv-captain--mysql-db"
DB_PORT="3306"
DB_NAME="club_management"
DB_USER="root"
DB_PASS="Alarcade123#"
APP_URL="https://${APP_NAME}.caprover.al-arcade.com"

# â”€â”€â”€ COLORS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }

# â”€â”€â”€ 1. PREREQUISITES â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Checking prerequisites..."

if ! command -v node &>/dev/null; then
    info "Installing Node.js 20..."
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    apt-get install -y nodejs
fi
node -v

if ! command -v caprover &>/dev/null; then
    info "Installing CapRover CLI..."
    npm install -g caprover
fi
caprover --version

command -v git &>/dev/null || { apt-get update && apt-get install -y git; }
command -v curl &>/dev/null || { apt-get update && apt-get install -y curl; }

# â”€â”€â”€ 2. CLONE REPO â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
DEPLOY_DIR="/tmp/club_deploy_$(date +%s)"
info "Cloning repository to ${DEPLOY_DIR}..."
git clone "http://${GITLAB_USER}:${GITLAB_TOKEN}@$(echo $GITLAB_REPO | sed 's|http://||')" "$DEPLOY_DIR"
cd "$DEPLOY_DIR"

# â”€â”€â”€ 3. SAFETY CLEANUP â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Removing sensitive/dead files..."
rm -f code.sql
rm -f core/App_112.php core/Database_110.php core/View_111.php core/helpers_109.php
rm -rf prompts/

# â”€â”€â”€ 4. GET CAPROVER JWT TOKEN â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Authenticating with CapRover API..."
TOKEN=$(curl -sf -X POST "${CAPROVER_URL}/api/v2/login" \
  -H "Content-Type: application/json" \
  -d "{\"password\":\"${CAPROVER_PASSWORD}\"}" \
  | grep -o '"token":"[^"]*"' | cut -d'"' -f4)

[ -z "$TOKEN" ] && error "Auth failed. Check CAPROVER_URL and CAPROVER_PASSWORD."
info "Authenticated OK."

# â”€â”€â”€ 5. CREATE APP (idempotent â€” ignores error if already exists) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Creating CapRover app '${APP_NAME}'..."
curl -sf -X POST "${CAPROVER_URL}/api/v2/user/apps/appDefinitions/register" \
  -H "Content-Type: application/json" \
  -H "x-captain-auth: ${TOKEN}" \
  -d "{\"appName\":\"${APP_NAME}\",\"hasPersistentData\":false}" > /dev/null || true

# â”€â”€â”€ 6. SET ENVIRONMENT VARIABLES â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Setting environment variables..."
curl -sf -X POST "${CAPROVER_URL}/api/v2/user/apps/appDefinitions/update" \
  -H "Content-Type: application/json" \
  -H "x-captain-auth: ${TOKEN}" \
  -d "{
    \"appName\": \"${APP_NAME}\",
    \"envVars\": [
      {\"key\":\"APP_DEBUG\", \"value\":\"false\"},
      {\"key\":\"APP_URL\",   \"value\":\"${APP_URL}\"},
      {\"key\":\"DB_HOST\",   \"value\":\"${DB_HOST}\"},
      {\"key\":\"DB_PORT\",   \"value\":\"${DB_PORT}\"},
      {\"key\":\"DB_NAME\",   \"value\":\"${DB_NAME}\"},
      {\"key\":\"DB_USER\",   \"value\":\"${DB_USER}\"},
      {\"key\":\"DB_PASS\",   \"value\":\"${DB_PASS}\"}
    ]
  }" > /dev/null
info "Env vars set."

# â”€â”€â”€ 7. ENABLE HTTPS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Enabling HTTPS..."
curl -sf -X POST "${CAPROVER_URL}/api/v2/user/apps/appDefinitions/enablebasedomainssl" \
  -H "Content-Type: application/json" \
  -H "x-captain-auth: ${TOKEN}" \
  -d "{\"appName\":\"${APP_NAME}\"}" > /dev/null \
  || warn "HTTPS may already be enabled."

# â”€â”€â”€ 8. DEPLOY â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Deploying to CapRover from current directory..."
caprover deploy \
  --caproverUrl      "$CAPROVER_URL" \
  --caproverPassword "$CAPROVER_PASSWORD" \
  --appName          "$APP_NAME" \
  --branch           "main"

# â”€â”€â”€ DONE â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
info "Deploy complete!"
echo ""
echo "  App URL : ${APP_URL}"
echo "  DB Host : ${DB_HOST}/${DB_NAME}"
echo ""

# â”€â”€â”€ CLEANUP â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
cd /tmp && rm -rf "$DEPLOY_DIR"
info "Temp files cleaned up."

```

## File: Dockerfile

```text
FROM php:8.2-apache

# â”€â”€ System deps â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
RUN apt-get update && apt-get install -y \
    libpng-dev libjpeg-dev libfreetype6-dev \
    libzip-dev libonig-dev libxml2-dev \
    zip unzip curl git \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo pdo_mysql mbstring zip gd xml opcache \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

# â”€â”€ Apache: point DocumentRoot at public/ and enable mod_rewrite â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public

RUN sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/html/public|g' \
        /etc/apache2/sites-available/000-default.conf \
    && sed -i 's|<Directory /var/www/html>|<Directory /var/www/html/public>|g' \
        /etc/apache2/apache2.conf \
    && echo '<Directory /var/www/html/public>\n    AllowOverride All\n    Require all granted\n</Directory>' \
        >> /etc/apache2/apache2.conf \
    && a2enmod rewrite

# â”€â”€ PHP production settings â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
RUN echo "display_errors = Off\n\
error_reporting = E_ALL\n\
log_errors = On\n\
error_log = /var/log/php_errors.log\n\
upload_max_filesize = 20M\n\
post_max_size = 22M\n\
memory_limit = 256M\n\
max_execution_time = 60\n\
opcache.enable = 1\n\
opcache.memory_consumption = 128\n\
opcache.validate_timestamps = 0" > /usr/local/etc/php/conf.d/production.ini

# â”€â”€ Copy application files â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
WORKDIR /var/www/html
COPY . .

# â”€â”€ Create required directories & set permissions â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
RUN mkdir -p storage/logs storage/uploads \
    && chown -R www-data:www-data /var/www/html \
    && chmod -R 775 storage

EXPOSE 80

```

## File: modules\admin\Controllers\AuditLogController.php

```php
<?php
class AuditLogController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $modules = $db->fetchAll("SELECT DISTINCT module FROM audit_log ORDER BY module");
        $users = $db->fetchAll("SELECT DISTINCT u.id, u.full_name_en FROM audit_log al JOIN auth_users u ON al.user_id = u.id ORDER BY u.full_name_en");

        $this->view('audit-log/index', [
            'title' => lang('admin.audit_log'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/audit-log'], ['label' => lang('admin.audit_log')]],
            'modules' => $modules,
            'users' => $users,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $search = trim($request->post('search')['value'] ?? '');

        $where = "1=1";
        $params = [];

        if ($search !== '') {
            $where .= " AND (al.description LIKE ? OR al.entity_type LIKE ? OR u.full_name_en LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s]);
        }

        $userFilter = $request->post('user_filter');
        if ($userFilter) { $where .= " AND al.user_id = ?"; $params[] = (int)$userFilter; }
        $moduleFilter = $request->post('module_filter');
        if ($moduleFilter) { $where .= " AND al.module = ?"; $params[] = $moduleFilter; }
        $actionFilter = $request->post('action_filter');
        if ($actionFilter) { $where .= " AND al.action_type = ?"; $params[] = $actionFilter; }
        $dateFrom = $request->post('date_from');
        if ($dateFrom) { $where .= " AND al.created_at >= ?"; $params[] = $dateFrom . ' 00:00:00'; }
        $dateTo = $request->post('date_to');
        if ($dateTo) { $where .= " AND al.created_at <= ?"; $params[] = $dateTo . ' 23:59:59'; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM audit_log al");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM audit_log al LEFT JOIN auth_users u ON al.user_id = u.id WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT al.*, u.full_name_en as user_name FROM audit_log al
             LEFT JOIN auth_users u ON al.user_id = u.id WHERE {$where}
             ORDER BY al.created_at DESC LIMIT {$length} OFFSET {$start}", $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actionBadge = match($r['action_type']) {
                'create' => '<span class="badge bg-success">Create</span>',
                'update' => '<span class="badge bg-primary">Update</span>',
                'delete' => '<span class="badge bg-danger">Delete</span>',
                'login' => '<span class="badge bg-info">Login</span>',
                'logout' => '<span class="badge bg-secondary">Logout</span>',
                default => '<span class="badge bg-dark">' . e($r['action_type']) . '</span>',
            };
            $data[] = [
                'id' => $r['id'],
                'datetime' => format_datetime($r['created_at']),
                'user' => e($r['user_name'] ?? 'System'),
                'action' => $actionBadge,
                'module' => e($r['module']),
                'entity' => e($r['entity_type']) . ($r['entity_id'] ? ' #' . $r['entity_id'] : ''),
                'description' => e(str_limit($r['description'], 80)),
                'ip' => e($r['ip_address'] ?? ''),
                'actions' => '<a href="/admin/audit-log/' . $r['id'] . '" class="btn btn-sm btn-outline-info"><i class="fas fa-eye"></i></a>',
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $entry = $db->fetchOne("SELECT al.*, u.full_name_en as user_name FROM audit_log al LEFT JOIN auth_users u ON al.user_id = u.id WHERE al.id = ?", [$id]);
        if (!$entry) { $this->abort(404); return; }

        $this->view('audit-log/show', [
            'title' => 'Audit Entry #' . $id,
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/audit-log'], ['label' => lang('admin.audit_log'), 'url' => '/admin/audit-log'], ['label' => '#' . $id]],
            'entry' => $entry,
        ]);
    }
}
```

## File: modules\admin\Controllers\DataQualityController.php

```php
<?php
class DataQualityController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $stats = [
            'total_rules' => (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_rules WHERE is_active = 1"),
            'open_issues' => (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues WHERE status = 'open'"),
            'critical_issues' => (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues WHERE status = 'open' AND severity = 'critical'"),
            'last_scan' => $db->fetchOne("SELECT * FROM data_quality_scan_runs ORDER BY started_at DESC LIMIT 1"),
        ];
        $this->view('data-quality/index', [
            'title' => lang('admin.data_quality'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/data-quality'], ['label' => lang('admin.data_quality')]],
            'stats' => $stats,
        ]);
    }

    public function rulesDatatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll("SELECT * FROM data_quality_rules ORDER BY entity_type, rule_code");
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'code' => e($r['rule_code']),
                'name' => e($r['rule_name_en']),
                'entity' => e($r['entity_type']),
                'type' => e($r['rule_type']),
                'severity' => status_badge($r['severity']),
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Disabled</span>',
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function issuesDatatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);

        $where = "1=1";
        $params = [];
        $statusFilter = $request->post('status_filter', 'open');
        if ($statusFilter) { $where .= " AND dqi.status = ?"; $params[] = $statusFilter; }
        $severityFilter = $request->post('severity_filter');
        if ($severityFilter) { $where .= " AND dqi.severity = ?"; $params[] = $severityFilter; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues dqi WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT dqi.*, dqr.rule_name_en, dqr.rule_code FROM data_quality_issues dqi JOIN data_quality_rules dqr ON dqi.rule_id = dqr.id WHERE {$where} ORDER BY dqi.detected_at DESC LIMIT {$length} OFFSET {$start}", $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '<div class="btn-group btn-group-sm">';
            if ($r['status'] === 'open') {
                $actions .= '<button class="btn btn-outline-success btn-issue-action" data-id="' . $r['id'] . '" data-action="fixed"><i class="fas fa-check"></i></button>';
                $actions .= '<button class="btn btn-outline-warning btn-issue-action" data-id="' . $r['id'] . '" data-action="acknowledged"><i class="fas fa-eye"></i></button>';
                $actions .= '<button class="btn btn-outline-secondary btn-issue-action" data-id="' . $r['id'] . '" data-action="ignored"><i class="fas fa-times"></i></button>';
            }
            $actions .= '</div>';
            $data[] = [
                'rule' => e($r['rule_name_en']),
                'entity' => e($r['entity_type']) . ' #' . $r['entity_id'],
                'field' => e($r['field_name'] ?? '-'),
                'issue' => e(str_limit($r['issue_description'], 60)),
                'severity' => status_badge($r['severity']),
                'status' => status_badge($r['status']),
                'detected' => format_datetime($r['detected_at']),
                'actions' => $actions,
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function issueAction(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $action = $request->post('action');
        $validActions = ['fixed', 'acknowledged', 'ignored', 'false_positive'];
        if (!in_array($action, $validActions)) { $this->json(['success' => false, 'message' => 'Invalid action'], 400); return; }

        $data = ['status' => $action];
        if ($action === 'fixed') { $data['fixed_at'] = date('Y-m-d H:i:s'); $data['fixed_by'] = Auth::id(); $data['fix_method'] = 'manual'; }
        $this->db()->update('data_quality_issues', $data, 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Issue updated']);
    }

    public function scan(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $scanId = $db->insert('data_quality_scan_runs', [
            'scan_type' => 'full',
            'started_at' => date('Y-m-d H:i:s'),
            'status' => 'running',
            'triggered_by' => Auth::id(),
        ]);

        $rules = $db->fetchAll("SELECT * FROM data_quality_rules WHERE is_active = 1");
        $issuesFound = 0;
        $rulesChecked = count($rules);
        $recordsScanned = 0;

        foreach ($rules as $rule) {
            if ($rule['entity_type'] === 'member' && $rule['rule_type'] === 'required') {
                $field = $rule['field_name'];
                if ($field) {
                    $missing = $db->fetchAll(
                        "SELECT id FROM members WHERE is_deleted = 0 AND status = 'active' AND ({$field} IS NULL OR {$field} = '')"
                    );
                    $recordsScanned += (int)$db->fetchColumn("SELECT COUNT(*) FROM members WHERE is_deleted = 0 AND status = 'active'");
                    foreach ($missing as $m) {
                        $exists = $db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues WHERE rule_id = ? AND entity_type = 'member' AND entity_id = ? AND status = 'open'", [$rule['id'], $m['id']]);
                        if (!$exists) {
                            $db->insert('data_quality_issues', [
                                'rule_id' => $rule['id'],
                                'entity_type' => 'member',
                                'entity_id' => $m['id'],
                                'field_name' => $field,
                                'issue_description' => $rule['rule_name_en'] . ' - Missing ' . $field,
                                'severity' => $rule['severity'],
                                'status' => 'open',
                                'detected_at' => date('Y-m-d H:i:s'),
                            ]);
                            $issuesFound++;
                        }
                    }
                }
            }
        }

        $db->update('data_quality_scan_runs', [
            'completed_at' => date('Y-m-d H:i:s'),
            'rules_checked' => $rulesChecked,
            'records_scanned' => $recordsScanned,
            'issues_found' => $issuesFound,
            'status' => 'completed',
        ], 'id = ?', [$scanId]);

        $this->audit('execute', 'data_quality_scan_runs', $scanId, "Data quality scan: {$issuesFound} issues found");
        $this->json(['success' => true, 'message' => "Scan complete. {$issuesFound} issues found."]);
    }

    public function storeRule(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = $this->db()->insert('data_quality_rules', [
            'rule_code' => $request->post('rule_code'),
            'rule_name_en' => $request->post('rule_name_en'),
            'entity_type' => $request->post('entity_type'),
            'field_name' => $request->post('field_name'),
            'rule_type' => $request->post('rule_type'),
            'severity' => $request->post('severity', 'warning'),
            'is_active' => 1,
        ]);
        $this->json(['success' => true, 'message' => 'Rule created']);
    }

    public function updateRule(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->update('data_quality_rules', [
            'rule_name_en' => $request->post('rule_name_en'),
            'severity' => $request->post('severity'),
            'is_active' => $request->post('is_active', 1) ? 1 : 0,
        ], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Rule updated']);
    }
}
```

## File: modules\admin\Controllers\DepartmentController.php

```php
<?php
class DepartmentController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $departments = $db->fetchAll("SELECT d.*, p.name_en as parent_name, u.full_name_en as manager_name FROM departments d LEFT JOIN departments p ON d.parent_id = p.id LEFT JOIN auth_users u ON d.manager_user_id = u.id ORDER BY d.sort_order, d.name_en");
        $users = $db->fetchAll("SELECT id, full_name_en FROM auth_users WHERE is_active = 1 AND is_deleted = 0 ORDER BY full_name_en");

        $this->view('departments/index', [
            'title' => lang('admin.departments'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/departments'],
                ['label' => lang('admin.departments')]
            ],
            'departments' => $departments,
            'users' => $users,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $rows = $db->fetchAll(
            "SELECT d.*, p.name_en as parent_name, u.full_name_en as manager_name,
                    (SELECT COUNT(*) FROM staff s WHERE s.department_id = d.id AND s.is_deleted = 0) as staff_count
             FROM departments d
             LEFT JOIN departments p ON d.parent_id = p.id
             LEFT JOIN auth_users u ON d.manager_user_id = u.id
             ORDER BY d.sort_order, d.name_en"
        );
        $data = [];
        foreach ($rows as $r) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<button class="btn btn-outline-primary btn-edit-dept" data-id="' . $r['id'] . '" data-code="' . e($r['department_code']) . '" data-name-en="' . e($r['name_en']) . '" data-name-ar="' . e($r['name_ar']) . '" data-parent="' . ($r['parent_id'] ?? '') . '" data-manager="' . ($r['manager_user_id'] ?? '') . '" data-active="' . $r['is_active'] . '"><i class="fas fa-edit"></i></button>';
            $actions .= '<button class="btn btn-outline-danger btn-delete-dept" data-id="' . $r['id'] . '"><i class="fas fa-trash"></i></button>';
            $actions .= '</div>';
            $data[] = [
                'code' => e($r['department_code']),
                'name' => e($r['name_en']),
                'name_ar' => e($r['name_ar']),
                'parent' => e($r['parent_name'] ?? '-'),
                'manager' => e($r['manager_name'] ?? '-'),
                'staff_count' => $r['staff_count'],
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions' => $actions,
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => count($rows), 'recordsFiltered' => count($rows), 'data' => $data]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'department_code' => 'required|string|max:20',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $id = $this->db()->insert('departments', [
            'department_code' => $request->post('department_code'),
            'name_en' => $request->post('name_en'),
            'name_ar' => $request->post('name_ar'),
            'parent_id' => $request->post('parent_id') ?: null,
            'manager_user_id' => $request->post('manager_user_id') ?: null,
            'is_active' => $request->post('is_active', 1) ? 1 : 0,
        ]);
        $this->audit('create', 'departments', $id, 'Created department: ' . $request->post('name_en'));
        $this->json(['success' => true, 'message' => 'Department created']);
    }

    public function update(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->update('departments', [
            'name_en' => $request->post('name_en'),
            'name_ar' => $request->post('name_ar'),
            'parent_id' => $request->post('parent_id') ?: null,
            'manager_user_id' => $request->post('manager_user_id') ?: null,
            'is_active' => $request->post('is_active', 1) ? 1 : 0,
        ], 'id = ?', [$id]);
        $this->audit('update', 'departments', $id, 'Updated department');
        $this->json(['success' => true, 'message' => 'Department updated']);
    }

    public function destroy(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $childCount = $this->db()->fetchColumn("SELECT COUNT(*) FROM departments WHERE parent_id = ?", [$id]);
        if ($childCount > 0) { $this->json(['success' => false, 'message' => 'Cannot delete department with children'], 400); return; }
        $this->db()->delete('departments', 'id = ?', [$id]);
        $this->audit('delete', 'departments', $id, 'Deleted department');
        $this->json(['success' => true, 'message' => 'Department deleted']);
    }
}
```

## File: modules\admin\Controllers\ExportLogController.php

```php
<?php
class ExportLogController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('export-log/index', [
            'title' => lang('admin.export_log'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/export-log'], ['label' => lang('admin.export_log')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM export_log");
        $rows = $db->fetchAll(
            "SELECT el.*, u.full_name_en as user_name FROM export_log el LEFT JOIN auth_users u ON el.exported_by = u.id ORDER BY el.exported_at DESC LIMIT {$length} OFFSET {$start}"
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'type' => e($r['export_type']),
                'source' => e($r['export_source']),
                'format' => '<span class="badge bg-info">' . e(strtoupper($r['export_format'])) . '</span>',
                'rows' => number_format($r['rows_exported'] ?? 0),
                'user' => e($r['user_name'] ?? '-'),
                'date' => format_datetime($r['exported_at']),
                'ip' => e($r['ip_address'] ?? ''),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }
}
```

## File: modules\admin\Controllers\FormNumberController.php

```php
<?php
class FormNumberController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('form-numbers/index', [
            'title' => lang('admin.form_numbers'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/form-numbers'], ['label' => lang('admin.form_numbers')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $rows = $this->db()->fetchAll("SELECT p.*, (SELECT COUNT(*) FROM sys_form_number_issued fi WHERE fi.pool_id = p.id) as issued_count FROM sys_form_number_pools p ORDER BY p.id");
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'name' => e($r['pool_name']),
                'type' => e($r['form_type']),
                'range' => number_format($r['starting_number']) . ' - ' . number_format($r['ending_number']),
                'current' => number_format($r['current_number']),
                'issued' => $r['issued_count'],
                'remaining' => number_format($r['ending_number'] - $r['current_number']),
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions' => '<button class="btn btn-sm btn-outline-primary btn-issue-form" data-id="' . $r['id'] . '"><i class="fas fa-plus"></i> Issue</button>',
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->insert('sys_form_number_pools', [
            'pool_name' => $request->post('pool_name'),
            'pool_name_ar' => $request->post('pool_name_ar', ''),
            'form_type' => $request->post('form_type'),
            'prefix' => $request->post('prefix'),
            'starting_number' => (int)$request->post('starting_number', 1),
            'ending_number' => (int)$request->post('ending_number'),
            'current_number' => (int)$request->post('starting_number', 1) - 1,
            'is_active' => 1,
            'created_by' => Auth::id(),
        ]);
        $this->json(['success' => true, 'message' => 'Pool created']);
    }

    public function issue(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $pool = $db->fetchOne("SELECT * FROM sys_form_number_pools WHERE id = ? AND is_active = 1", [$id]);
        if (!$pool) { $this->json(['success' => false, 'message' => 'Pool not found'], 404); return; }
        $next = $pool['current_number'] + 1;
        if ($next > $pool['ending_number']) { $this->json(['success' => false, 'message' => 'Pool exhausted'], 400); return; }

        $formNumber = ($pool['prefix'] ?? '') . str_pad($next, 6, '0', STR_PAD_LEFT);
        $db->update('sys_form_number_pools', ['current_number' => $next, 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $db->insert('sys_form_number_issued', [
            'pool_id' => $id,
            'form_number' => $formNumber,
            'issued_to_user' => Auth::id(),
            'issued_at' => date('Y-m-d H:i:s'),
            'status' => 'issued',
        ]);
        $this->json(['success' => true, 'message' => 'Issued: ' . $formNumber, 'form_number' => $formNumber]);
    }
}
```

## File: modules\admin\Controllers\ImportController.php

```php
<?php
class ImportController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('import/index', [
            'title' => lang('admin.import'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/import'], ['label' => lang('admin.import')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll(
            "SELECT ib.*, u.full_name_en as created_by_name FROM import_batches ib LEFT JOIN auth_users u ON ib.created_by = u.id ORDER BY ib.created_at DESC LIMIT 100"
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'batch' => e($r['batch_number']),
                'type' => e($r['import_type']),
                'file' => e($r['source_file_name']),
                'total' => $r['total_rows'],
                'success' => $r['success_rows'],
                'errors' => $r['error_rows'],
                'status' => status_badge($r['status']),
                'by' => e($r['created_by_name'] ?? '-'),
                'date' => format_datetime($r['created_at']),
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function upload(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $file = $request->file('import_file');
        if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
            $this->json(['success' => false, 'message' => 'No file uploaded'], 400);
            return;
        }

        $upload = new FileUpload($file);
        $upload->setAllowedTypes(['csv', 'xls', 'xlsx'])->setMaxSize(10 * 1024 * 1024)->setDestination('uploads/imports/');
        if (!$upload->validate()) {
            $this->json(['success' => false, 'message' => implode(', ', $upload->errors())], 400);
            return;
        }

        $path = $upload->save();
        if (!$path) {
            $this->json(['success' => false, 'message' => 'Failed to save file'], 500);
            return;
        }

        $importType = $request->post('import_type', 'members');
        $batchNumber = 'IMP-' . date('Ymd') . '-' . str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT);

        $rows = [];
        $fullPath = PUBLIC_PATH . '/' . $path;
        if (($handle = fopen($fullPath, 'r')) !== false) {
            $header = fgetcsv($handle);
            $rowNum = 0;
            while (($row = fgetcsv($handle)) !== false) {
                $rowNum++;
                $mapped = [];
                foreach ($header as $i => $col) {
                    $mapped[trim($col)] = $row[$i] ?? '';
                }
                $rows[] = ['row_number' => $rowNum, 'data' => $mapped];
            }
            fclose($handle);
        }

        $batchId = $this->db()->insert('import_batches', [
            'batch_number' => $batchNumber,
            'import_type' => $importType,
            'source_file_name' => $upload->originalName(),
            'source_file_path' => $path,
            'source_file_size' => $file['size'],
            'total_rows' => count($rows),
            'status' => 'uploaded',
            'created_by' => Auth::id(),
        ]);

        foreach ($rows as $r) {
            $this->db()->insert('import_batch_rows', [
                'batch_id' => $batchId,
                'row_number' => $r['row_number'],
                'raw_data' => json_encode($r['data'], JSON_UNESCAPED_UNICODE),
                'status' => 'pending',
            ]);
        }

        $this->audit('create', 'import_batches', $batchId, "Uploaded import: {$batchNumber}");
        $this->json(['success' => true, 'message' => count($rows) . ' rows uploaded', 'batch_id' => $batchId]);
    }

    public function validate(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $batch = $db->fetchOne("SELECT * FROM import_batches WHERE id = ?", [$id]);
        if (!$batch) { $this->json(['success' => false, 'message' => 'Batch not found'], 404); return; }

        $rows = $db->fetchAll("SELECT * FROM import_batch_rows WHERE batch_id = ? ORDER BY row_number", [$id]);
        $validCount = 0;
        $invalidCount = 0;

        foreach ($rows as $row) {
            $data = json_decode($row['raw_data'], true);
            $errors = [];

            if ($batch['import_type'] === 'members') {
                if (empty($data['first_name_en'] ?? '')) $errors[] = 'first_name_en is required';
                if (empty($data['last_name_en'] ?? '')) $errors[] = 'last_name_en is required';
                if (empty($data['gender'] ?? '')) $errors[] = 'gender is required';
            }

            $status = empty($errors) ? 'valid' : 'invalid';
            if ($status === 'valid') $validCount++;
            else $invalidCount++;

            $db->update('import_batch_rows', [
                'status' => $status,
                'mapped_data' => json_encode($data, JSON_UNESCAPED_UNICODE),
                'error_messages' => empty($errors) ? null : json_encode($errors),
            ], 'id = ?', [$row['id']]);
        }

        $db->update('import_batches', ['status' => 'validated'], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => "Validated: {$validCount} valid, {$invalidCount} invalid"]);
    }

    public function execute(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $batch = $db->fetchOne("SELECT * FROM import_batches WHERE id = ?", [$id]);
        if (!$batch) { $this->json(['success' => false, 'message' => 'Batch not found'], 404); return; }

        $db->update('import_batches', ['status' => 'importing', 'started_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);

        $rows = $db->fetchAll("SELECT * FROM import_batch_rows WHERE batch_id = ? AND status = 'valid' ORDER BY row_number", [$id]);
        $success = 0;
        $errors = 0;

        foreach ($rows as $row) {
            $data = json_decode($row['mapped_data'] ?: $row['raw_data'], true);
            try {
                if ($batch['import_type'] === 'members') {
                    $memberId = $db->insert('members', [
                        'membership_number' => SequenceGenerator::next('member_number') ?? ('IMP-' . $row['row_number']),
                        'membership_type_id' => (int)($data['membership_type_id'] ?? 1),
                        'first_name_en' => $data['first_name_en'] ?? '',
                        'last_name_en' => $data['last_name_en'] ?? '',
                        'full_name_en' => trim(($data['first_name_en'] ?? '') . ' ' . ($data['last_name_en'] ?? '')),
                        'first_name_ar' => $data['first_name_ar'] ?? null,
                        'last_name_ar' => $data['last_name_ar'] ?? null,
                        'gender' => $data['gender'] ?? 'male',
                        'national_id' => $data['national_id'] ?? null,
                        'primary_phone' => $data['primary_phone'] ?? null,
                        'primary_email' => $data['primary_email'] ?? null,
                        'status' => 'active',
                        'join_date' => date('Y-m-d'),
                        'activation_date' => date('Y-m-d'),
                        'created_by' => Auth::id(),
                    ]);
                    $db->update('import_batch_rows', ['status' => 'imported', 'result_entity_type' => 'member', 'result_entity_id' => $memberId, 'processed_at' => date('Y-m-d H:i:s')], 'id = ?', [$row['id']]);
                    $success++;
                }
            } catch (\Exception $e) {
                $db->update('import_batch_rows', ['status' => 'error', 'error_messages' => json_encode([$e->getMessage()]), 'processed_at' => date('Y-m-d H:i:s')], 'id = ?', [$row['id']]);
                $errors++;
            }
        }

        $db->update('import_batches', [
            'status' => 'completed',
            'completed_at' => date('Y-m-d H:i:s'),
            'processed_rows' => $success + $errors,
            'success_rows' => $success,
            'error_rows' => $errors,
        ], 'id = ?', [$id]);

        $this->audit('execute', 'import_batches', $id, "Import executed: {$success} success, {$errors} errors");
        $this->json(['success' => true, 'message' => "Import complete: {$success} succeeded, {$errors} failed"]);
    }
}
```

## File: modules\admin\Controllers\JobController.php

```php
<?php
class JobController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('jobs/index', [
            'title' => lang('admin.system_jobs'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/jobs'], ['label' => lang('admin.system_jobs')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll("SELECT * FROM system_jobs ORDER BY id");
        $data = [];
        foreach ($rows as $r) {
            $statusBadge = match($r['last_run_status']) {
                'success' => '<span class="badge bg-success">Success</span>',
                'failed' => '<span class="badge bg-danger">Failed</span>',
                'partial' => '<span class="badge bg-warning">Partial</span>',
                default => '<span class="badge bg-secondary">Never Run</span>',
            };
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<button class="btn btn-outline-' . ($r['is_active'] ? 'warning' : 'success') . ' btn-toggle-job" data-id="' . $r['id'] . '">' . ($r['is_active'] ? '<i class="fas fa-pause"></i>' : '<i class="fas fa-play"></i>') . '</button>';
            $actions .= '<button class="btn btn-outline-primary btn-run-job" data-id="' . $r['id'] . '" title="Run Now"><i class="fas fa-bolt"></i></button>';
            $actions .= '<a href="/admin/jobs/' . $r['id'] . '/runs" class="btn btn-outline-info" title="History"><i class="fas fa-history"></i></a>';
            $actions .= '</div>';
            $data[] = [
                'code' => e($r['job_code']),
                'name' => e($r['job_name']),
                'type' => e($r['job_type']),
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Disabled</span>',
                'last_run' => $r['last_run_at'] ? format_datetime($r['last_run_at']) : '-',
                'last_status' => $statusBadge,
                'runs' => number_format($r['run_count']),
                'actions' => $actions,
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function toggle(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $job = $db->fetchOne("SELECT id, is_active, job_code FROM system_jobs WHERE id = ?", [$id]);
        if (!$job) { $this->json(['success' => false, 'message' => 'Job not found'], 404); return; }
        $db->update('system_jobs', ['is_active' => $job['is_active'] ? 0 : 1, 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'system_jobs', $id, ($job['is_active'] ? 'Disabled' : 'Enabled') . ' job: ' . $job['job_code']);
        $this->json(['success' => true, 'message' => 'Job status toggled']);
    }

    public function run(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $job = $db->fetchOne("SELECT * FROM system_jobs WHERE id = ?", [$id]);
        if (!$job) { $this->json(['success' => false, 'message' => 'Job not found'], 404); return; }

        $runId = $db->insert('system_job_runs', [
            'job_id' => $id,
            'started_at' => date('Y-m-d H:i:s'),
            'status' => 'running',
            'triggered_by' => 'manual',
            'triggered_by_user' => Auth::id(),
        ]);

        try {
            $procName = 'sp_' . str_replace('-', '_', $job['job_code']);
            if ($job['job_code'] === 'daily_maintenance') {
                $db->execute("CALL sp_daily_maintenance()");
            } elseif ($job['job_code'] === 'overdue_invoices') {
                $db->execute("CALL sp_mark_overdue_invoices()");
            } elseif ($job['job_code'] === 'overdue_installments') {
                $db->execute("CALL sp_mark_overdue_installments()");
            } elseif ($job['job_code'] === 'expire_cards') {
                $db->execute("CALL sp_expire_cards()");
            } elseif ($job['job_code'] === 'expire_carnets') {
                $db->execute("CALL sp_expire_carnets()");
            } elseif ($job['job_code'] === 'expire_guest_passes') {
                $db->execute("CALL sp_expire_guest_passes()");
            } elseif ($job['job_code'] === 'end_freezes') {
                $db->execute("CALL sp_end_expired_freezes()");
            } elseif ($job['job_code'] === 'end_suspensions') {
                $db->execute("CALL sp_end_expired_suspensions()");
            } elseif ($job['job_code'] === 'process_notifications') {
                $db->execute("CALL sp_process_notification_queue()");
            } elseif ($job['job_code'] === 'clean_sessions') {
                $db->execute("CALL sp_clean_expired_sessions()");
            } elseif ($job['job_code'] === 'expire_offers') {
                $db->execute("CALL sp_expire_special_offers()");
            } elseif ($job['job_code'] === 'waitlist_offers_expire') {
                $db->execute("CALL sp_expire_waiting_list_offers()");
            } else {
                $db->update('system_job_runs', ['status' => 'failed', 'completed_at' => date('Y-m-d H:i:s'), 'error_message' => 'No handler defined'], 'id = ?', [$runId]);
                $this->json(['success' => false, 'message' => 'No handler for this job']);
                return;
            }

            $db->update('system_job_runs', ['status' => 'success', 'completed_at' => date('Y-m-d H:i:s')], 'id = ?', [$runId]);
            $db->update('system_jobs', ['last_run_at' => date('Y-m-d H:i:s'), 'last_run_status' => 'success', 'run_count' => $job['run_count'] + 1], 'id = ?', [$id]);
            $this->audit('execute', 'system_jobs', $id, 'Manually executed job: ' . $job['job_code']);
            $this->json(['success' => true, 'message' => 'Job executed successfully']);
        } catch (\Exception $e) {
            $db->update('system_job_runs', ['status' => 'failed', 'completed_at' => date('Y-m-d H:i:s'), 'error_message' => $e->getMessage()], 'id = ?', [$runId]);
            $db->update('system_jobs', ['last_run_at' => date('Y-m-d H:i:s'), 'last_run_status' => 'failed', 'fail_count' => $job['fail_count'] + 1], 'id = ?', [$id]);
            $this->json(['success' => false, 'message' => 'Job failed: ' . $e->getMessage()]);
        }
    }

    public function runs(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $job = $db->fetchOne("SELECT * FROM system_jobs WHERE id = ?", [$id]);
        if (!$job) { $this->abort(404); return; }
        $runs = $db->fetchAll("SELECT * FROM system_job_runs WHERE job_id = ? ORDER BY started_at DESC LIMIT 100", [$id]);
        $this->view('jobs/runs', [
            'title' => 'Job Runs: ' . $job['job_name'],
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/jobs'], ['label' => lang('admin.system_jobs'), 'url' => '/admin/jobs'], ['label' => 'Runs']],
            'job' => $job, 'runs' => $runs,
        ]);
    }
}
```

## File: modules\admin\Controllers\LoginLogController.php

```php
<?php
class LoginLogController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('login-log/index', [
            'title' => lang('admin.login_log'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/login-log'], ['label' => lang('admin.login_log')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $search = trim($request->post('search')['value'] ?? '');

        $where = "1=1";
        $params = [];
        if ($search !== '') {
            $where .= " AND (ll.username LIKE ? OR ll.ip_address LIKE ?)";
            $s = "%{$search}%";
            $params = [$s, $s];
        }
        $actionFilter = $request->post('action_filter');
        if ($actionFilter) { $where .= " AND ll.action = ?"; $params[] = $actionFilter; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM login_log ll");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM login_log ll WHERE {$where}", $params);
        $rows = $db->fetchAll("SELECT ll.* FROM login_log ll WHERE {$where} ORDER BY ll.created_at DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = [];
        foreach ($rows as $r) {
            $actionBadge = match($r['action']) {
                'login_success' => '<span class="badge bg-success">Login</span>',
                'login_failed' => '<span class="badge bg-danger">Failed</span>',
                'logout' => '<span class="badge bg-info">Logout</span>',
                default => '<span class="badge bg-secondary">' . e($r['action']) . '</span>',
            };
            $data[] = [
                'datetime' => format_datetime($r['created_at']),
                'username' => e($r['username']),
                'action' => $actionBadge,
                'ip' => e($r['ip_address']),
                'reason' => e($r['failure_reason'] ?? '-'),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }
}
```

## File: modules\admin\Controllers\LookupController.php

```php
<?php
class LookupController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $categories = $db->fetchAll("SELECT * FROM sys_lookup_categories ORDER BY category_name_en");
        $this->view('lookups/index', [
            'title' => lang('admin.lookups'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/lookups'], ['label' => lang('admin.lookups')]],
            'categories' => $categories,
        ]);
    }

    public function categoriesDatatable(Request $request): void
    {
        $rows = $this->db()->fetchAll("SELECT lc.*, (SELECT COUNT(*) FROM sys_lookup_values lv WHERE lv.category_id = lc.id) as value_count FROM sys_lookup_categories lc ORDER BY lc.category_name_en");
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'],
                'code' => e($r['category_code']),
                'name_en' => e($r['category_name_en']),
                'name_ar' => e($r['category_name_ar']),
                'values' => $r['value_count'],
                'system' => $r['is_system'] ? '<span class="badge bg-info">System</span>' : '',
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function valuesDatatable(Request $request): void
    {
        $categoryId = (int)$request->post('category_id');
        $rows = $this->db()->fetchAll("SELECT * FROM sys_lookup_values WHERE category_id = ? ORDER BY sort_order, value_en", [$categoryId]);
        $data = [];
        foreach ($rows as $r) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<button class="btn btn-outline-primary btn-edit-value" data-id="' . $r['id'] . '" data-code="' . e($r['lookup_code']) . '" data-en="' . e($r['value_en']) . '" data-ar="' . e($r['value_ar']) . '" data-sort="' . $r['sort_order'] . '" data-active="' . $r['is_active'] . '"><i class="fas fa-edit"></i></button>';
            $actions .= '<button class="btn btn-outline-danger btn-delete-value" data-id="' . $r['id'] . '"><i class="fas fa-trash"></i></button>';
            $actions .= '</div>';
            $data[] = [
                'code' => e($r['lookup_code']),
                'value_en' => e($r['value_en']),
                'value_ar' => e($r['value_ar']),
                'sort' => $r['sort_order'],
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions' => $actions,
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function storeValue(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->insert('sys_lookup_values', [
            'category_id' => (int)$request->post('category_id'),
            'lookup_code' => $request->post('lookup_code'),
            'value_en' => $request->post('value_en'),
            'value_ar' => $request->post('value_ar'),
            'sort_order' => (int)$request->post('sort_order', 0),
            'is_active' => 1,
        ]);
        $this->json(['success' => true, 'message' => 'Value created']);
    }

    public function updateValue(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->update('sys_lookup_values', [
            'value_en' => $request->post('value_en'),
            'value_ar' => $request->post('value_ar'),
            'sort_order' => (int)$request->post('sort_order', 0),
            'is_active' => $request->post('is_active', 1) ? 1 : 0,
        ], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Value updated']);
    }

    public function deleteValue(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->delete('sys_lookup_values', 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Value deleted']);
    }
}
```

## File: modules\admin\Controllers\RecycleBinController.php

```php
<?php
class RecycleBinController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('recycle-bin/index', [
            'title' => lang('admin.recycle_bin'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/recycle-bin'], ['label' => lang('admin.recycle_bin')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM recycle_bin WHERE restored = 0 AND permanently_deleted = 0");
        $rows = $db->fetchAll(
            "SELECT rb.*, u.full_name_en as deleted_by_name FROM recycle_bin rb LEFT JOIN auth_users u ON rb.deleted_by = u.id WHERE rb.restored = 0 AND rb.permanently_deleted = 0 ORDER BY rb.deleted_at DESC LIMIT {$length} OFFSET {$start}"
        );
        $data = [];
        foreach ($rows as $r) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<button class="btn btn-outline-success btn-restore" data-id="' . $r['id'] . '" title="Restore"><i class="fas fa-undo"></i></button>';
            $actions .= '<button class="btn btn-outline-danger btn-purge" data-id="' . $r['id'] . '" title="Permanently Delete"><i class="fas fa-trash-alt"></i></button>';
            $actions .= '</div>';
            $data[] = [
                'type' => e($r['entity_type']),
                'entity_id' => $r['entity_id'],
                'deleted_by' => e($r['deleted_by_name'] ?? '-'),
                'deleted_at' => format_datetime($r['deleted_at']),
                'deadline' => $r['restore_deadline'] ? format_datetime($r['restore_deadline']) : '-',
                'actions' => $actions,
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function restore(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $item = $db->fetchOne("SELECT * FROM recycle_bin WHERE id = ? AND restored = 0 AND permanently_deleted = 0", [$id]);
        if (!$item) { $this->json(['success' => false, 'message' => 'Item not found'], 404); return; }

        $entityData = json_decode($item['entity_data'], true);
        if ($entityData && !empty($item['entity_type'])) {
            try {
                $db->update($item['entity_type'], ['is_deleted' => 0, 'deleted_at' => null, 'deleted_by' => null], 'id = ?', [$item['entity_id']]);
            } catch (\Exception $e) {
                Logger::warning('Could not restore entity directly: ' . $e->getMessage());
            }
        }

        $db->update('recycle_bin', ['restored' => 1, 'restored_by' => Auth::id(), 'restored_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'recycle_bin', $id, 'Restored entity: ' . $item['entity_type'] . '#' . $item['entity_id']);
        $this->json(['success' => true, 'message' => 'Item restored']);
    }

    public function purge(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->update('recycle_bin', ['permanently_deleted' => 1, 'permanently_deleted_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('delete', 'recycle_bin', $id, 'Permanently deleted recycled item');
        $this->json(['success' => true, 'message' => 'Item permanently deleted']);
    }
}
```

## File: modules\admin\Controllers\RoleController.php

```php
<?php
class RoleController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('roles/index', [
            'title' => lang('admin.roles'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/roles'],
                ['label' => lang('admin.roles')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $search = trim($request->post('search')['value'] ?? '');

        $where = "1=1";
        $params = [];
        if ($search !== '') {
            $where .= " AND (r.role_code LIKE ? OR r.role_name_en LIKE ? OR r.role_name_ar LIKE ?)";
            $s = "%{$search}%";
            $params = [$s, $s, $s];
        }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM auth_roles r");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM auth_roles r WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT r.*, (SELECT COUNT(*) FROM auth_user_roles ur WHERE ur.role_id = r.id AND ur.is_active = 1) as user_count,
                    (SELECT COUNT(*) FROM auth_role_permissions rp WHERE rp.role_id = r.id) as perm_count
             FROM auth_roles r WHERE {$where} ORDER BY r.sort_order ASC, r.id ASC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<a href="/admin/roles/' . $row['id'] . '/edit" class="btn btn-outline-primary"><i class="fas fa-edit"></i></a>';
            if (!$row['is_system']) {
                $actions .= '<button class="btn btn-outline-danger btn-delete-role" data-id="' . $row['id'] . '"><i class="fas fa-trash"></i></button>';
            }
            $actions .= '</div>';

            $data[] = [
                'id'         => $row['id'],
                'code'       => e($row['role_code']),
                'name'       => e($row['role_name_en']),
                'name_ar'    => e($row['role_name_ar']),
                'users'      => $row['user_count'],
                'permissions' => $row['perm_count'],
                'system'     => $row['is_system'] ? '<span class="badge bg-info">System</span>' : '',
                'active'     => $row['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions'    => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $permissions = $db->fetchAll("SELECT * FROM auth_permissions WHERE is_active = 1 ORDER BY module, category, permission_code");
        $modules = [];
        foreach ($permissions as $p) {
            $modules[$p['module']][] = $p;
        }

        $this->view('roles/form', [
            'title' => lang('admin.create_role'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/roles'],
                ['label' => lang('admin.roles'), 'url' => '/admin/roles'],
                ['label' => lang('admin.create_role')]
            ],
            'role' => null,
            'modules' => $modules,
            'rolePermissions' => [],
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'role_code'    => 'required|string|max:50',
            'role_name_en' => 'required|string|max:200',
            'role_name_ar' => 'required|string|max:200',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $db = $this->db();
        $exists = $db->fetchColumn("SELECT COUNT(*) FROM auth_roles WHERE role_code = ?", [$request->post('role_code')]);
        if ($exists) { $this->json(['success' => false, 'errors' => ['role_code' => ['Role code already exists']]], 422); return; }

        $db->beginTransaction();
        try {
            $roleId = $db->insert('auth_roles', [
                'role_code'      => $request->post('role_code'),
                'role_name_en'   => $request->post('role_name_en'),
                'role_name_ar'   => $request->post('role_name_ar'),
                'description_en' => $request->post('description_en'),
                'description_ar' => $request->post('description_ar'),
                'is_system'      => 0,
                'is_active'      => $request->post('is_active', 1) ? 1 : 0,
                'created_by'     => Auth::id(),
            ]);

            $permIds = $request->post('permissions', []);
            if (is_array($permIds)) {
                foreach ($permIds as $pid) {
                    $db->insert('auth_role_permissions', [
                        'role_id'       => $roleId,
                        'permission_id' => (int)$pid,
                        'granted_by'    => Auth::id(),
                    ]);
                }
            }

            $this->audit('create', 'auth_roles', $roleId, 'Created role: ' . $request->post('role_code'));
            $db->commit();
            $this->json(['success' => true, 'message' => 'Role created', 'redirect' => '/admin/roles']);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Role creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create role'], 500);
        }
    }

    public function edit(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $role = $db->fetchOne("SELECT * FROM auth_roles WHERE id = ?", [$id]);
        if (!$role) { $this->abort(404); return; }

        $permissions = $db->fetchAll("SELECT * FROM auth_permissions WHERE is_active = 1 ORDER BY module, category, permission_code");
        $modules = [];
        foreach ($permissions as $p) { $modules[$p['module']][] = $p; }

        $rolePerms = $db->fetchAll("SELECT permission_id FROM auth_role_permissions WHERE role_id = ?", [$id]);
        $rolePermIds = array_column($rolePerms, 'permission_id');

        $this->view('roles/form', [
            'title' => lang('admin.edit_role') . ': ' . $role['role_name_en'],
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/roles'],
                ['label' => lang('admin.roles'), 'url' => '/admin/roles'],
                ['label' => lang('admin.edit_role')]
            ],
            'role' => $role,
            'modules' => $modules,
            'rolePermissions' => $rolePermIds,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'role_name_en' => 'required|string|max:200',
            'role_name_ar' => 'required|string|max:200',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $db = $this->db();
        $db->beginTransaction();
        try {
            $db->update('auth_roles', [
                'role_name_en'   => $request->post('role_name_en'),
                'role_name_ar'   => $request->post('role_name_ar'),
                'description_en' => $request->post('description_en'),
                'description_ar' => $request->post('description_ar'),
                'is_active'      => $request->post('is_active', 1) ? 1 : 0,
            ], 'id = ?', [$id]);

            $db->delete('auth_role_permissions', 'role_id = ?', [$id]);
            $permIds = $request->post('permissions', []);
            if (is_array($permIds)) {
                foreach ($permIds as $pid) {
                    $db->insert('auth_role_permissions', [
                        'role_id'       => $id,
                        'permission_id' => (int)$pid,
                        'granted_by'    => Auth::id(),
                    ]);
                }
            }

            $this->audit('update', 'auth_roles', $id, 'Updated role permissions');
            $db->commit();
            $this->json(['success' => true, 'message' => 'Role updated', 'redirect' => '/admin/roles']);
        } catch (\Exception $e) {
            $db->rollBack();
            $this->json(['success' => false, 'message' => 'Failed to update role'], 500);
        }
    }

    public function destroy(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $role = $db->fetchOne("SELECT * FROM auth_roles WHERE id = ?", [$id]);
        if (!$role || $role['is_system']) { $this->json(['success' => false, 'message' => 'Cannot delete system role'], 400); return; }
        $userCount = $db->fetchColumn("SELECT COUNT(*) FROM auth_user_roles WHERE role_id = ? AND is_active = 1", [$id]);
        if ($userCount > 0) { $this->json(['success' => false, 'message' => 'Role has assigned users. Remove them first.'], 400); return; }

        $db->delete('auth_role_permissions', 'role_id = ?', [$id]);
        $db->delete('auth_roles', 'id = ?', [$id]);
        $this->audit('delete', 'auth_roles', $id, 'Deleted role: ' . $role['role_code']);
        $this->json(['success' => true, 'message' => 'Role deleted']);
    }
}
```

## File: modules\admin\Controllers\SequenceController.php

```php
<?php
class SequenceController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('sequences/index', [
            'title' => lang('admin.sequences'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/sequences'], ['label' => lang('admin.sequences')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $rows = $this->db()->fetchAll("SELECT * FROM sys_sequences ORDER BY sequence_name");
        $data = [];
        foreach ($rows as $r) {
            $sample = ($r['prefix'] ?? '') . str_pad($r['current_value'] + 1, $r['pad_length'], '0', STR_PAD_LEFT) . ($r['suffix'] ?? '');
            $data[] = [
                'name' => e($r['sequence_name']),
                'prefix' => e($r['prefix'] ?? '-'),
                'current' => number_format($r['current_value']),
                'next_sample' => e($sample),
                'reset' => e($r['reset_period']),
                'active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions' => '<div class="btn-group btn-group-sm"><button class="btn btn-outline-danger btn-reset-seq" data-id="' . $r['id'] . '" data-name="' . e($r['sequence_name']) . '"><i class="fas fa-undo"></i> Reset</button></div>',
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function update(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->update('sys_sequences', [
            'prefix' => $request->post('prefix'),
            'suffix' => $request->post('suffix'),
            'pad_length' => (int)$request->post('pad_length', 6),
            'is_active' => $request->post('is_active', 1) ? 1 : 0,
            'updated_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Sequence updated']);
    }

    public function reset(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $seq = $db->fetchOne("SELECT * FROM sys_sequences WHERE id = ?", [$id]);
        if (!$seq) { $this->json(['success' => false, 'message' => 'Sequence not found'], 404); return; }
        $db->update('sys_sequences', ['current_value' => $seq['min_value'] - 1, 'last_reset_date' => date('Y-m-d'), 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'sys_sequences', $id, 'Reset sequence: ' . $seq['sequence_name']);
        $this->json(['success' => true, 'message' => 'Sequence reset to ' . $seq['min_value']]);
    }
}
```

## File: modules\admin\Controllers\SettingsController.php

```php
<?php
class SettingsController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $settings = $db->fetchAll("SELECT * FROM sys_config ORDER BY config_group, id");
        $groups = [];
        foreach ($settings as $s) {
            $groups[$s['config_group']][] = $s;
        }

        $this->view('settings/index', [
            'title' => lang('admin.settings'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/settings'],
                ['label' => lang('admin.settings')]
            ],
            'groups' => $groups,
        ]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $db = $this->db();
        $configs = $request->post('config', []);
        $updated = 0;

        foreach ($configs as $key => $value) {
            $existing = $db->fetchOne("SELECT id, config_value, is_readonly FROM sys_config WHERE config_key = ?", [$key]);
            if ($existing && !$existing['is_readonly'] && $existing['config_value'] !== $value) {
                $db->update('sys_config', [
                    'config_value' => $value,
                    'updated_by'   => Auth::id(),
                    'updated_at'   => date('Y-m-d H:i:s'),
                ], 'config_key = ?', [$key]);
                $updated++;
            }
        }

        $this->audit('update', 'sys_config', null, "Updated {$updated} settings");
        Config::clearCache();
        $this->json(['success' => true, 'message' => "Updated {$updated} settings"]);
    }
}
```

## File: modules\admin\Controllers\StaffController.php

```php
<?php
class StaffController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('staff/index', [
            'title' => lang('admin.staff'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/staff'], ['label' => lang('admin.staff')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $search = trim($request->post('search')['value'] ?? '');

        $where = "s.is_deleted = 0";
        $params = [];
        if ($search !== '') {
            $where .= " AND (s.staff_number LIKE ? OR s.first_name_en LIKE ? OR s.last_name_en LIKE ? OR s.phone LIKE ?)";
            $w = "%{$search}%";
            $params = [$w, $w, $w, $w];
        }

        $statusFilter = $request->post('status_filter');
        if ($statusFilter) { $where .= " AND s.status = ?"; $params[] = $statusFilter; }
        $deptFilter = $request->post('dept_filter');
        if ($deptFilter) { $where .= " AND s.department_id = ?"; $params[] = (int)$deptFilter; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM staff s WHERE s.is_deleted = 0");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM staff s WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT s.*, d.name_en as dept_name, sp.title_en as position_name
             FROM staff s LEFT JOIN departments d ON s.department_id = d.id
             LEFT JOIN staff_positions sp ON s.position_id = sp.id
             WHERE {$where} ORDER BY s.id DESC LIMIT {$length} OFFSET {$start}", $params
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'],
                'staff_number' => e($r['staff_number']),
                'name' => e($r['first_name_en'] . ' ' . $r['last_name_en']),
                'department' => e($r['dept_name'] ?? '-'),
                'position' => e($r['position_name'] ?? '-'),
                'phone' => e($r['phone'] ?? '-'),
                'status' => status_badge($r['status']),
                'hire_date' => format_date($r['hire_date']),
                'actions' => '<div class="btn-group btn-group-sm"><a href="/admin/staff/' . $r['id'] . '" class="btn btn-outline-info"><i class="fas fa-eye"></i></a><a href="/admin/staff/' . $r['id'] . '/edit" class="btn btn-outline-primary"><i class="fas fa-edit"></i></a></div>',
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $this->view('staff/form', [
            'title' => 'Create Staff',
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/staff'], ['label' => lang('admin.staff'), 'url' => '/admin/staff'], ['label' => 'Create']],
            'staff' => null,
            'departments' => $db->fetchAll("SELECT id, name_en FROM departments WHERE is_active = 1 ORDER BY name_en"),
            'positions' => $db->fetchAll("SELECT id, title_en FROM staff_positions WHERE is_active = 1 ORDER BY title_en"),
            'users' => $db->fetchAll("SELECT id, full_name_en, username FROM auth_users WHERE is_active = 1 AND is_deleted = 0 ORDER BY full_name_en"),
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'staff_number'  => 'required|string|max:30',
            'first_name_en' => 'required|string|max:100',
            'last_name_en'  => 'required|string|max:100',
            'hire_date'     => 'required|date',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $id = $this->db()->insert('staff', [
            'staff_number'  => $request->post('staff_number'),
            'first_name_en' => $request->post('first_name_en'),
            'last_name_en'  => $request->post('last_name_en'),
            'first_name_ar' => $request->post('first_name_ar'),
            'last_name_ar'  => $request->post('last_name_ar'),
            'department_id' => $request->post('department_id') ?: null,
            'position_id'   => $request->post('position_id') ?: null,
            'user_id'       => $request->post('user_id') ?: null,
            'phone'         => $request->post('phone'),
            'email'         => $request->post('email'),
            'gender'        => $request->post('gender') ?: null,
            'hire_date'     => $request->post('hire_date'),
            'status'        => 'active',
            'created_by'    => Auth::id(),
        ]);
        $this->audit('create', 'staff', $id, 'Created staff member');
        $this->json(['success' => true, 'message' => 'Staff member created', 'redirect' => '/admin/staff']);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $staff = $db->fetchOne(
            "SELECT s.*, d.name_en as dept_name, sp.title_en as position_name, u.username as linked_username
             FROM staff s LEFT JOIN departments d ON s.department_id = d.id
             LEFT JOIN staff_positions sp ON s.position_id = sp.id LEFT JOIN auth_users u ON s.user_id = u.id
             WHERE s.id = ? AND s.is_deleted = 0", [$id]
        );
        if (!$staff) { $this->abort(404); return; }

        $tasks = $db->fetchAll("SELECT * FROM staff_tasks WHERE assigned_to = ? ORDER BY created_at DESC LIMIT 20", [$id]);
        $shifts = $db->fetchAll("SELECT * FROM staff_shifts WHERE staff_id = ? ORDER BY shift_date DESC LIMIT 20", [$id]);
        $reviews = $db->fetchAll("SELECT spr.*, u.full_name_en as reviewer_name FROM staff_performance_reviews spr LEFT JOIN auth_users u ON spr.reviewer_id = u.id WHERE spr.staff_id = ? ORDER BY spr.review_period_end DESC", [$id]);

        $this->view('staff/view', [
            'title' => $staff['first_name_en'] . ' ' . $staff['last_name_en'],
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/staff'], ['label' => lang('admin.staff'), 'url' => '/admin/staff'], ['label' => 'View']],
            'staff' => $staff, 'tasks' => $tasks, 'shifts' => $shifts, 'reviews' => $reviews,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $staff = $db->fetchOne("SELECT * FROM staff WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$staff) { $this->abort(404); return; }

        $this->view('staff/form', [
            'title' => 'Edit Staff',
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/staff'], ['label' => lang('admin.staff'), 'url' => '/admin/staff'], ['label' => 'Edit']],
            'staff' => $staff,
            'departments' => $db->fetchAll("SELECT id, name_en FROM departments WHERE is_active = 1 ORDER BY name_en"),
            'positions' => $db->fetchAll("SELECT id, title_en FROM staff_positions WHERE is_active = 1 ORDER BY title_en"),
            'users' => $db->fetchAll("SELECT id, full_name_en, username FROM auth_users WHERE is_active = 1 AND is_deleted = 0 ORDER BY full_name_en"),
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $this->db()->update('staff', [
            'first_name_en' => $request->post('first_name_en'),
            'last_name_en'  => $request->post('last_name_en'),
            'first_name_ar' => $request->post('first_name_ar'),
            'last_name_ar'  => $request->post('last_name_ar'),
            'department_id' => $request->post('department_id') ?: null,
            'position_id'   => $request->post('position_id') ?: null,
            'user_id'       => $request->post('user_id') ?: null,
            'phone'         => $request->post('phone'),
            'email'         => $request->post('email'),
            'status'        => $request->post('status', 'active'),
            'updated_at'    => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);
        $this->audit('update', 'staff', $id, 'Updated staff member');
        $this->json(['success' => true, 'message' => 'Staff updated', 'redirect' => '/admin/staff']);
    }
}
```

## File: modules\admin\Controllers\SystemHealthController.php

```php
<?php
class SystemHealthController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();

        $health = [
            'php_version' => phpversion(),
            'mysql_version' => $db->fetchColumn("SELECT VERSION()"),
            'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown',
            'memory_usage' => round(memory_get_usage(true) / 1024 / 1024, 2) . ' MB',
            'memory_limit' => ini_get('memory_limit'),
            'max_upload' => ini_get('upload_max_filesize'),
            'max_post' => ini_get('post_max_size'),
            'timezone' => date_default_timezone_get(),
            'disk_free' => round(disk_free_space(ROOT_PATH) / 1024 / 1024 / 1024, 2) . ' GB',
            'disk_total' => round(disk_total_space(ROOT_PATH) / 1024 / 1024 / 1024, 2) . ' GB',
            'active_sessions' => (int)$db->fetchColumn("SELECT COUNT(*) FROM auth_sessions WHERE is_active = 1"),
            'active_users' => (int)$db->fetchColumn("SELECT COUNT(*) FROM auth_users WHERE is_active = 1 AND is_deleted = 0"),
            'total_members' => (int)$db->fetchColumn("SELECT COUNT(*) FROM members WHERE is_deleted = 0"),
            'active_members' => (int)$db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0"),
            'errors_today' => (int)$db->fetchColumn("SELECT COUNT(*) FROM system_error_log WHERE DATE(created_at) = CURDATE()"),
            'open_dq_issues' => (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues WHERE status = 'open'"),
            'pending_notifications' => (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE status = 'queued'"),
        ];

        $recentErrors = $db->fetchAll("SELECT * FROM system_error_log ORDER BY created_at DESC LIMIT 20");

        $storagePath = ROOT_PATH . '/storage';
        $uploadsPath = ROOT_PATH . '/public/uploads';
        $storageSize = 0;
        if (is_dir($storagePath)) {
            $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($storagePath, \RecursiveDirectoryIterator::SKIP_DOTS));
            foreach ($iterator as $file) { $storageSize += $file->getSize(); }
        }
        $health['storage_used'] = round($storageSize / 1024 / 1024, 2) . ' MB';

        $this->view('system-health/index', [
            'title' => lang('admin.system_health'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/system-health'], ['label' => lang('admin.system_health')]],
            'health' => $health,
            'recentErrors' => $recentErrors,
        ]);
    }
}
```

## File: modules\admin\Controllers\TranslationController.php

```php
<?php
class TranslationController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $languages = $db->fetchAll("SELECT * FROM sys_languages WHERE is_active = 1 ORDER BY sort_order");
        $groups = $db->fetchAll("SELECT DISTINCT translation_group FROM sys_translations ORDER BY translation_group");
        $this->view('translations/index', [
            'title' => lang('admin.translations'),
            'breadcrumbs' => [['label' => lang('admin.administration'), 'url' => '/admin/translations'], ['label' => lang('admin.translations')]],
            'languages' => $languages,
            'groups' => $groups,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $langId = (int)$request->post('language_id', 1);
        $group = $request->post('group_filter', '');
        $search = trim($request->post('search')['value'] ?? '');

        $where = "t.language_id = ?";
        $params = [$langId];
        if ($group) { $where .= " AND t.translation_group = ?"; $params[] = $group; }
        if ($search !== '') { $where .= " AND (t.translation_key LIKE ? OR t.translation_value LIKE ?)"; $s = "%{$search}%"; $params[] = $s; $params[] = $s; }

        $rows = $db->fetchAll("SELECT * FROM sys_translations t WHERE {$where} ORDER BY t.translation_group, t.translation_key LIMIT 200", $params);
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'],
                'group' => e($r['translation_group']),
                'key' => e($r['translation_key']),
                'value' => '<input type="text" class="form-control form-control-sm translation-input" data-id="' . $r['id'] . '" value="' . e($r['translation_value']) . '">',
            ];
        }
        $this->json(['draw' => (int)$request->post('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function save(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $translations = $request->post('translations', []);
        $db = $this->db();
        $count = 0;
        foreach ($translations as $id => $value) {
            $db->update('sys_translations', ['translation_value' => $value, 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [(int)$id]);
            $count++;
        }
        $this->json(['success' => true, 'message' => "{$count} translations saved"]);
    }
}
```

## File: modules\admin\Controllers\UserController.php

```php
<?php
class UserController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('users/index', [
            'title' => lang('admin.users'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/users'],
                ['label' => lang('admin.users')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->post('draw', 1);
        $start = (int)$request->post('start', 0);
        $length = (int)$request->post('length', 25);
        $searchValue = trim($request->post('search')['value'] ?? '');
        $orderCol = (int)($request->post('order')[0]['column'] ?? 0);
        $orderDir = ($request->post('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

        $columns = ['u.id', 'u.username', 'u.email', 'u.full_name_en', 'r.role_name_en', 'u.is_active', 'u.last_login_at'];
        $orderColumn = $columns[$orderCol] ?? 'u.id';

        $baseWhere = "u.is_deleted = 0";
        $params = [];

        if ($searchValue !== '') {
            $baseWhere .= " AND (u.username LIKE ? OR u.email LIKE ? OR u.full_name_en LIKE ? OR u.full_name_ar LIKE ?)";
            $s = "%{$searchValue}%";
            $params = [$s, $s, $s, $s];
        }

        $totalRecords = (int)$db->fetchColumn("SELECT COUNT(*) FROM auth_users u WHERE u.is_deleted = 0");
        $filteredRecords = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM auth_users u LEFT JOIN auth_user_roles ur ON u.id = ur.user_id AND ur.is_active = 1 LEFT JOIN auth_roles r ON ur.role_id = r.id WHERE {$baseWhere}",
            $params
        );

        $rows = $db->fetchAll(
            "SELECT u.id, u.username, u.email, u.full_name_en, u.full_name_ar, u.is_active, u.is_locked, u.last_login_at, u.last_login_ip, u.created_at,
                    GROUP_CONCAT(r.role_name_en SEPARATOR ', ') as roles_en, GROUP_CONCAT(r.role_name_ar SEPARATOR ', ') as roles_ar
             FROM auth_users u
             LEFT JOIN auth_user_roles ur ON u.id = ur.user_id AND ur.is_active = 1
             LEFT JOIN auth_roles r ON ur.role_id = r.id AND r.is_active = 1
             WHERE {$baseWhere}
             GROUP BY u.id
             ORDER BY {$orderColumn} {$orderDir}
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $statusHtml = $row['is_active']
                ? '<span class="badge bg-success">Active</span>'
                : '<span class="badge bg-secondary">Inactive</span>';
            if ($row['is_locked']) {
                $statusHtml .= ' <span class="badge bg-danger">Locked</span>';
            }

            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<a href="/admin/users/' . $row['id'] . '/edit" class="btn btn-outline-primary" title="Edit"><i class="fas fa-edit"></i></a>';
            $actions .= '<a href="/admin/users/' . $row['id'] . '/sessions" class="btn btn-outline-info" title="Sessions"><i class="fas fa-desktop"></i></a>';
            if ($row['is_locked']) {
                $actions .= '<button class="btn btn-outline-success btn-unlock" data-id="' . $row['id'] . '" title="Unlock"><i class="fas fa-lock-open"></i></button>';
            }
            $actions .= '</div>';

            $data[] = [
                'id'         => $row['id'],
                'username'   => e($row['username']),
                'full_name'  => e($row['full_name_en']),
                'email'      => e($row['email']),
                'roles'      => e($row['roles_en'] ?? '-'),
                'status'     => $statusHtml,
                'last_login' => $row['last_login_at'] ? format_datetime($row['last_login_at']) : '-',
                'actions'    => $actions,
            ];
        }

        $this->json([
            'draw' => $draw,
            'recordsTotal' => $totalRecords,
            'recordsFiltered' => $filteredRecords,
            'data' => $data,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $roles = $db->fetchAll("SELECT id, role_code, role_name_en, role_name_ar FROM auth_roles WHERE is_active = 1 ORDER BY role_name_en");

        $this->view('users/form', [
            'title' => lang('admin.create_user'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/users'],
                ['label' => lang('admin.users'), 'url' => '/admin/users'],
                ['label' => lang('admin.create_user')]
            ],
            'user' => null,
            'roles' => $roles,
            'userRoles' => [],
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'username'      => 'required|string|min:3|max:100',
            'email'         => 'required|email|max:191',
            'password'      => 'required|string|min:8|max:100',
            'full_name_en'  => 'required|string|max:300',
            'full_name_ar'  => 'nullable|string|max:300',
            'phone'         => 'nullable|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = $this->db();

        $exists = $db->fetchColumn("SELECT COUNT(*) FROM auth_users WHERE username = ? AND is_deleted = 0", [$request->post('username')]);
        if ($exists > 0) {
            $this->json(['success' => false, 'errors' => ['username' => ['Username already exists']]], 422);
            return;
        }

        $emailExists = $db->fetchColumn("SELECT COUNT(*) FROM auth_users WHERE email = ? AND is_deleted = 0", [$request->post('email')]);
        if ($emailExists > 0) {
            $this->json(['success' => false, 'errors' => ['email' => ['Email already exists']]], 422);
            return;
        }

        $db->beginTransaction();
        try {
            $userId = $db->insert('auth_users', [
                'username'       => $request->post('username'),
                'email'          => $request->post('email'),
                'password_hash'  => password_hash($request->post('password'), PASSWORD_BCRYPT, ['cost' => 12]),
                'full_name_en'   => $request->post('full_name_en'),
                'full_name_ar'   => $request->post('full_name_ar', ''),
                'phone'          => $request->post('phone'),
                'is_active'      => $request->post('is_active', 1) ? 1 : 0,
                'must_change_password' => $request->post('must_change_password', 0) ? 1 : 0,
                'created_by'     => Auth::id(),
            ]);

            $roleIds = $request->post('roles', []);
            if (is_array($roleIds)) {
                foreach ($roleIds as $roleId) {
                    $db->insert('auth_user_roles', [
                        'user_id'     => $userId,
                        'role_id'     => (int)$roleId,
                        'assigned_by' => Auth::id(),
                        'is_active'   => 1,
                    ]);
                }
            }

            $this->audit('create', 'auth_users', $userId, "Created user: " . $request->post('username'));
            $db->commit();
            $this->json(['success' => true, 'message' => 'User created successfully', 'redirect' => '/admin/users']);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('User creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create user'], 500);
        }
    }

    public function edit(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $user = $db->fetchOne("SELECT * FROM auth_users WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$user) {
            $this->abort(404, 'User not found');
            return;
        }

        $roles = $db->fetchAll("SELECT id, role_code, role_name_en, role_name_ar FROM auth_roles WHERE is_active = 1 ORDER BY role_name_en");
        $userRoles = $db->fetchAll("SELECT role_id FROM auth_user_roles WHERE user_id = ? AND is_active = 1", [$id]);
        $userRoleIds = array_column($userRoles, 'role_id');

        $this->view('users/form', [
            'title' => lang('admin.edit_user'),
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/users'],
                ['label' => lang('admin.users'), 'url' => '/admin/users'],
                ['label' => lang('admin.edit_user')]
            ],
            'user' => $user,
            'roles' => $roles,
            'userRoles' => $userRoleIds,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'username'      => 'required|string|min:3|max:100',
            'email'         => 'required|email|max:191',
            'full_name_en'  => 'required|string|max:300',
            'full_name_ar'  => 'nullable|string|max:300',
            'phone'         => 'nullable|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = $this->db();

        $exists = $db->fetchColumn("SELECT COUNT(*) FROM auth_users WHERE username = ? AND id != ? AND is_deleted = 0", [$request->post('username'), $id]);
        if ($exists > 0) {
            $this->json(['success' => false, 'errors' => ['username' => ['Username already taken']]], 422);
            return;
        }

        $db->beginTransaction();
        try {
            $data = [
                'username'       => $request->post('username'),
                'email'          => $request->post('email'),
                'full_name_en'   => $request->post('full_name_en'),
                'full_name_ar'   => $request->post('full_name_ar', ''),
                'phone'          => $request->post('phone'),
                'is_active'      => $request->post('is_active', 1) ? 1 : 0,
                'must_change_password' => $request->post('must_change_password', 0) ? 1 : 0,
                'updated_at'     => date('Y-m-d H:i:s'),
            ];

            $password = $request->post('password');
            if (!empty($password) && mb_strlen($password) >= 8) {
                $data['password_hash'] = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
                $data['password_changed_at'] = date('Y-m-d H:i:s');
            }

            $db->update('auth_users', $data, 'id = ?', [$id]);

            $db->delete('auth_user_roles', 'user_id = ? AND is_active = 1', [$id]);
            $roleIds = $request->post('roles', []);
            if (is_array($roleIds)) {
                foreach ($roleIds as $roleId) {
                    $db->insert('auth_user_roles', [
                        'user_id'     => $id,
                        'role_id'     => (int)$roleId,
                        'assigned_by' => Auth::id(),
                        'is_active'   => 1,
                    ]);
                }
            }

            $this->audit('update', 'auth_users', $id, "Updated user: " . $request->post('username'));
            $db->commit();
            $this->json(['success' => true, 'message' => 'User updated successfully', 'redirect' => '/admin/users']);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('User update failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to update user'], 500);
        }
    }

    public function toggle(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }
        $db = $this->db();
        $user = $db->fetchOne("SELECT id, is_active, username FROM auth_users WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$user) {
            $this->json(['success' => false, 'message' => 'User not found'], 404);
            return;
        }
        $newStatus = $user['is_active'] ? 0 : 1;
        $db->update('auth_users', ['is_active' => $newStatus, 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'auth_users', $id, ($newStatus ? 'Activated' : 'Deactivated') . " user: " . $user['username']);
        $this->json(['success' => true, 'message' => 'User status updated']);
    }

    public function lock(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $db->update('auth_users', [
            'is_locked' => 1,
            'locked_at' => date('Y-m-d H:i:s'),
            'lock_reason' => $request->post('reason', 'Locked by admin'),
        ], 'id = ?', [$id]);
        $this->audit('update', 'auth_users', $id, 'Locked user account');
        $this->json(['success' => true, 'message' => 'User locked']);
    }

    public function unlock(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $db = $this->db();
        $db->update('auth_users', [
            'is_locked' => 0,
            'locked_at' => null,
            'lock_reason' => null,
            'failed_login_attempts' => 0,
        ], 'id = ?', [$id]);
        $this->audit('update', 'auth_users', $id, 'Unlocked user account');
        $this->json(['success' => true, 'message' => 'User unlocked']);
    }

    public function forcePassword(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $this->db()->update('auth_users', ['must_change_password' => 1, 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'auth_users', $id, 'Forced password change');
        $this->json(['success' => true, 'message' => 'Password change will be required on next login']);
    }

    public function sessions(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $user = $db->fetchOne("SELECT id, username, full_name_en FROM auth_users WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$user) { $this->abort(404); return; }

        $sessions = $db->fetchAll(
            "SELECT * FROM auth_sessions WHERE user_id = ? ORDER BY started_at DESC LIMIT 50",
            [$id]
        );

        $this->view('users/sessions', [
            'title' => 'Sessions: ' . $user['full_name_en'],
            'breadcrumbs' => [
                ['label' => lang('admin.administration'), 'url' => '/admin/users'],
                ['label' => lang('admin.users'), 'url' => '/admin/users'],
                ['label' => 'Sessions']
            ],
            'targetUser' => $user,
            'sessions' => $sessions,
        ]);
    }
}
```

## File: modules\admin\css\admin.css

```css
/* Admin Module Styles */
.list-group-item.category-item.active {
    background-color: var(--primary);
    color: #fff;
    border-color: var(--primary);
}
.list-group-item.category-item:hover:not(.active) {
    background-color: #f8f9fa;
}
.translation-input {
    border: 1px solid #dee2e6;
    transition: border-color 0.15s;
}
.translation-input:focus {
    border-color: var(--primary-light);
    box-shadow: 0 0 0 0.15rem rgba(44, 82, 130, 0.15);
}
.perm-checkbox + label {
    cursor: pointer;
}
.perm-checkbox + label:hover {
    color: var(--primary);
}
```

## File: modules\admin\js\admin.js

```js
/**
 * Admin Module JavaScript
 */
(function() {
    'use strict';

    // Auto-initialize DataTables on admin pages
    document.addEventListener('DOMContentLoaded', function() {
        // Initialize any Select2 dropdowns
        if ($.fn.select2) {
            $('.admin-select2').select2({ theme: 'bootstrap-5', width: '100%' });
        }
    });
})();
```

## File: modules\admin\lang\ar.php

```php
<?php
return [
    'admin.administration' => 'Ø§Ù„Ø¥Ø¯Ø§Ø±Ø©',
    'admin.users' => 'Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù…ÙŠÙ†',
    'admin.create_user' => 'Ø¥Ù†Ø´Ø§Ø¡ Ù…Ø³ØªØ®Ø¯Ù…',
    'admin.edit_user' => 'ØªØ¹Ø¯ÙŠÙ„ Ù…Ø³ØªØ®Ø¯Ù…',
    'admin.roles' => 'Ø§Ù„Ø£Ø¯ÙˆØ§Ø±',
    'admin.create_role' => 'Ø¥Ù†Ø´Ø§Ø¡ Ø¯ÙˆØ±',
    'admin.edit_role' => 'ØªØ¹Ø¯ÙŠÙ„ Ø¯ÙˆØ±',
    'admin.settings' => 'Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Ø§Ù„Ù†Ø¸Ø§Ù…',
    'admin.departments' => 'Ø§Ù„Ø£Ù‚Ø³Ø§Ù…',
    'admin.staff' => 'Ø§Ù„Ù…ÙˆØ¸ÙÙŠÙ†',
    'admin.system_jobs' => 'Ù…Ù‡Ø§Ù… Ø§Ù„Ù†Ø¸Ø§Ù…',
    'admin.audit_log' => 'Ø³Ø¬Ù„ Ø§Ù„ØªØ¯Ù‚ÙŠÙ‚',
    'admin.login_log' => 'Ø³Ø¬Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'admin.data_quality' => 'Ø¬ÙˆØ¯Ø© Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª',
    'admin.import' => 'Ø§Ø³ØªÙŠØ±Ø§Ø¯ Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª',
    'admin.export_log' => 'Ø³Ø¬Ù„ Ø§Ù„ØªØµØ¯ÙŠØ±',
    'admin.recycle_bin' => 'Ø³Ù„Ø© Ø§Ù„Ù…Ø­Ø°ÙˆÙØ§Øª',
    'admin.lookups' => 'Ø§Ù„Ù‚ÙˆØ§Ø¦Ù… Ø§Ù„Ù…Ø±Ø¬Ø¹ÙŠØ©',
    'admin.translations' => 'Ø§Ù„ØªØ±Ø¬Ù…Ø§Øª',
    'admin.form_numbers' => 'Ø£Ø±Ù‚Ø§Ù… Ø§Ù„Ù†Ù…Ø§Ø°Ø¬',
    'admin.sequences' => 'Ø§Ù„ØªØ³Ù„Ø³Ù„Ø§Øª',
    'admin.system_health' => 'ØµØ­Ø© Ø§Ù„Ù†Ø¸Ø§Ù…',
];
```

## File: modules\admin\lang\en.php

```php
<?php
return [
    'admin.administration' => 'Administration',
    'admin.users' => 'Users',
    'admin.create_user' => 'Create User',
    'admin.edit_user' => 'Edit User',
    'admin.roles' => 'Roles',
    'admin.create_role' => 'Create Role',
    'admin.edit_role' => 'Edit Role',
    'admin.settings' => 'System Settings',
    'admin.departments' => 'Departments',
    'admin.staff' => 'Staff',
    'admin.system_jobs' => 'System Jobs',
    'admin.audit_log' => 'Audit Log',
    'admin.login_log' => 'Login Log',
    'admin.data_quality' => 'Data Quality',
    'admin.import' => 'Import Data',
    'admin.export_log' => 'Export Log',
    'admin.recycle_bin' => 'Recycle Bin',
    'admin.lookups' => 'Lookup Values',
    'admin.translations' => 'Translations',
    'admin.form_numbers' => 'Form Numbers',
    'admin.sequences' => 'Sequences',
    'admin.system_health' => 'System Health',
];
```

## File: modules\admin\module.json

```json
{
    "name": "admin",
    "version": "1.0.0",
    "description_en": "System Administration Module",
    "description_ar": "ÙˆØ­Ø¯Ø© Ø¥Ø¯Ø§Ø±Ø© Ø§Ù„Ù†Ø¸Ø§Ù…",
    "sort_order": 99,
    "routes_file": "routes.php",
    "css_files": ["css/admin.css"],
    "js_files": ["js/admin.js"],
    "menu": [
        {
            "label_en": "Administration",
            "label_ar": "Ø§Ù„Ø¥Ø¯Ø§Ø±Ø©",
            "icon": "fas fa-cogs",
            "sort_order": 90,
            "children": [
                {"label_en": "Users", "label_ar": "Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù…ÙŠÙ†", "route": "/admin/users", "permission": "users.view", "sort_order": 1},
                {"label_en": "Roles", "label_ar": "Ø§Ù„Ø£Ø¯ÙˆØ§Ø±", "route": "/admin/roles", "permission": "roles.manage", "sort_order": 2},
                {"label_en": "Departments", "label_ar": "Ø§Ù„Ø£Ù‚Ø³Ø§Ù…", "route": "/admin/departments", "permission": "staff.view", "sort_order": 3},
                {"label_en": "Staff", "label_ar": "Ø§Ù„Ù…ÙˆØ¸ÙÙŠÙ†", "route": "/admin/staff", "permission": "staff.view", "sort_order": 4},
                {"label_en": "Settings", "label_ar": "Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª", "route": "/admin/settings", "permission": "settings.view", "sort_order": 5},
                {"label_en": "Lookups", "label_ar": "Ø§Ù„Ù‚ÙˆØ§Ø¦Ù… Ø§Ù„Ù…Ø±Ø¬Ø¹ÙŠØ©", "route": "/admin/lookups", "permission": "settings.manage", "sort_order": 6},
                {"label_en": "Translations", "label_ar": "Ø§Ù„ØªØ±Ø¬Ù…Ø§Øª", "route": "/admin/translations", "permission": "settings.manage", "sort_order": 7},
                {"label_en": "System Jobs", "label_ar": "Ù…Ù‡Ø§Ù… Ø§Ù„Ù†Ø¸Ø§Ù…", "route": "/admin/jobs", "permission": "settings.manage", "sort_order": 8},
                {"label_en": "Audit Log", "label_ar": "Ø³Ø¬Ù„ Ø§Ù„ØªØ¯Ù‚ÙŠÙ‚", "route": "/admin/audit-log", "permission": "audit.view", "sort_order": 9},
                {"label_en": "Login Log", "label_ar": "Ø³Ø¬Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„", "route": "/admin/login-log", "permission": "audit.view", "sort_order": 10},
                {"label_en": "Data Quality", "label_ar": "Ø¬ÙˆØ¯Ø© Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª", "route": "/admin/data-quality", "permission": "settings.manage", "sort_order": 11},
                {"label_en": "Import", "label_ar": "Ø§Ø³ØªÙŠØ±Ø§Ø¯ Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª", "route": "/admin/import", "permission": "import.execute", "sort_order": 12},
                {"label_en": "Export Log", "label_ar": "Ø³Ø¬Ù„ Ø§Ù„ØªØµØ¯ÙŠØ±", "route": "/admin/export-log", "permission": "export.execute", "sort_order": 13},
                {"label_en": "Recycle Bin", "label_ar": "Ø³Ù„Ø© Ø§Ù„Ù…Ø­Ø°ÙˆÙØ§Øª", "route": "/admin/recycle-bin", "permission": "settings.manage", "sort_order": 14},
                {"label_en": "Form Numbers", "label_ar": "Ø£Ø±Ù‚Ø§Ù… Ø§Ù„Ù†Ù…Ø§Ø°Ø¬", "route": "/admin/form-numbers", "permission": "settings.manage", "sort_order": 15},
                {"label_en": "Sequences", "label_ar": "Ø§Ù„ØªØ³Ù„Ø³Ù„Ø§Øª", "route": "/admin/sequences", "permission": "settings.manage", "sort_order": 16},
                {"label_en": "System Health", "label_ar": "ØµØ­Ø© Ø§Ù„Ù†Ø¸Ø§Ù…", "route": "/admin/system-health", "permission": "settings.view", "sort_order": 17}
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "label_en": "System Health",
            "label_ar": "ØµØ­Ø© Ø§Ù„Ù†Ø¸Ø§Ù…",
            "partial": "views/widgets/system_health.php",
            "permission": "settings.view",
            "size": "col-md-6",
            "sort_order": 95
        },
        {
            "label_en": "Data Quality",
            "label_ar": "Ø¬ÙˆØ¯Ø© Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª",
            "partial": "views/widgets/data_quality.php",
            "permission": "settings.manage",
            "size": "col-md-6",
            "sort_order": 96
        }
    ]
}
```

## File: modules\admin\routes.php

```php
<?php
return [
    // Users
    ['GET',  '/admin/users',                    'UserController@index',          'users.view'],
    ['POST', '/admin/users/datatable',          'UserController@datatable',      'users.view'],
    ['GET',  '/admin/users/create',             'UserController@create',         'users.manage'],
    ['POST', '/admin/users',                    'UserController@store',          'users.manage'],
    ['GET',  '/admin/users/{id}/edit',          'UserController@edit',           'users.manage'],
    ['POST', '/admin/users/{id}',               'UserController@update',         'users.manage'],
    ['POST', '/admin/users/{id}/toggle',        'UserController@toggle',         'users.manage'],
    ['POST', '/admin/users/{id}/lock',          'UserController@lock',           'users.manage'],
    ['POST', '/admin/users/{id}/unlock',        'UserController@unlock',         'users.manage'],
    ['POST', '/admin/users/{id}/force-password','UserController@forcePassword',  'users.manage'],
    ['GET',  '/admin/users/{id}/sessions',      'UserController@sessions',       'users.manage'],

    // Roles
    ['GET',  '/admin/roles',                    'RoleController@index',          'roles.manage'],
    ['POST', '/admin/roles/datatable',          'RoleController@datatable',      'roles.manage'],
    ['GET',  '/admin/roles/create',             'RoleController@create',         'roles.manage'],
    ['POST', '/admin/roles',                    'RoleController@store',          'roles.manage'],
    ['GET',  '/admin/roles/{id}/edit',          'RoleController@edit',           'roles.manage'],
    ['POST', '/admin/roles/{id}',               'RoleController@update',         'roles.manage'],
    ['POST', '/admin/roles/{id}/delete',        'RoleController@destroy',        'roles.manage'],

    // Settings
    ['GET',  '/admin/settings',                 'SettingsController@index',      'settings.view'],
    ['POST', '/admin/settings',                 'SettingsController@update',     'settings.manage'],

    // Departments
    ['GET',  '/admin/departments',              'DepartmentController@index',    'staff.view'],
    ['POST', '/admin/departments/datatable',    'DepartmentController@datatable','staff.view'],
    ['POST', '/admin/departments/store',        'DepartmentController@store',    'staff.manage'],
    ['POST', '/admin/departments/{id}/update',  'DepartmentController@update',   'staff.manage'],
    ['POST', '/admin/departments/{id}/delete',  'DepartmentController@destroy',  'staff.manage'],

    // Staff
    ['GET',  '/admin/staff',                    'StaffController@index',         'staff.view'],
    ['POST', '/admin/staff/datatable',          'StaffController@datatable',     'staff.view'],
    ['GET',  '/admin/staff/create',             'StaffController@create',        'staff.manage'],
    ['POST', '/admin/staff',                    'StaffController@store',         'staff.manage'],
    ['GET',  '/admin/staff/{id}',               'StaffController@show',          'staff.view'],
    ['GET',  '/admin/staff/{id}/edit',          'StaffController@edit',          'staff.manage'],
    ['POST', '/admin/staff/{id}',               'StaffController@update',        'staff.manage'],

    // Jobs
    ['GET',  '/admin/jobs',                     'JobController@index',           'settings.manage'],
    ['POST', '/admin/jobs/datatable',           'JobController@datatable',       'settings.manage'],
    ['POST', '/admin/jobs/{id}/toggle',         'JobController@toggle',          'settings.manage'],
    ['POST', '/admin/jobs/{id}/run',            'JobController@run',             'settings.manage'],
    ['GET',  '/admin/jobs/{id}/runs',           'JobController@runs',            'settings.manage'],

    // Audit Log
    ['GET',  '/admin/audit-log',                'AuditLogController@index',      'audit.view'],
    ['POST', '/admin/audit-log/datatable',      'AuditLogController@datatable',  'audit.view'],
    ['GET',  '/admin/audit-log/{id}',           'AuditLogController@show',       'audit.view'],

    // Login Log
    ['GET',  '/admin/login-log',                'LoginLogController@index',      'audit.view'],
    ['POST', '/admin/login-log/datatable',      'LoginLogController@datatable',  'audit.view'],

    // Data Quality
    ['GET',  '/admin/data-quality',             'DataQualityController@index',   'settings.manage'],
    ['POST', '/admin/data-quality/rules/datatable','DataQualityController@rulesDatatable','settings.manage'],
    ['POST', '/admin/data-quality/issues/datatable','DataQualityController@issuesDatatable','settings.manage'],
    ['POST', '/admin/data-quality/rules/store', 'DataQualityController@storeRule','settings.manage'],
    ['POST', '/admin/data-quality/rules/{id}/update','DataQualityController@updateRule','settings.manage'],
    ['POST', '/admin/data-quality/issues/{id}/action','DataQualityController@issueAction','settings.manage'],
    ['POST', '/admin/data-quality/scan',        'DataQualityController@scan',    'settings.manage'],

    // Import
    ['GET',  '/admin/import',                   'ImportController@index',        'import.execute'],
    ['POST', '/admin/import/upload',            'ImportController@upload',       'import.execute'],
    ['POST', '/admin/import/{id}/validate',     'ImportController@validate',     'import.execute'],
    ['POST', '/admin/import/{id}/execute',      'ImportController@execute',      'import.execute'],
    ['POST', '/admin/import/datatable',         'ImportController@datatable',    'import.execute'],

    // Export Log
    ['GET',  '/admin/export-log',               'ExportLogController@index',     'export.execute'],
    ['POST', '/admin/export-log/datatable',     'ExportLogController@datatable', 'export.execute'],

    // Recycle Bin
    ['GET',  '/admin/recycle-bin',              'RecycleBinController@index',    'settings.manage'],
    ['POST', '/admin/recycle-bin/datatable',    'RecycleBinController@datatable','settings.manage'],
    ['POST', '/admin/recycle-bin/{id}/restore', 'RecycleBinController@restore',  'settings.manage'],
    ['POST', '/admin/recycle-bin/{id}/purge',   'RecycleBinController@purge',    'settings.manage'],

    // Lookups
    ['GET',  '/admin/lookups',                  'LookupController@index',        'settings.manage'],
    ['POST', '/admin/lookups/categories/datatable','LookupController@categoriesDatatable','settings.manage'],
    ['POST', '/admin/lookups/values/datatable', 'LookupController@valuesDatatable','settings.manage'],
    ['POST', '/admin/lookups/values/store',     'LookupController@storeValue',   'settings.manage'],
    ['POST', '/admin/lookups/values/{id}/update','LookupController@updateValue', 'settings.manage'],
    ['POST', '/admin/lookups/values/{id}/delete','LookupController@deleteValue', 'settings.manage'],

    // Translations
    ['GET',  '/admin/translations',             'TranslationController@index',   'settings.manage'],
    ['POST', '/admin/translations/datatable',   'TranslationController@datatable','settings.manage'],
    ['POST', '/admin/translations/save',        'TranslationController@save',    'settings.manage'],

    // Form Numbers
    ['GET',  '/admin/form-numbers',             'FormNumberController@index',    'settings.manage'],
    ['POST', '/admin/form-numbers/datatable',   'FormNumberController@datatable','settings.manage'],
    ['POST', '/admin/form-numbers/store',       'FormNumberController@store',    'settings.manage'],
    ['POST', '/admin/form-numbers/{id}/issue',  'FormNumberController@issue',    'settings.manage'],

    // Sequences
    ['GET',  '/admin/sequences',                'SequenceController@index',      'settings.manage'],
    ['POST', '/admin/sequences/datatable',      'SequenceController@datatable',  'settings.manage'],
    ['POST', '/admin/sequences/{id}/update',    'SequenceController@update',     'settings.manage'],
    ['POST', '/admin/sequences/{id}/reset',     'SequenceController@reset',      'settings.manage'],

    // System Health
    ['GET',  '/admin/system-health',            'SystemHealthController@index',  'settings.view'],
];
```

## File: modules\admin\views\audit-log\index.php

```php
<h4 class="mb-4"><i class="fas fa-clipboard-list me-2"></i><?= lang('admin.audit_log') ?></h4>
<div class="card mb-3"><div class="card-body">
    <div class="row g-2" data-filter-table="audit-table">
        <div class="col-md-2"><select name="user_filter" class="form-select form-select-sm"><option value="">All Users</option><?php foreach ($users as $u): ?><option value="<?= $u['id'] ?>"><?= e($u['full_name_en']) ?></option><?php endforeach; ?></select></div>
        <div class="col-md-2"><select name="module_filter" class="form-select form-select-sm"><option value="">All Modules</option><?php foreach ($modules as $m): ?><option value="<?= e($m['module']) ?>"><?= e($m['module']) ?></option><?php endforeach; ?></select></div>
        <div class="col-md-2"><select name="action_filter" class="form-select form-select-sm"><option value="">All Actions</option><option value="create">Create</option><option value="update">Update</option><option value="delete">Delete</option><option value="login">Login</option><option value="logout">Logout</option></select></div>
        <div class="col-md-2"><input type="date" name="date_from" class="form-control form-control-sm" placeholder="From"></div>
        <div class="col-md-2"><input type="date" name="date_to" class="form-control form-control-sm" placeholder="To"></div>
        <div class="col-md-2"><button type="button" class="btn btn-sm btn-primary w-100" onclick="DataTableHelper.reload('audit-table')"><i class="fas fa-filter me-1"></i>Filter</button></div>
    </div>
</div></div>
<div class="card"><div class="card-body">
    <table id="audit-table" class="table table-striped table-sm w-100" data-url="/admin/audit-log/datatable"
           data-columns='[{"data":"id"},{"data":"datetime"},{"data":"user"},{"data":"action"},{"data":"module"},{"data":"entity"},{"data":"description"},{"data":"ip"},{"data":"actions","orderable":false}]'>
        <thead><tr><th>#</th><th>Time</th><th>User</th><th>Action</th><th>Module</th><th>Entity</th><th>Description</th><th>IP</th><th></th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>DataTableHelper.init('audit-table',{order:[[0,'desc']],pageLength:50});</script><?php $__view->endPush() ?>
```

## File: modules\admin\views\audit-log\show.php

```php
<div class="d-flex justify-content-between mb-4">
    <h4 class="mb-0">Audit Entry #<?= $entry['id'] ?></h4>
    <a href="/admin/audit-log" class="btn btn-secondary">Back</a>
</div>
<div class="row g-4">
    <div class="col-md-6"><div class="card"><div class="card-header">Details</div><div class="card-body">
        <table class="table table-sm table-borderless">
            <tr><td class="text-muted" width="35%">User</td><td><?= e($entry['user_name'] ?? 'System') ?></td></tr>
            <tr><td class="text-muted">Action</td><td><?= e($entry['action_type']) ?></td></tr>
            <tr><td class="text-muted">Module</td><td><?= e($entry['module']) ?></td></tr>
            <tr><td class="text-muted">Entity</td><td><?= e($entry['entity_type']) ?> #<?= $entry['entity_id'] ?></td></tr>
            <tr><td class="text-muted">Description</td><td><?= e($entry['description']) ?></td></tr>
            <tr><td class="text-muted">IP</td><td><?= e($entry['ip_address'] ?? '-') ?></td></tr>
            <tr><td class="text-muted">Time</td><td><?= format_datetime($entry['created_at']) ?></td></tr>
            <tr><td class="text-muted">Severity</td><td><?= status_badge($entry['severity'] ?? 'info') ?></td></tr>
        </table>
    </div></div></div>
    <div class="col-md-6">
        <?php if ($entry['old_values']): ?><div class="card mb-3"><div class="card-header">Old Values</div><div class="card-body"><pre class="mb-0" style="max-height:300px;overflow:auto"><?= e(json_encode(json_decode($entry['old_values']), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) ?></pre></div></div><?php endif; ?>
        <?php if ($entry['new_values']): ?><div class="card"><div class="card-header">New Values</div><div class="card-body"><pre class="mb-0" style="max-height:300px;overflow:auto"><?= e(json_encode(json_decode($entry['new_values']), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) ?></pre></div></div><?php endif; ?>
    </div>
</div>
```

## File: modules\admin\views\data-quality\index.php

```php
<h4 class="mb-4"><i class="fas fa-check-double me-2"></i><?= lang('admin.data_quality') ?></h4>
<div class="row g-3 mb-4">
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted">Active Rules</h6><h3><?= $stats['total_rules'] ?></h3></div><i class="fas fa-ruler fa-2x text-primary opacity-50"></i></div></div></div></div>
    <div class="col-md-3"><div class="card border-warning h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted">Open Issues</h6><h3><?= $stats['open_issues'] ?></h3></div><i class="fas fa-exclamation-triangle fa-2x text-warning opacity-50"></i></div></div></div></div>
    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted">Critical</h6><h3><?= $stats['critical_issues'] ?></h3></div><i class="fas fa-fire fa-2x text-danger opacity-50"></i></div></div></div></div>
    <div class="col-md-3"><div class="card border-info h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted">Last Scan</h6><h5><?= $stats['last_scan'] ? format_datetime($stats['last_scan']['started_at']) : 'Never' ?></h5></div><i class="fas fa-search fa-2x text-info opacity-50"></i></div></div></div></div>
</div>
<div class="mb-3"><button class="btn btn-primary" id="btn-scan"><i class="fas fa-search me-1"></i>Run Scan Now</button></div>
<ul class="nav nav-tabs mb-3"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-issues">Issues</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-rules">Rules</a></li></ul>
<div class="tab-content">
    <div class="tab-pane fade show active" id="tab-issues"><div class="card"><div class="card-body">
        <table id="issues-table" class="table table-sm table-striped w-100" data-url="/admin/data-quality/issues/datatable"
               data-columns='[{"data":"rule"},{"data":"entity"},{"data":"field"},{"data":"issue"},{"data":"severity"},{"data":"status"},{"data":"detected"},{"data":"actions","orderable":false}]'>
            <thead><tr><th>Rule</th><th>Entity</th><th>Field</th><th>Issue</th><th>Severity</th><th>Status</th><th>Detected</th><th>Actions</th></tr></thead>
        </table>
    </div></div></div>
    <div class="tab-pane fade" id="tab-rules"><div class="card"><div class="card-body">
        <table id="rules-table" class="table table-sm table-striped w-100" data-url="/admin/data-quality/rules/datatable"
               data-columns='[{"data":"code"},{"data":"name"},{"data":"entity"},{"data":"type"},{"data":"severity"},{"data":"active"}]'>
            <thead><tr><th>Code</th><th>Name</th><th>Entity</th><th>Type</th><th>Severity</th><th>Active</th></tr></thead>
        </table>
    </div></div></div>
</div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('issues-table');DataTableHelper.init('rules-table',{paging:false});
$('#btn-scan').click(function(){const btn=$(this);btn.prop('disabled',true).html('<i class="fas fa-spinner fa-spin me-1"></i>Scanning...');$.post('/admin/data-quality/scan',{_token:'<?= csrf_token() ?>'},function(r){btn.prop('disabled',false).html('<i class="fas fa-search me-1"></i>Run Scan Now');if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('issues-table');}else FormHelper.showToast('error',r.message);},'json');});
$(document).on('click','.btn-issue-action',function(){$.post('/admin/data-quality/issues/'+$(this).data('id')+'/action',{_token:'<?= csrf_token() ?>',action:$(this).data('action')},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('issues-table');}else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\departments\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-sitemap me-2"></i><?= lang('admin.departments') ?></h4>
    <?php if (Gate::can('staff.manage')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#deptModal" onclick="openDeptForm()"><i class="fas fa-plus me-1"></i>Add Department</button>
    <?php endif; ?>
</div>
<div class="card">
    <div class="card-body">
        <table id="dept-table" class="table table-striped w-100"
               data-url="/admin/departments/datatable"
               data-columns='[{"data":"code"},{"data":"name"},{"data":"name_ar"},{"data":"parent"},{"data":"manager"},{"data":"staff_count"},{"data":"active"},{"data":"actions","orderable":false}]'>
            <thead><tr><th>Code</th><th>Name</th><th>Arabic</th><th>Parent</th><th>Manager</th><th>Staff</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<div class="modal fade" id="deptModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title" id="deptModalTitle">Department</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="dept-form">
            <input type="hidden" name="_token" value="<?= csrf_token() ?>">
            <input type="hidden" name="dept_id" id="dept_id">
            <div class="mb-3"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="department_code" id="dept_code" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" id="dept_name_en" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" id="dept_name_ar" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">Parent</label><select name="parent_id" id="dept_parent" class="form-select"><option value="">None</option><?php foreach($departments as $d): ?><option value="<?= $d['id'] ?>"><?= e($d['name_en']) ?></option><?php endforeach; ?></select></div>
            <div class="mb-3"><label class="form-label">Manager</label><select name="manager_user_id" id="dept_manager" class="form-select"><option value="">None</option><?php foreach($users as $u): ?><option value="<?= $u['id'] ?>"><?= e($u['full_name_en']) ?></option><?php endforeach; ?></select></div>
            <div class="form-check form-switch"><input class="form-check-input" type="checkbox" name="is_active" value="1" id="dept_active" checked><label class="form-check-label">Active</label></div>
        </form>
    </div>
    <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button><button type="button" class="btn btn-primary" id="btn-save-dept">Save</button></div>
</div></div></div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('dept-table');
function openDeptForm(data){
    $('#deptModalTitle').text(data?'Edit Department':'Add Department');
    $('#dept_id').val(data?.id||''); $('#dept_code').val(data?.code||'').prop('readonly',!!data);
    $('#dept_name_en').val(data?.nameEn||''); $('#dept_name_ar').val(data?.nameAr||'');
    $('#dept_parent').val(data?.parent||''); $('#dept_manager').val(data?.manager||'');
    $('#dept_active').prop('checked',data?data.active=='1':true);
}
$(document).on('click','.btn-edit-dept',function(){
    openDeptForm({id:$(this).data('id'),code:$(this).data('code'),nameEn:$(this).data('name-en'),nameAr:$(this).data('name-ar'),parent:$(this).data('parent'),manager:$(this).data('manager'),active:$(this).data('active')});
    new bootstrap.Modal('#deptModal').show();
});
$('#btn-save-dept').click(function(){
    const id=$('#dept_id').val();
    const url=id?'/admin/departments/'+id+'/update':'/admin/departments/store';
    $.post(url,$('#dept-form').serialize(),function(r){
        if(r.success){bootstrap.Modal.getInstance(document.getElementById('deptModal')).hide();FormHelper.showToast('success',r.message);DataTableHelper.reload('dept-table');}
        else FormHelper.showToast('error',r.message||'Error');
    },'json');
});
$(document).on('click','.btn-delete-dept',function(){
    if(!confirm('Delete this department?'))return;
    $.post('/admin/departments/'+$(this).data('id')+'/delete',{_token:'<?= csrf_token() ?>'},function(r){
        if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('dept-table');}else FormHelper.showToast('error',r.message);
    },'json');
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\export-log\index.php

```php
<h4 class="mb-4"><i class="fas fa-file-export me-2"></i><?= lang('admin.export_log') ?></h4>
<div class="card"><div class="card-body">
    <table id="export-table" class="table table-sm table-striped w-100" data-url="/admin/export-log/datatable"
           data-columns='[{"data":"type"},{"data":"source"},{"data":"format"},{"data":"rows"},{"data":"user"},{"data":"date"},{"data":"ip"}]'>
        <thead><tr><th>Type</th><th>Source</th><th>Format</th><th>Rows</th><th>User</th><th>Date</th><th>IP</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>DataTableHelper.init('export-table',{order:[[5,'desc']]});</script><?php $__view->endPush() ?>
```

## File: modules\admin\views\form-numbers\index.php

```php
<h4 class="mb-4"><i class="fas fa-hashtag me-2"></i><?= lang('admin.form_numbers') ?></h4>
<div class="card"><div class="card-body">
    <table id="fn-table" class="table table-striped w-100" data-url="/admin/form-numbers/datatable"
           data-columns='[{"data":"name"},{"data":"type"},{"data":"range"},{"data":"current"},{"data":"issued"},{"data":"remaining"},{"data":"active"},{"data":"actions","orderable":false}]'>
        <thead><tr><th>Pool Name</th><th>Type</th><th>Range</th><th>Current</th><th>Issued</th><th>Remaining</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('fn-table',{paging:false});
$(document).on('click','.btn-issue-form',function(){if(!confirm('Issue next form number?'))return;
    $.post('/admin/form-numbers/'+$(this).data('id')+'/issue',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success','Issued: '+r.form_number);DataTableHelper.reload('fn-table');}else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\import\index.php

```php
<h4 class="mb-4"><i class="fas fa-file-import me-2"></i><?= lang('admin.import') ?></h4>
<div class="card mb-4"><div class="card-header">Upload Import File</div><div class="card-body">
    <form id="import-form" enctype="multipart/form-data">
        <input type="hidden" name="_token" value="<?= csrf_token() ?>">
        <div class="row g-3">
            <div class="col-md-4"><label class="form-label">Import Type</label><select name="import_type" class="form-select"><option value="members">Members</option></select></div>
            <div class="col-md-6"><label class="form-label">CSV File</label><input type="file" name="import_file" class="form-control" accept=".csv,.xls,.xlsx" required></div>
            <div class="col-md-2 d-flex align-items-end"><button type="submit" class="btn btn-primary w-100"><i class="fas fa-upload me-1"></i>Upload</button></div>
        </div>
    </form>
</div></div>
<div class="card"><div class="card-header">Import History</div><div class="card-body">
    <table id="import-table" class="table table-sm table-striped w-100" data-url="/admin/import/datatable"
           data-columns='[{"data":"batch"},{"data":"type"},{"data":"file"},{"data":"total"},{"data":"success"},{"data":"errors"},{"data":"status"},{"data":"by"},{"data":"date"}]'>
        <thead><tr><th>Batch</th><th>Type</th><th>File</th><th>Total</th><th>Success</th><th>Errors</th><th>Status</th><th>By</th><th>Date</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('import-table');
$('#import-form').on('submit',function(e){
    e.preventDefault();const fd=new FormData(this);
    $.ajax({url:'/admin/import/upload',type:'POST',data:fd,processData:false,contentType:false,dataType:'json',
        success:function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('import-table');}else FormHelper.showToast('error',r.message);},
        error:function(){FormHelper.showToast('error','Upload failed');}
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\jobs\index.php

```php
<h4 class="mb-4"><i class="fas fa-cog fa-spin me-2"></i><?= lang('admin.system_jobs') ?></h4>
<div class="card"><div class="card-body">
    <table id="jobs-table" class="table table-striped w-100" data-url="/admin/jobs/datatable"
           data-columns='[{"data":"code"},{"data":"name"},{"data":"type"},{"data":"active"},{"data":"last_run"},{"data":"last_status"},{"data":"runs"},{"data":"actions","orderable":false}]'>
        <thead><tr><th>Code</th><th>Name</th><th>Type</th><th>Status</th><th>Last Run</th><th>Result</th><th>Runs</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('jobs-table',{paging:false});
$(document).on('click','.btn-toggle-job',function(){$.post('/admin/jobs/'+$(this).data('id')+'/toggle',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('jobs-table');}else FormHelper.showToast('error',r.message);},'json');});
$(document).on('click','.btn-run-job',function(){if(!confirm('Run this job now?'))return;const btn=$(this);btn.prop('disabled',true).html('<i class="fas fa-spinner fa-spin"></i>');$.post('/admin/jobs/'+$(this).data('id')+'/run',{_token:'<?= csrf_token() ?>'},function(r){btn.prop('disabled',false).html('<i class="fas fa-bolt"></i>');if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('jobs-table');}else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\jobs\runs.php

```php
<div class="d-flex justify-content-between mb-4">
    <h4 class="mb-0">Job Runs: <?= e($job['job_name']) ?></h4>
    <a href="/admin/jobs" class="btn btn-secondary">Back</a>
</div>
<div class="card"><div class="card-body"><div class="table-responsive">
    <table class="table table-sm table-striped">
        <thead><tr><th>Started</th><th>Completed</th><th>Duration</th><th>Status</th><th>Triggered</th><th>Records</th><th>Error</th></tr></thead>
        <tbody>
        <?php foreach ($runs as $r): ?>
        <tr>
            <td><?= format_datetime($r['started_at']) ?></td>
            <td><?= $r['completed_at'] ? format_datetime($r['completed_at']) : '-' ?></td>
            <td><?= $r['duration_ms'] ? $r['duration_ms'] . 'ms' : '-' ?></td>
            <td><?= status_badge($r['status']) ?></td>
            <td><?= e($r['triggered_by']) ?></td>
            <td><?= $r['records_processed'] !== null ? $r['records_processed'] : '-' ?></td>
            <td><?= $r['error_message'] ? '<span class="text-danger" title="' . e($r['error_message']) . '"><i class="fas fa-exclamation-circle"></i></span>' : '-' ?></td>
        </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
</div></div></div>
```

## File: modules\admin\views\login-log\index.php

```php
<h4 class="mb-4"><i class="fas fa-sign-in-alt me-2"></i><?= lang('admin.login_log') ?></h4>
<div class="card mb-3"><div class="card-body">
    <div class="row g-2" data-filter-table="login-table">
        <div class="col-md-3"><select name="action_filter" class="form-select form-select-sm"><option value="">All Actions</option><option value="login_success">Success</option><option value="login_failed">Failed</option><option value="logout">Logout</option></select></div>
        <div class="col-md-2"><button type="button" class="btn btn-sm btn-primary" onclick="DataTableHelper.reload('login-table')"><i class="fas fa-filter me-1"></i>Filter</button></div>
    </div>
</div></div>
<div class="card"><div class="card-body">
    <table id="login-table" class="table table-striped table-sm w-100" data-url="/admin/login-log/datatable"
           data-columns='[{"data":"datetime"},{"data":"username"},{"data":"action"},{"data":"ip"},{"data":"reason"}]'>
        <thead><tr><th>Time</th><th>Username</th><th>Action</th><th>IP</th><th>Reason</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>DataTableHelper.init('login-table',{order:[[0,'desc']],pageLength:50});</script><?php $__view->endPush() ?>
```

## File: modules\admin\views\lookups\index.php

```php
<h4 class="mb-4"><i class="fas fa-list me-2"></i><?= lang('admin.lookups') ?></h4>
<div class="row g-4">
    <div class="col-md-4"><div class="card"><div class="card-header">Categories</div><div class="card-body">
        <div class="list-group list-group-flush">
            <?php foreach ($categories as $c): ?>
            <a href="#" class="list-group-item list-group-item-action category-item" data-id="<?= $c['id'] ?>" data-name="<?= e($c['category_name_en']) ?>">
                <div class="d-flex justify-content-between align-items-center">
                    <div><strong><?= e($c['category_name_en']) ?></strong><br><small class="text-muted"><?= e($c['category_code']) ?></small></div>
                    <?php if ($c['is_system']): ?><span class="badge bg-info">System</span><?php endif; ?>
                </div>
            </a>
            <?php endforeach; ?>
        </div>
    </div></div></div>
    <div class="col-md-8"><div class="card"><div class="card-header d-flex justify-content-between"><span id="values-title">Select a category</span><button class="btn btn-sm btn-primary d-none" id="btn-add-value"><i class="fas fa-plus me-1"></i>Add Value</button></div><div class="card-body">
        <table id="values-table" class="table table-sm table-striped w-100" data-url="/admin/lookups/values/datatable"
               data-columns='[{"data":"code"},{"data":"value_en"},{"data":"value_ar"},{"data":"sort"},{"data":"active"},{"data":"actions","orderable":false}]'>
            <thead><tr><th>Code</th><th>English</th><th>Arabic</th><th>Sort</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div></div></div>
</div>
<div class="modal fade" id="valueModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Lookup Value</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="value-form"><input type="hidden" name="_token" value="<?= csrf_token() ?>"><input type="hidden" name="category_id" id="vf_cat_id"><input type="hidden" name="value_id" id="vf_id">
            <div class="mb-3"><label class="form-label">Code</label><input type="text" name="lookup_code" id="vf_code" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">English</label><input type="text" name="value_en" id="vf_en" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">Arabic</label><input type="text" name="value_ar" id="vf_ar" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">Sort Order</label><input type="number" name="sort_order" id="vf_sort" class="form-control" value="0"></div>
            <div class="form-check"><input class="form-check-input" type="checkbox" name="is_active" value="1" id="vf_active" checked><label class="form-check-label">Active</label></div>
        </form>
    </div>
    <div class="modal-footer"><button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button><button class="btn btn-primary" id="btn-save-value">Save</button></div>
</div></div></div>
<?php $__view->push('scripts') ?>
<script>
let currentCatId=null;
const vt=DataTableHelper.init('values-table',{paging:false,ajax:{data:function(d){d.category_id=currentCatId||0;}}});
$('.category-item').click(function(e){e.preventDefault();$('.category-item').removeClass('active');$(this).addClass('active');
    currentCatId=$(this).data('id');$('#values-title').text($(this).data('name'));$('#btn-add-value').removeClass('d-none');DataTableHelper.reload('values-table');});
$('#btn-add-value').click(function(){$('#vf_id').val('');$('#vf_cat_id').val(currentCatId);$('#vf_code').val('').prop('readonly',false);$('#vf_en').val('');$('#vf_ar').val('');$('#vf_sort').val(0);$('#vf_active').prop('checked',true);new bootstrap.Modal('#valueModal').show();});
$(document).on('click','.btn-edit-value',function(){$('#vf_id').val($(this).data('id'));$('#vf_cat_id').val(currentCatId);$('#vf_code').val($(this).data('code')).prop('readonly',true);$('#vf_en').val($(this).data('en'));$('#vf_ar').val($(this).data('ar'));$('#vf_sort').val($(this).data('sort'));$('#vf_active').prop('checked',$(this).data('active')=='1');new bootstrap.Modal('#valueModal').show();});
$('#btn-save-value').click(function(){const id=$('#vf_id').val();const url=id?'/admin/lookups/values/'+id+'/update':'/admin/lookups/values/store';
    $.post(url,$('#value-form').serialize(),function(r){if(r.success){bootstrap.Modal.getInstance(document.getElementById('valueModal')).hide();FormHelper.showToast('success',r.message);DataTableHelper.reload('values-table');}else FormHelper.showToast('error',r.message);},'json');});
$(document).on('click','.btn-delete-value',function(){if(!confirm('Delete?'))return;$.post('/admin/lookups/values/'+$(this).data('id')+'/delete',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('values-table');}else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\recycle-bin\index.php

```php
<h4 class="mb-4"><i class="fas fa-trash-restore me-2"></i><?= lang('admin.recycle_bin') ?></h4>
<div class="card"><div class="card-body">
    <table id="recycle-table" class="table table-sm table-striped w-100" data-url="/admin/recycle-bin/datatable"
           data-columns='[{"data":"type"},{"data":"entity_id"},{"data":"deleted_by"},{"data":"deleted_at"},{"data":"deadline"},{"data":"actions","orderable":false}]'>
        <thead><tr><th>Entity Type</th><th>Entity ID</th><th>Deleted By</th><th>Deleted At</th><th>Restore Deadline</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('recycle-table');
$(document).on('click','.btn-restore',function(){if(!confirm('Restore this item?'))return;$.post('/admin/recycle-bin/'+$(this).data('id')+'/restore',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('recycle-table');}else FormHelper.showToast('error',r.message);},'json');});
$(document).on('click','.btn-purge',function(){if(!confirm('PERMANENTLY delete? This cannot be undone!'))return;$.post('/admin/recycle-bin/'+$(this).data('id')+'/purge',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('recycle-table');}else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\roles\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= $role ? 'Edit Role: ' . e($role['role_name_en']) : 'Create Role' ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= $role ? '/admin/roles/' . $role['id'] : '/admin/roles' ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3 mb-4">
                        <?php if (!$role): ?>
                        <div class="col-md-4">
                            <label class="form-label">Role Code <span class="text-danger">*</span></label>
                            <input type="text" name="role_code" class="form-control" required>
                            <div class="invalid-feedback" data-field="role_code"></div>
                        </div>
                        <?php endif; ?>
                        <div class="col-md-4">
                            <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="role_name_en" class="form-control" value="<?= e($role['role_name_en'] ?? '') ?>" required>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                            <input type="text" name="role_name_ar" class="form-control" value="<?= e($role['role_name_ar'] ?? '') ?>" required>
                        </div>
                        <div class="col-md-6">
                            <div class="form-check form-switch">
                                <input class="form-check-input" type="checkbox" name="is_active" value="1" <?= ($role['is_active'] ?? 1) ? 'checked' : '' ?>>
                                <label class="form-check-label">Active</label>
                            </div>
                        </div>
                    </div>
                    <h5 class="mb-3">Permission Matrix</h5>
                    <div class="mb-3">
                        <button type="button" class="btn btn-sm btn-outline-success" id="btn-check-all">Check All</button>
                        <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-uncheck-all">Uncheck All</button>
                    </div>
                    <?php foreach ($modules as $moduleName => $perms): ?>
                    <div class="card mb-3">
                        <div class="card-header py-2"><strong><?= e(ucfirst($moduleName)) ?></strong></div>
                        <div class="card-body py-2">
                            <div class="row">
                                <?php foreach ($perms as $p): ?>
                                <div class="col-md-4 col-lg-3">
                                    <div class="form-check">
                                        <input class="form-check-input perm-checkbox" type="checkbox" name="permissions[]" value="<?= $p['id'] ?>" id="perm-<?= $p['id'] ?>"
                                            <?= in_array($p['id'], $rolePermissions) ? 'checked' : '' ?>>
                                        <label class="form-check-label small" for="perm-<?= $p['id'] ?>" title="<?= e($p['permission_code']) ?>">
                                            <?= e($p['permission_name_en']) ?>
                                        </label>
                                    </div>
                                </div>
                                <?php endforeach; ?>
                            </div>
                        </div>
                    </div>
                    <?php endforeach; ?>
                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button>
                        <a href="/admin/roles" class="btn btn-secondary">Cancel</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>
$('#btn-check-all').click(function(){$('.perm-checkbox').prop('checked',true);});
$('#btn-uncheck-all').click(function(){$('.perm-checkbox').prop('checked',false);});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\roles\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-shield-alt me-2"></i><?= lang('admin.roles') ?></h4>
    <a href="/admin/roles/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i>Create Role</a>
</div>
<div class="card">
    <div class="card-body">
        <table id="roles-table" class="table table-striped w-100"
               data-url="/admin/roles/datatable"
               data-columns='[{"data":"id"},{"data":"code"},{"data":"name"},{"data":"name_ar"},{"data":"users"},{"data":"permissions"},{"data":"system"},{"data":"active"},{"data":"actions","orderable":false}]'>
            <thead><tr><th>#</th><th>Code</th><th>Name</th><th>Arabic</th><th>Users</th><th>Perms</th><th>System</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('roles-table');
    $(document).on('click','.btn-delete-role',function(){
        if(!confirm('Delete this role?')) return;
        $.post('/admin/roles/'+$(this).data('id')+'/delete',{_token:'<?= csrf_token() ?>'},function(r){
            if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('roles-table');}
            else FormHelper.showToast('error',r.message);
        },'json');
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\sequences\index.php

```php
<h4 class="mb-4"><i class="fas fa-sort-numeric-up me-2"></i><?= lang('admin.sequences') ?></h4>
<div class="card"><div class="card-body">
    <table id="seq-table" class="table table-striped w-100" data-url="/admin/sequences/datatable"
           data-columns='[{"data":"name"},{"data":"prefix"},{"data":"current"},{"data":"next_sample"},{"data":"reset"},{"data":"active"},{"data":"actions","orderable":false}]'>
        <thead><tr><th>Name</th><th>Prefix</th><th>Current</th><th>Next Sample</th><th>Reset</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('seq-table',{paging:false});
$(document).on('click','.btn-reset-seq',function(){if(!confirm('Reset sequence "'+$(this).data('name')+'"? This is irreversible!'))return;
    $.post('/admin/sequences/'+$(this).data('id')+'/reset',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('seq-table');}else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\settings\index.php

```php
<h4 class="mb-4"><i class="fas fa-cogs me-2"></i><?= lang('admin.settings') ?></h4>
<form method="POST" action="/admin/settings" data-ajax="true">
    <?= csrf_field() ?>
    <ul class="nav nav-tabs mb-3" role="tablist">
        <?php $i = 0; foreach (array_keys($groups) as $group): ?>
        <li class="nav-item">
            <a class="nav-link <?= $i === 0 ? 'active' : '' ?>" data-bs-toggle="tab" href="#tab-<?= e($group) ?>"><?= e(ucfirst(str_replace('_', ' ', $group))) ?></a>
        </li>
        <?php $i++; endforeach; ?>
    </ul>
    <div class="tab-content">
        <?php $i = 0; foreach ($groups as $group => $settings): ?>
        <div class="tab-pane fade <?= $i === 0 ? 'show active' : '' ?>" id="tab-<?= e($group) ?>">
            <div class="card">
                <div class="card-body">
                    <?php foreach ($settings as $s): ?>
                    <div class="row mb-3 align-items-center">
                        <div class="col-md-4">
                            <label class="form-label fw-bold mb-0"><?= e($s['config_key']) ?></label>
                            <?php if ($s['description_en']): ?><br><small class="text-muted"><?= e($s['description_en']) ?></small><?php endif; ?>
                        </div>
                        <div class="col-md-6">
                            <?php if ($s['is_readonly']): ?>
                                <input type="text" class="form-control" value="<?= e($s['config_value'] ?? '') ?>" disabled>
                            <?php elseif ($s['config_type'] === 'boolean'): ?>
                                <select name="config[<?= e($s['config_key']) ?>]" class="form-select">
                                    <option value="true" <?= $s['config_value'] === 'true' ? 'selected' : '' ?>>True</option>
                                    <option value="false" <?= $s['config_value'] === 'false' ? 'selected' : '' ?>>False</option>
                                </select>
                            <?php elseif ($s['config_type'] === 'integer'): ?>
                                <input type="number" name="config[<?= e($s['config_key']) ?>]" class="form-control" value="<?= e($s['config_value'] ?? '') ?>">
                            <?php else: ?>
                                <input type="text" name="config[<?= e($s['config_key']) ?>]" class="form-control" value="<?= e($s['config_value'] ?? '') ?>">
                            <?php endif; ?>
                        </div>
                        <div class="col-md-2"><span class="badge bg-light text-dark"><?= e($s['config_type']) ?></span></div>
                    </div>
                    <?php endforeach; ?>
                </div>
            </div>
        </div>
        <?php $i++; endforeach; ?>
    </div>
    <?php if (Gate::can('settings.manage')): ?>
    <div class="mt-3"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save Settings</button></div>
    <?php endif; ?>
</form>
```

## File: modules\admin\views\staff\form.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $staff ? 'Edit Staff' : 'Create Staff' ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $staff ? '/admin/staff/' . $staff['id'] : '/admin/staff' ?>" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Staff # <span class="text-danger">*</span></label><input type="text" name="staff_number" class="form-control" value="<?= e($staff['staff_number'] ?? '') ?>" required><div class="invalid-feedback" data-field="staff_number"></div></div>
                <div class="col-md-4"><label class="form-label">First Name (EN) <span class="text-danger">*</span></label><input type="text" name="first_name_en" class="form-control" value="<?= e($staff['first_name_en'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Last Name (EN) <span class="text-danger">*</span></label><input type="text" name="last_name_en" class="form-control" value="<?= e($staff['last_name_en'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">First Name (AR)</label><input type="text" name="first_name_ar" class="form-control" value="<?= e($staff['first_name_ar'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Last Name (AR)</label><input type="text" name="last_name_ar" class="form-control" value="<?= e($staff['last_name_ar'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Gender</label><select name="gender" class="form-select"><option value="">-</option><option value="male" <?= selected('male', $staff['gender'] ?? '') ?>>Male</option><option value="female" <?= selected('female', $staff['gender'] ?? '') ?>>Female</option></select></div>
                <div class="col-md-4"><label class="form-label">Department</label><select name="department_id" class="form-select"><option value="">-</option><?php foreach($departments as $d): ?><option value="<?= $d['id'] ?>" <?= selected($d['id'], $staff['department_id'] ?? '') ?>><?= e($d['name_en']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Position</label><select name="position_id" class="form-select"><option value="">-</option><?php foreach($positions as $p): ?><option value="<?= $p['id'] ?>" <?= selected($p['id'], $staff['position_id'] ?? '') ?>><?= e($p['title_en']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Linked User</label><select name="user_id" class="form-select"><option value="">-</option><?php foreach($users as $u): ?><option value="<?= $u['id'] ?>" <?= selected($u['id'], $staff['user_id'] ?? '') ?>><?= e($u['full_name_en']) ?> (<?= e($u['username']) ?>)</option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Phone</label><input type="text" name="phone" class="form-control" value="<?= e($staff['phone'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Email</label><input type="email" name="email" class="form-control" value="<?= e($staff['email'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Hire Date <span class="text-danger">*</span></label><input type="date" name="hire_date" class="form-control datepicker" value="<?= e($staff['hire_date'] ?? '') ?>" required></div>
                <?php if ($staff): ?>
                <div class="col-md-4"><label class="form-label">Status</label><select name="status" class="form-select"><option value="active" <?= selected('active', $staff['status']) ?>>Active</option><option value="on_leave" <?= selected('on_leave', $staff['status']) ?>>On Leave</option><option value="suspended" <?= selected('suspended', $staff['status']) ?>>Suspended</option><option value="terminated" <?= selected('terminated', $staff['status']) ?>>Terminated</option></select></div>
                <?php endif; ?>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/admin/staff" class="btn btn-secondary">Cancel</a></div>
        </form>
    </div>
</div></div></div>
```

## File: modules\admin\views\staff\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-id-badge me-2"></i><?= lang('admin.staff') ?></h4>
    <?php if (Gate::can('staff.manage')): ?>
    <a href="/admin/staff/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i>Add Staff</a>
    <?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="staff-table" class="table table-striped w-100" data-url="/admin/staff/datatable"
           data-columns='[{"data":"id"},{"data":"staff_number"},{"data":"name"},{"data":"department"},{"data":"position"},{"data":"phone"},{"data":"status"},{"data":"hire_date"},{"data":"actions","orderable":false}]'>
        <thead><tr><th>#</th><th>Number</th><th>Name</th><th>Dept</th><th>Position</th><th>Phone</th><th>Status</th><th>Hire Date</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>DataTableHelper.init('staff-table',{order:[[0,'desc']]});</script><?php $__view->endPush() ?>
```

## File: modules\admin\views\staff\view.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><?= e($staff['first_name_en'] . ' ' . $staff['last_name_en']) ?> <?= status_badge($staff['status']) ?></h4>
    <div class="d-flex gap-2">
        <a href="/admin/staff/<?= $staff['id'] ?>/edit" class="btn btn-warning"><i class="fas fa-edit me-1"></i>Edit</a>
        <a href="/admin/staff" class="btn btn-secondary">Back</a>
    </div>
</div>
<div class="row g-4">
    <div class="col-md-4">
        <div class="card"><div class="card-body">
            <h6 class="text-muted">Staff Info</h6>
            <table class="table table-sm table-borderless mb-0">
                <tr><td class="text-muted" width="40%">Number</td><td><?= e($staff['staff_number']) ?></td></tr>
                <tr><td class="text-muted">Department</td><td><?= e($staff['dept_name'] ?? '-') ?></td></tr>
                <tr><td class="text-muted">Position</td><td><?= e($staff['position_name'] ?? '-') ?></td></tr>
                <tr><td class="text-muted">Phone</td><td><?= e($staff['phone'] ?? '-') ?></td></tr>
                <tr><td class="text-muted">Email</td><td><?= e($staff['email'] ?? '-') ?></td></tr>
                <tr><td class="text-muted">Hire Date</td><td><?= format_date($staff['hire_date']) ?></td></tr>
                <tr><td class="text-muted">User Account</td><td><?= e($staff['linked_username'] ?? 'Not linked') ?></td></tr>
            </table>
        </div></div>
    </div>
    <div class="col-md-8">
        <ul class="nav nav-tabs mb-3"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-tasks">Tasks (<?= count($tasks) ?>)</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-shifts">Shifts</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-reviews">Reviews</a></li></ul>
        <div class="tab-content">
            <div class="tab-pane fade show active" id="tab-tasks"><div class="card"><div class="card-body">
                <table class="table table-sm"><thead><tr><th>Title</th><th>Priority</th><th>Status</th><th>Due</th></tr></thead><tbody>
                <?php foreach ($tasks as $t): ?><tr><td><?= e($t['title']) ?></td><td><?= status_badge($t['priority']) ?></td><td><?= status_badge($t['status']) ?></td><td><?= $t['due_date'] ? format_datetime($t['due_date']) : '-' ?></td></tr><?php endforeach; ?>
                <?php if (empty($tasks)): ?><tr><td colspan="4" class="text-center text-muted">No tasks</td></tr><?php endif; ?>
                </tbody></table>
            </div></div></div>
            <div class="tab-pane fade" id="tab-shifts"><div class="card"><div class="card-body">
                <table class="table table-sm"><thead><tr><th>Date</th><th>Scheduled</th><th>Actual</th><th>Status</th></tr></thead><tbody>
                <?php foreach ($shifts as $s): ?><tr><td><?= format_date($s['shift_date']) ?></td><td><?= e($s['start_time']) ?> - <?= e($s['end_time']) ?></td><td><?= e($s['actual_start'] ?? '-') ?> - <?= e($s['actual_end'] ?? '-') ?></td><td><?= status_badge($s['status']) ?></td></tr><?php endforeach; ?>
                <?php if (empty($shifts)): ?><tr><td colspan="4" class="text-center text-muted">No shifts</td></tr><?php endif; ?>
                </tbody></table>
            </div></div></div>
            <div class="tab-pane fade" id="tab-reviews"><div class="card"><div class="card-body">
                <table class="table table-sm"><thead><tr><th>Period</th><th>Reviewer</th><th>Rating</th><th>Status</th></tr></thead><tbody>
                <?php foreach ($reviews as $r): ?><tr><td><?= format_date($r['review_period_start']) ?> - <?= format_date($r['review_period_end']) ?></td><td><?= e($r['reviewer_name'] ?? '-') ?></td><td><?= $r['overall_rating'] ? number_format($r['overall_rating'], 1) . '/5' : '-' ?></td><td><?= status_badge($r['status']) ?></td></tr><?php endforeach; ?>
                <?php if (empty($reviews)): ?><tr><td colspan="4" class="text-center text-muted">No reviews</td></tr><?php endif; ?>
                </tbody></table>
            </div></div></div>
        </div>
    </div>
</div>
```

## File: modules\admin\views\system-health\index.php

```php
<h4 class="mb-4"><i class="fas fa-heartbeat me-2"></i><?= lang('admin.system_health') ?></h4>
<div class="row g-3 mb-4">
    <div class="col-md-3"><div class="card border-success h-100"><div class="card-body"><h6 class="text-muted">Active Members</h6><h3 class="mb-0"><?= number_format($health['active_members']) ?></h3><small class="text-muted">of <?= number_format($health['total_members']) ?> total</small></div></div></div>
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><h6 class="text-muted">Active Users</h6><h3 class="mb-0"><?= $health['active_users'] ?></h3><small class="text-muted"><?= $health['active_sessions'] ?> sessions</small></div></div></div>
    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body"><h6 class="text-muted">Errors Today</h6><h3 class="mb-0"><?= $health['errors_today'] ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-warning h-100"><div class="card-body"><h6 class="text-muted">DQ Issues</h6><h3 class="mb-0"><?= $health['open_dq_issues'] ?></h3><small class="text-muted"><?= $health['pending_notifications'] ?> pending notifications</small></div></div></div>
</div>
<div class="row g-4">
    <div class="col-md-6"><div class="card"><div class="card-header">Server Information</div><div class="card-body">
        <table class="table table-sm table-borderless mb-0">
            <tr><td class="text-muted" width="40%">PHP Version</td><td><?= e($health['php_version']) ?></td></tr>
            <tr><td class="text-muted">MySQL Version</td><td><?= e($health['mysql_version']) ?></td></tr>
            <tr><td class="text-muted">Web Server</td><td><?= e($health['server_software']) ?></td></tr>
            <tr><td class="text-muted">Memory Usage</td><td><?= e($health['memory_usage']) ?> / <?= e($health['memory_limit']) ?></td></tr>
            <tr><td class="text-muted">Max Upload</td><td><?= e($health['max_upload']) ?></td></tr>
            <tr><td class="text-muted">Max POST</td><td><?= e($health['max_post']) ?></td></tr>
            <tr><td class="text-muted">Timezone</td><td><?= e($health['timezone']) ?></td></tr>
            <tr><td class="text-muted">Disk Free</td><td><?= e($health['disk_free']) ?> / <?= e($health['disk_total']) ?></td></tr>
            <tr><td class="text-muted">Storage Used</td><td><?= e($health['storage_used']) ?></td></tr>
        </table>
    </div></div></div>
    <div class="col-md-6"><div class="card"><div class="card-header">Recent Errors</div><div class="card-body" style="max-height:400px;overflow:auto">
        <?php if (empty($recentErrors)): ?>
        <div class="text-center text-muted py-4"><i class="fas fa-check-circle fa-3x mb-2 text-success"></i><p>No recent errors</p></div>
        <?php else: ?>
        <div class="list-group list-group-flush">
            <?php foreach ($recentErrors as $err): ?>
            <div class="list-group-item px-0">
                <div class="d-flex justify-content-between">
                    <span class="badge bg-<?= $err['error_level'] === 'critical' ? 'danger' : ($err['error_level'] === 'error' ? 'warning' : 'info') ?>"><?= e($err['error_level']) ?></span>
                    <small class="text-muted"><?= format_datetime($err['created_at']) ?></small>
                </div>
                <small class="text-truncate d-block" style="max-width:100%"><?= e(str_limit($err['message'], 120)) ?></small>
                <?php if ($err['file_path']): ?><small class="text-muted"><?= e(basename($err['file_path'])) ?>:<?= $err['line_number'] ?></small><?php endif; ?>
            </div>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>
    </div></div></div>
</div>
```

## File: modules\admin\views\translations\index.php

```php
<h4 class="mb-4"><i class="fas fa-language me-2"></i><?= lang('admin.translations') ?></h4>
<div class="card mb-3"><div class="card-body">
    <div class="row g-2">
        <div class="col-md-3"><select id="lang-filter" class="form-select form-select-sm"><?php foreach ($languages as $l): ?><option value="<?= $l['id'] ?>"><?= e($l['name_en']) ?> (<?= e($l['code']) ?>)</option><?php endforeach; ?></select></div>
        <div class="col-md-3"><select id="group-filter" class="form-select form-select-sm"><option value="">All Groups</option><?php foreach ($groups as $g): ?><option value="<?= e($g['translation_group']) ?>"><?= e($g['translation_group']) ?></option><?php endforeach; ?></select></div>
        <div class="col-md-2"><button class="btn btn-sm btn-primary" id="btn-filter-trans">Filter</button></div>
        <div class="col-md-2 ms-auto"><button class="btn btn-sm btn-success" id="btn-save-trans"><i class="fas fa-save me-1"></i>Save All</button></div>
    </div>
</div></div>
<div class="card"><div class="card-body">
    <table id="trans-table" class="table table-sm w-100" data-url="/admin/translations/datatable"
           data-columns='[{"data":"group"},{"data":"key"},{"data":"value","orderable":false}]'>
        <thead><tr><th>Group</th><th>Key</th><th>Value</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
const tt=DataTableHelper.init('trans-table',{paging:false,ajax:{data:function(d){d.language_id=$('#lang-filter').val();d.group_filter=$('#group-filter').val();}}});
$('#btn-filter-trans').click(function(){DataTableHelper.reload('trans-table');});
$('#btn-save-trans').click(function(){const data={_token:'<?= csrf_token() ?>',translations:{}};$('.translation-input').each(function(){data.translations[$(this).data('id')]=$(this).val();});
    $.post('/admin/translations/save',data,function(r){if(r.success)FormHelper.showToast('success',r.message);else FormHelper.showToast('error',r.message);},'json');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\users\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header">
                <h5 class="mb-0"><i class="fas fa-user me-2"></i><?= $user ? 'Edit User' : 'Create User' ?></h5>
            </div>
            <div class="card-body">
                <form method="POST" action="<?= $user ? '/admin/users/' . $user['id'] : '/admin/users' ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Username <span class="text-danger">*</span></label>
                            <input type="text" name="username" class="form-control" value="<?= e($user['username'] ?? '') ?>" required <?= $user ? 'readonly' : '' ?>>
                            <div class="invalid-feedback" data-field="username"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Email <span class="text-danger">*</span></label>
                            <input type="email" name="email" class="form-control" value="<?= e($user['email'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="email"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Full Name (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="full_name_en" class="form-control" value="<?= e($user['full_name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="full_name_en"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Full Name (AR)</label>
                            <input type="text" name="full_name_ar" class="form-control" value="<?= e($user['full_name_ar'] ?? '') ?>">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Password <?= $user ? '' : '<span class="text-danger">*</span>' ?></label>
                            <input type="password" name="password" class="form-control" <?= $user ? '' : 'required' ?> minlength="8">
                            <small class="text-muted"><?= $user ? 'Leave blank to keep current password' : 'Minimum 8 characters' ?></small>
                            <div class="invalid-feedback" data-field="password"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Phone</label>
                            <input type="text" name="phone" class="form-control" value="<?= e($user['phone'] ?? '') ?>">
                        </div>
                        <div class="col-12">
                            <label class="form-label">Roles</label>
                            <div class="row">
                                <?php foreach ($roles as $role): ?>
                                <div class="col-md-4">
                                    <div class="form-check">
                                        <input class="form-check-input" type="checkbox" name="roles[]" value="<?= $role['id'] ?>" id="role-<?= $role['id'] ?>"
                                            <?= in_array($role['id'], $userRoles) ? 'checked' : '' ?>>
                                        <label class="form-check-label" for="role-<?= $role['id'] ?>"><?= e($role['role_name_en']) ?></label>
                                    </div>
                                </div>
                                <?php endforeach; ?>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="form-check form-switch">
                                <input class="form-check-input" type="checkbox" name="is_active" value="1" id="is_active" <?= ($user['is_active'] ?? 1) ? 'checked' : '' ?>>
                                <label class="form-check-label" for="is_active">Active</label>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="form-check form-switch">
                                <input class="form-check-input" type="checkbox" name="must_change_password" value="1" id="must_change_password" <?= ($user['must_change_password'] ?? 0) ? 'checked' : '' ?>>
                                <label class="form-check-label" for="must_change_password">Force Password Change</label>
                            </div>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button>
                        <a href="/admin/users" class="btn btn-secondary">Cancel</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\admin\views\users\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-user-shield me-2"></i><?= lang('admin.users') ?></h4>
    <?php if (Gate::can('users.manage')): ?>
    <a href="/admin/users/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i>Create User</a>
    <?php endif; ?>
</div>
<div class="card">
    <div class="card-body">
        <table id="users-table" class="table table-striped table-hover w-100"
               data-url="/admin/users/datatable"
               data-columns='[{"data":"id"},{"data":"username"},{"data":"full_name"},{"data":"email"},{"data":"roles"},{"data":"status"},{"data":"last_login"},{"data":"actions","orderable":false,"searchable":false}]'>
            <thead>
                <tr>
                    <th>#</th><th>Username</th><th>Name</th><th>Email</th><th>Roles</th><th>Status</th><th>Last Login</th><th>Actions</th>
                </tr>
            </thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = DataTableHelper.init('users-table', {order:[[0,'desc']]});
    $(document).on('click','.btn-unlock',function(){
        const id = $(this).data('id');
        if(!confirm('Unlock this user?')) return;
        $.post('/admin/users/'+id+'/unlock', {_token:'<?= csrf_token() ?>'}, function(r){
            if(r.success) { FormHelper.showToast('success',r.message); DataTableHelper.reload('users-table'); }
            else FormHelper.showToast('error',r.message);
        },'json');
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\admin\views\users\sessions.php

```php
<div class="card">
    <div class="card-header d-flex justify-content-between">
        <h5 class="mb-0"><i class="fas fa-desktop me-2"></i>Sessions for <?= e($targetUser['full_name_en']) ?></h5>
        <a href="/admin/users" class="btn btn-sm btn-secondary">Back</a>
    </div>
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-sm table-striped">
                <thead><tr><th>Started</th><th>IP</th><th>Active</th><th>Expires</th><th>Reason</th></tr></thead>
                <tbody>
                <?php foreach ($sessions as $s): ?>
                <tr>
                    <td><?= format_datetime($s['started_at']) ?></td>
                    <td><?= e($s['ip_address']) ?></td>
                    <td><?= $s['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Ended</span>' ?></td>
                    <td><?= format_datetime($s['expires_at']) ?></td>
                    <td><?= e($s['terminated_reason'] ?? '-') ?></td>
                </tr>
                <?php endforeach; ?>
                <?php if (empty($sessions)): ?><tr><td colspan="5" class="text-center text-muted">No sessions found</td></tr><?php endif; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>
```

## File: modules\admin\views\widgets\data_quality.php

```php
<?php
$db = Database::getInstance();
$openIssues = (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues WHERE status = 'open'");
$criticalIssues = (int)$db->fetchColumn("SELECT COUNT(*) FROM data_quality_issues WHERE status = 'open' AND severity IN ('error','critical')");
$warningIssues = $openIssues - $criticalIssues;
?>
<div class="card h-100">
    <div class="card-header"><h6 class="mb-0"><i class="fas fa-check-double me-2 text-warning"></i>Data Quality</h6></div>
    <div class="card-body">
        <div class="row text-center">
            <div class="col-4"><h4 class="mb-0 text-warning"><?= $openIssues ?></h4><small class="text-muted">Open Issues</small></div>
            <div class="col-4"><h4 class="mb-0 text-danger"><?= $criticalIssues ?></h4><small class="text-muted">Critical</small></div>
            <div class="col-4"><h4 class="mb-0 text-info"><?= $warningIssues ?></h4><small class="text-muted">Warnings</small></div>
        </div>
        <div class="mt-3 text-center"><a href="/admin/data-quality" class="btn btn-sm btn-outline-primary">View Details</a></div>
    </div>
</div>
```

## File: modules\admin\views\widgets\system_health.php

```php
<?php
$db = Database::getInstance();
$activeUsers = (int)$db->fetchColumn("SELECT COUNT(*) FROM auth_sessions WHERE is_active = 1");
$errorsToday = (int)$db->fetchColumn("SELECT COUNT(*) FROM system_error_log WHERE DATE(created_at) = CURDATE()");
$activeMembers = (int)$db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0");
?>
<div class="card h-100">
    <div class="card-header"><h6 class="mb-0"><i class="fas fa-heartbeat me-2 text-success"></i>System Health</h6></div>
    <div class="card-body">
        <div class="row text-center">
            <div class="col-4"><h4 class="mb-0 text-primary"><?= $activeUsers ?></h4><small class="text-muted">Active Sessions</small></div>
            <div class="col-4"><h4 class="mb-0 text-<?= $errorsToday > 0 ? 'danger' : 'success' ?>"><?= $errorsToday ?></h4><small class="text-muted">Errors Today</small></div>
            <div class="col-4"><h4 class="mb-0 text-success"><?= number_format($activeMembers) ?></h4><small class="text-muted">Active Members</small></div>
        </div>
    </div>
</div>
```

## File: modules\advanced_finance\Controllers\AdvFinanceApiController.php

```php
<?php

class AdvFinanceApiController extends Controller
{
    public function memberSearch(Request $request): void
    {
        $q = trim($request->get('q', ''));
        if (mb_strlen($q) < 2) {
            $this->json(['results' => [], 'pagination' => ['more' => false]]);
            return;
        }
        $db = $this->db();
        $like = "%{$q}%";
        $rows = $db->fetchAll(
            "SELECT id, membership_number, full_name_en, full_name_ar, primary_phone
             FROM members
             WHERE is_deleted = 0
               AND (membership_number LIKE ? OR full_name_en LIKE ? OR full_name_ar LIKE ? OR national_id LIKE ? OR primary_phone LIKE ?)
             ORDER BY full_name_en
             LIMIT 30",
            [$like, $like, $like, $like, $like]
        );
        $results = [];
        foreach ($rows as $r) {
            $results[] = [
                'id'   => $r['id'],
                'text'  => $r['membership_number'] . ' â€” ' . ($r['full_name_en'] ?: $r['full_name_ar']),
            ];
        }
        $this->json(['results' => $results, 'pagination' => ['more' => false]]);
    }

    public function invoiceSearch(Request $request): void
    {
        $memberId = (int)$request->get('member_id', 0);
        $q = trim($request->get('q', ''));
        $db = $this->db();
        $params = [];
        $where = "i.is_deleted = 0 AND i.status IN ('issued','partially_paid','overdue')";
        if ($memberId) {
            $where .= " AND i.member_id = ?";
            $params[] = $memberId;
        }
        if ($q) {
            $where .= " AND (i.invoice_number LIKE ?)";
            $params[] = "%{$q}%";
        }
        $rows = $db->fetchAll(
            "SELECT i.id, i.invoice_number, i.total_amount, i.balance_due, i.invoice_type,
                    m.membership_number, m.full_name_en
             FROM invoices i
             JOIN members m ON i.member_id = m.id
             WHERE {$where}
             ORDER BY i.invoice_date DESC
             LIMIT 30",
            $params
        );
        $results = [];
        foreach ($rows as $r) {
            $results[] = [
                'id'   => $r['id'],
                'text'  => $r['invoice_number'] . ' â€” ' . money($r['balance_due']) . ' due',
                'balance_due' => $r['balance_due'],
                'total_amount' => $r['total_amount'],
            ];
        }
        $this->json(['results' => $results, 'pagination' => ['more' => false]]);
    }
}
```

## File: modules\advanced_finance\Controllers\BankAccountController.php

```php
<?php

class BankAccountController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('bank-accounts/index', [
            'title' => lang('adv_finance.bank_accounts'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.bank_accounts')]],
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1'; $params = [];
        if ($search) {
            $where .= ' AND (ba.account_code LIKE ? OR ba.bank_name_en LIKE ? OR ba.account_number LIKE ?)';
            $like = "%{$search}%"; $params = [$like,$like,$like];
        }
        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM bank_accounts ba");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM bank_accounts ba WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT ba.*, c.currency_code FROM bank_accounts ba LEFT JOIN sys_currencies c ON ba.currency_id=c.id WHERE {$where} ORDER BY ba.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'], 'account_code' => $r['account_code'],
                'bank_name' => $r['bank_name_en'], 'account_number' => $r['account_number'],
                'iban' => $r['iban'] ?? 'â€”', 'account_type' => $r['account_type'],
                'current_balance' => format_money($r['current_balance']),
                'is_active' => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $currencies = $this->db()->fetchAll("SELECT * FROM sys_currencies WHERE is_active = 1");
        $this->view('bank-accounts/form', [
            'title' => lang('common.create') . ' ' . lang('adv_finance.bank_accounts'),
            'breadcrumbs' => [['label' => lang('adv_finance.bank_accounts'), 'url' => '/advanced-finance/bank-accounts'], ['label' => lang('common.create')]],
            'account' => null, 'currencies' => $currencies,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'account_code' => 'required|string|max:30|unique:bank_accounts,account_code',
            'bank_name_en' => 'required|string|max:200', 'bank_name_ar' => 'required|string|max:200',
            'account_name' => 'required|string|max:200', 'account_number' => 'required|string|max:50',
            'iban' => 'nullable|string|max:50', 'swift_code' => 'nullable|string|max:20',
            'branch' => 'nullable|string|max:200', 'currency_id' => 'required|integer|exists:sys_currencies,id',
            'account_type' => 'required|in:current,savings,deposit',
            'opening_balance' => 'required|decimal|min:0',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $d['current_balance'] = $d['opening_balance'];
        $d['is_active'] = 1;
        $id = $this->db()->insert('bank_accounts', $d);
        $this->audit('create', 'bank_accounts', $id, 'Created bank account ' . $d['account_code']);
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/bank-accounts']);
    }

    public function edit(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $account = $this->db()->fetchOne("SELECT * FROM bank_accounts WHERE id = ?", [$id]);
        if (!$account) { $this->abort(404, 'Not found'); return; }
        $currencies = $this->db()->fetchAll("SELECT * FROM sys_currencies WHERE is_active = 1");
        $this->view('bank-accounts/form', [
            'title' => lang('common.edit') . ' ' . $account['account_code'],
            'breadcrumbs' => [['label' => lang('adv_finance.bank_accounts'), 'url' => '/advanced-finance/bank-accounts'], ['label' => lang('common.edit')]],
            'account' => $account, 'currencies' => $currencies,
        ], 'app');
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'account_code' => "required|string|max:30|unique:bank_accounts,account_code,{$id}",
            'bank_name_en' => 'required|string|max:200', 'bank_name_ar' => 'required|string|max:200',
            'account_name' => 'required|string|max:200', 'account_number' => 'required|string|max:50',
            'iban' => 'nullable|string|max:50', 'swift_code' => 'nullable|string|max:20',
            'branch' => 'nullable|string|max:200', 'currency_id' => 'required|integer|exists:sys_currencies,id',
            'account_type' => 'required|in:current,savings,deposit',
            'is_active' => 'nullable|boolean',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $d['is_active'] = !empty($d['is_active']) ? 1 : 0;
        $this->db()->update('bank_accounts', $d, 'id = ?', [$id]);
        $this->audit('update', 'bank_accounts', $id, 'Updated bank account');
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/bank-accounts']);
    }
}
```

## File: modules\advanced_finance\Controllers\BankDepositController.php

```php
<?php

class BankDepositController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('deposits/index', [
            'title' => lang('adv_finance.deposits'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.deposits')]],
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1'; $params = [];
        if ($search) { $where .= ' AND (bd.deposit_number LIKE ? OR ba.bank_name_en LIKE ?)'; $like = "%{$search}%"; $params = [$like,$like]; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM bank_deposits bd JOIN bank_accounts ba ON bd.bank_account_id=ba.id");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM bank_deposits bd JOIN bank_accounts ba ON bd.bank_account_id=ba.id WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT bd.*, ba.bank_name_en FROM bank_deposits bd JOIN bank_accounts ba ON bd.bank_account_id=ba.id WHERE {$where} ORDER BY bd.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'], 'deposit_number' => $r['deposit_number'],
                'bank_name' => $r['bank_name_en'], 'deposit_date' => format_date($r['deposit_date']),
                'total_amount' => format_money($r['total_amount']),
                'cash_amount' => format_money($r['cash_amount']), 'check_amount' => format_money($r['check_amount']),
                'status' => $r['status'], 'status_badge' => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $bankAccounts = $db->fetchAll("SELECT * FROM bank_accounts WHERE is_active = 1 ORDER BY bank_name_en");
        $receivedChecks = $db->fetchAll("SELECT * FROM checks WHERE status = 'received' ORDER BY check_date");
        $this->view('deposits/create', [
            'title' => lang('common.create') . ' ' . lang('adv_finance.deposits'),
            'breadcrumbs' => [['label' => lang('adv_finance.deposits'), 'url' => '/advanced-finance/deposits'], ['label' => lang('common.create')]],
            'bankAccounts' => $bankAccounts, 'receivedChecks' => $receivedChecks,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'bank_account_id' => 'required|integer|exists:bank_accounts,id',
            'deposit_date'    => 'required|date',
            'cash_amount'     => 'required|decimal|min:0',
            'notes'           => 'nullable|string|max:500',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $db = $this->db();

        $checkIds = $request->post('check_ids', []);
        if (!is_array($checkIds)) $checkIds = [];
        $checkTotal = 0;
        $checkCount = 0;

        if (!empty($checkIds)) {
            $placeholders = implode(',', array_fill(0, count($checkIds), '?'));
            $checks = $db->fetchAll("SELECT id, amount FROM checks WHERE id IN ({$placeholders}) AND status = 'received'", $checkIds);
            $checkCount = count($checks);
            foreach ($checks as $ch) { $checkTotal += (float)$ch['amount']; }
        }

        $totalAmount = (float)$d['cash_amount'] + $checkTotal;
        $depNumber = SequenceGenerator::next('deposit_number');

        try {
            $db->beginTransaction();
            $depId = $db->insert('bank_deposits', [
                'deposit_number'  => $depNumber,
                'bank_account_id' => $d['bank_account_id'],
                'deposit_date'    => $d['deposit_date'],
                'total_amount'    => $totalAmount,
                'cash_amount'     => $d['cash_amount'],
                'check_amount'    => $checkTotal,
                'check_count'     => $checkCount,
                'status'          => 'prepared',
                'notes'           => $d['notes'] ?? null,
                'created_by'      => Auth::id(),
            ]);

            if ((float)$d['cash_amount'] > 0) {
                $db->insert('bank_deposit_items', [
                    'deposit_id' => $depId, 'item_type' => 'cash', 'amount' => $d['cash_amount'],
                ]);
            }

            foreach ($checkIds as $cid) {
                $ch = $db->fetchOne("SELECT id, amount FROM checks WHERE id = ? AND status = 'received'", [$cid]);
                if ($ch) {
                    $db->insert('bank_deposit_items', [
                        'deposit_id' => $depId, 'item_type' => 'check', 'amount' => $ch['amount'], 'check_id' => $ch['id'],
                    ]);
                    $db->update('checks', ['status' => 'deposited', 'deposit_date' => $d['deposit_date'], 'bank_account_id' => $d['bank_account_id']], 'id = ?', [$ch['id']]);
                }
            }

            $db->commit();
            $this->audit('create', 'bank_deposits', $depId, 'Created deposit ' . $depNumber);
            $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/deposits/' . $depId]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Deposit creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create deposit'], 500);
        }
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $dep = $db->fetchOne("SELECT bd.*, ba.bank_name_en FROM bank_deposits bd JOIN bank_accounts ba ON bd.bank_account_id=ba.id WHERE bd.id = ?", [$id]);
        if (!$dep) { $this->abort(404, 'Not found'); return; }
        $items = $db->fetchAll(
            "SELECT bdi.*, c.check_number, c.drawer_name, c.bank_name AS check_bank
             FROM bank_deposit_items bdi
             LEFT JOIN checks c ON bdi.check_id = c.id
             WHERE bdi.deposit_id = ?",
            [$id]
        );
        $this->view('deposits/show', [
            'title' => $dep['deposit_number'],
            'breadcrumbs' => [['label' => lang('adv_finance.deposits'), 'url' => '/advanced-finance/deposits'], ['label' => $dep['deposit_number']]],
            'dep' => $dep, 'items' => $items,
        ], 'app');
    }

    public function confirm(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $dep = $db->fetchOne("SELECT * FROM bank_deposits WHERE id = ? AND status IN ('prepared','deposited')", [$id]);
        if (!$dep) { $this->json(['success' => false, 'message' => 'Cannot confirm'], 422); return; }

        $bankRef = $request->post('bank_reference', '');
        $db->update('bank_deposits', [
            'status' => 'confirmed', 'confirmed_by' => Auth::id(),
            'confirmed_at' => date('Y-m-d H:i:s'), 'bank_reference' => $bankRef ?: null,
        ], 'id = ?', [$id]);

        // Update bank account balance
        $db->execute(
            "UPDATE bank_accounts SET current_balance = current_balance + ? WHERE id = ?",
            [$dep['total_amount'], $dep['bank_account_id']]
        );

        $this->audit('update', 'bank_deposits', $id, 'Confirmed deposit');
        $this->json(['success' => true, 'message' => lang('adv_finance.confirmed'), 'redirect' => '/advanced-finance/deposits/' . $id]);
    }
}
```

## File: modules\advanced_finance\Controllers\BudgetController.php

```php
<?php

class BudgetController extends Controller
{
    public function index(Request $request): void
    {
        $fiscalYears = $this->db()->fetchAll("SELECT * FROM fiscal_years ORDER BY start_date DESC");
        $this->view('budgets/index', [
            'title' => lang('adv_finance.budgets'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.budgets')]],
            'fiscalYears' => $fiscalYears,
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $yearFilter = (int)$request->get('fiscal_year_id', 0);

        $where = '1=1'; $params = [];
        if ($yearFilter) { $where .= ' AND bh.fiscal_year_id = ?'; $params[] = $yearFilter; }
        if ($search) { $where .= ' AND (ga.account_code LIKE ? OR ga.name_en LIKE ?)'; $like = "%{$search}%"; $params = array_merge($params, [$like,$like]); }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM budget_heads bh JOIN gl_accounts ga ON bh.account_id=ga.id JOIN fiscal_years fy ON bh.fiscal_year_id=fy.id");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM budget_heads bh JOIN gl_accounts ga ON bh.account_id=ga.id JOIN fiscal_years fy ON bh.fiscal_year_id=fy.id WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT bh.*, ga.account_code, ga.name_en AS account_name, fy.year_code,
                    d.name_en AS dept_name
             FROM budget_heads bh
             JOIN gl_accounts ga ON bh.account_id = ga.id
             JOIN fiscal_years fy ON bh.fiscal_year_id = fy.id
             LEFT JOIN departments d ON bh.department_id = d.id
             WHERE {$where}
             ORDER BY bh.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'], 'year_code' => $r['year_code'],
                'account_code' => $r['account_code'], 'account_name' => $r['account_name'],
                'dept_name' => $r['dept_name'] ?? 'â€”',
                'annual_budget' => format_money($r['annual_budget']),
                'actual_spent' => format_money($r['actual_spent']),
                'variance' => format_money($r['annual_budget'] - $r['actual_spent']),
                'status' => $r['status'], 'status_badge' => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $accounts = $db->fetchAll("SELECT id, account_code, name_en FROM gl_accounts WHERE is_active = 1 AND is_postable = 1 AND account_type = 'expense' ORDER BY account_code");
        $fiscalYears = $db->fetchAll("SELECT * FROM fiscal_years ORDER BY start_date DESC LIMIT 5");
        $departments = $db->fetchAll("SELECT * FROM departments WHERE is_active = 1 ORDER BY name_en");

        $this->view('budgets/form', [
            'title' => lang('common.create') . ' ' . lang('adv_finance.budgets'),
            'breadcrumbs' => [['label' => lang('adv_finance.budgets'), 'url' => '/advanced-finance/budgets'], ['label' => lang('common.create')]],
            'budget' => null, 'accounts' => $accounts, 'fiscalYears' => $fiscalYears, 'departments' => $departments,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'fiscal_year_id' => 'required|integer|exists:fiscal_years,id',
            'account_id'     => 'required|integer|exists:gl_accounts,id',
            'department_id'  => 'nullable|integer',
            'annual_budget'  => 'required|decimal|min:0',
            'q1_budget'      => 'required|decimal|min:0',
            'q2_budget'      => 'required|decimal|min:0',
            'q3_budget'      => 'required|decimal|min:0',
            'q4_budget'      => 'required|decimal|min:0',
            'notes'          => 'nullable|string|max:2000',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $d['status'] = 'draft';
        $d['variance'] = $d['annual_budget'];
        $id = $this->db()->insert('budget_heads', $d);
        $this->audit('create', 'budget_heads', $id, 'Created budget');
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/budgets']);
    }

    public function edit(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $budget = $db->fetchOne("SELECT * FROM budget_heads WHERE id = ?", [$id]);
        if (!$budget) { $this->abort(404, 'Not found'); return; }
        $accounts = $db->fetchAll("SELECT id, account_code, name_en FROM gl_accounts WHERE is_active = 1 AND is_postable = 1 AND account_type = 'expense' ORDER BY account_code");
        $fiscalYears = $db->fetchAll("SELECT * FROM fiscal_years ORDER BY start_date DESC LIMIT 5");
        $departments = $db->fetchAll("SELECT * FROM departments WHERE is_active = 1 ORDER BY name_en");

        $this->view('budgets/form', [
            'title' => lang('common.edit') . ' ' . lang('adv_finance.budgets'),
            'breadcrumbs' => [['label' => lang('adv_finance.budgets'), 'url' => '/advanced-finance/budgets'], ['label' => lang('common.edit')]],
            'budget' => $budget, 'accounts' => $accounts, 'fiscalYears' => $fiscalYears, 'departments' => $departments,
        ], 'app');
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'annual_budget' => 'required|decimal|min:0',
            'q1_budget' => 'required|decimal|min:0', 'q2_budget' => 'required|decimal|min:0',
            'q3_budget' => 'required|decimal|min:0', 'q4_budget' => 'required|decimal|min:0',
            'notes' => 'nullable|string|max:2000',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $budget = $this->db()->fetchOne("SELECT actual_spent FROM budget_heads WHERE id = ?", [$id]);
        $d['variance'] = $d['annual_budget'] - ($budget['actual_spent'] ?? 0);
        $this->db()->update('budget_heads', $d, 'id = ?', [$id]);
        $this->audit('update', 'budget_heads', $id, 'Updated budget');
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/budgets']);
    }
}
```

## File: modules\advanced_finance\Controllers\CheckController.php

```php
<?php

class CheckController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $stats = $db->fetchOne(
            "SELECT
                COUNT(*) AS total,
                SUM(CASE WHEN status='received' THEN 1 ELSE 0 END) AS received,
                SUM(CASE WHEN status='deposited' THEN 1 ELSE 0 END) AS deposited,
                SUM(CASE WHEN status='cleared' THEN 1 ELSE 0 END) AS cleared,
                SUM(CASE WHEN status='bounced' THEN 1 ELSE 0 END) AS bounced
             FROM checks"
        );

        $this->view('checks/index', [
            'title' => lang('adv_finance.checks'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.checks')]],
            'stats' => $stats,
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status_filter', '');

        $where = '1=1';
        $params = [];
        if ($statusFilter) { $where .= ' AND c.status = ?'; $params[] = $statusFilter; }
        if ($search) {
            $where .= ' AND (c.check_number LIKE ? OR c.drawer_name LIKE ? OR c.bank_name LIKE ? OR m.full_name_en LIKE ?)';
            $like = "%{$search}%";
            $params = array_merge($params, [$like,$like,$like,$like]);
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM checks c LEFT JOIN members m ON c.member_id=m.id");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM checks c LEFT JOIN members m ON c.member_id=m.id WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT c.*, m.full_name_en AS member_name, m.membership_number,
                    cur.currency_code
             FROM checks c
             LEFT JOIN members m ON c.member_id = m.id
             LEFT JOIN sys_currencies cur ON c.currency_id = cur.id
             WHERE {$where}
             ORDER BY c.id DESC
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'            => $r['id'],
                'check_number'  => $r['check_number'],
                'bank_name'     => $r['bank_name'],
                'drawer_name'   => $r['drawer_name'],
                'amount'        => format_money($r['amount']),
                'check_date'    => format_date($r['check_date']),
                'receive_date'  => format_date($r['receive_date']),
                'member_name'   => $r['member_name'] ?? 'â€”',
                'status'        => $r['status'],
                'status_badge'  => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $currencies = $db->fetchAll("SELECT * FROM sys_currencies WHERE is_active = 1");
        $bankAccounts = $db->fetchAll("SELECT * FROM bank_accounts WHERE is_active = 1");

        $this->view('checks/create', [
            'title' => lang('adv_finance.register_check'),
            'breadcrumbs' => [['label' => lang('adv_finance.checks'), 'url' => '/advanced-finance/checks'], ['label' => lang('adv_finance.register_check')]],
            'currencies' => $currencies,
            'bankAccounts' => $bankAccounts,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'check_number'  => 'required|string|max:50',
            'bank_name'     => 'required|string|max:200',
            'branch'        => 'nullable|string|max:200',
            'account_number'=> 'nullable|string|max:50',
            'drawer_name'   => 'required|string|max:300',
            'amount'        => 'required|decimal|min:0.01',
            'currency_id'   => 'required|integer|exists:sys_currencies,id',
            'check_date'    => 'required|date',
            'receive_date'  => 'required|date',
            'member_id'     => 'nullable|integer',
            'bank_account_id' => 'nullable|integer',
            'notes'         => 'nullable|string|max:500',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $d['status'] = 'received';
        $d['created_by'] = Auth::id();

        $id = $this->db()->insert('checks', $d);
        $this->audit('create', 'checks', $id, 'Registered check #' . $d['check_number']);
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/checks/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $check = $db->fetchOne(
            "SELECT c.*, m.full_name_en AS member_name, m.membership_number, cur.currency_code,
                    ba.bank_name_en AS deposit_bank_name
             FROM checks c
             LEFT JOIN members m ON c.member_id = m.id
             LEFT JOIN sys_currencies cur ON c.currency_id = cur.id
             LEFT JOIN bank_accounts ba ON c.bank_account_id = ba.id
             WHERE c.id = ?",
            [$id]
        );
        if (!$check) { $this->abort(404, 'Not found'); return; }

        $history = $db->fetchAll("SELECT * FROM check_status_history WHERE check_id = ? ORDER BY changed_at DESC", [$id]);
        $bankAccounts = $db->fetchAll("SELECT * FROM bank_accounts WHERE is_active = 1");

        $this->view('checks/show', [
            'title' => lang('adv_finance.checks') . ' â€” ' . $check['check_number'],
            'breadcrumbs' => [['label' => lang('adv_finance.checks'), 'url' => '/advanced-finance/checks'], ['label' => $check['check_number']]],
            'check' => $check,
            'history' => $history,
            'bankAccounts' => $bankAccounts,
        ], 'app');
    }

    public function updateStatus(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $check = $db->fetchOne("SELECT * FROM checks WHERE id = ?", [$id]);
        if (!$check) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $newStatus = $request->post('new_status', '');
        $allowed = [
            'received'  => ['deposited','cancelled','returned'],
            'deposited' => ['cleared','bounced'],
            'bounced'   => ['replaced','received'],
        ];

        $currentAllowed = $allowed[$check['status']] ?? [];
        if (!in_array($newStatus, $currentAllowed)) {
            $this->json(['success' => false, 'message' => 'Invalid status transition from ' . $check['status'] . ' to ' . $newStatus], 422);
            return;
        }

        $updates = ['status' => $newStatus, 'updated_at' => date('Y-m-d H:i:s')];

        if ($newStatus === 'deposited') {
            $updates['deposit_date'] = $request->post('deposit_date', date('Y-m-d'));
            if ($request->post('bank_account_id')) {
                $updates['bank_account_id'] = (int)$request->post('bank_account_id');
            }
        } elseif ($newStatus === 'cleared') {
            $updates['clearance_date'] = $request->post('clearance_date', date('Y-m-d'));
        } elseif ($newStatus === 'bounced') {
            $updates['bounce_date'] = date('Y-m-d');
            $updates['bounce_reason'] = $request->post('bounce_reason', '');
            $updates['bounce_fee'] = (float)$request->post('bounce_fee', 0);
        }

        $notes = $request->post('notes', '');
        if ($notes) { $updates['notes'] = $notes; }

        $db->update('checks', $updates, 'id = ?', [$id]);
        $this->audit('update', 'checks', $id, "Check status changed from {$check['status']} to {$newStatus}");
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated'), 'redirect' => '/advanced-finance/checks/' . $id]);
    }
}
```

## File: modules\advanced_finance\Controllers\CreditNoteController.php

```php
<?php

class CreditNoteController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('credit-notes/index', [
            'title' => lang('adv_finance.credit_notes'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.credit_notes')]],
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= ' AND (cn.credit_note_number LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ?)';
            $like = "%{$search}%";
            $params = [$like,$like,$like];
        }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM credit_notes cn JOIN members m ON cn.member_id=m.id");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM credit_notes cn JOIN members m ON cn.member_id=m.id WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT cn.*, m.full_name_en AS member_name, m.membership_number
             FROM credit_notes cn JOIN members m ON cn.member_id=m.id
             WHERE {$where} ORDER BY cn.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'],
                'credit_note_number' => $r['credit_note_number'],
                'member_name' => $r['member_name'],
                'credit_date' => format_date($r['credit_date']),
                'total_amount' => format_money($r['total_amount']),
                'applied_amount' => format_money($r['applied_amount']),
                'remaining_amount' => format_money($r['remaining_amount']),
                'status' => $r['status'],
                'status_badge' => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $currencies = $this->db()->fetchAll("SELECT * FROM sys_currencies WHERE is_active = 1");
        $this->view('credit-notes/create', [
            'title' => lang('adv_finance.create_credit_note'),
            'breadcrumbs' => [['label' => lang('adv_finance.credit_notes'), 'url' => '/advanced-finance/credit-notes'], ['label' => lang('common.create')]],
            'currencies' => $currencies,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'member_id'         => 'required|integer|exists:members,id',
            'original_invoice_id' => 'nullable|integer',
            'total_amount'      => 'required|decimal|min:0.01',
            'reason'            => 'required|string|max:2000',
            'notes'             => 'nullable|string|max:2000',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $db = $this->db();

        $currency = $db->fetchOne("SELECT id FROM sys_currencies WHERE is_default = 1 LIMIT 1");

        $cnNumber = SequenceGenerator::next('credit_note_number');
        $id = $db->insert('credit_notes', [
            'credit_note_number' => $cnNumber,
            'member_id'          => $d['member_id'],
            'original_invoice_id'=> $d['original_invoice_id'] ?: null,
            'credit_date'        => date('Y-m-d'),
            'total_amount'       => $d['total_amount'],
            'applied_amount'     => 0,
            'remaining_amount'   => $d['total_amount'],
            'currency_id'        => $currency['id'] ?? 1,
            'reason'             => $d['reason'],
            'status'             => 'draft',
            'notes'              => $d['notes'] ?? null,
            'created_by'         => Auth::id(),
        ]);

        // Add a single item for the full amount
        $db->insert('credit_note_items', [
            'credit_note_id'  => $id,
            'description_en'  => $d['reason'],
            'amount'          => $d['total_amount'],
            'original_invoice_item_id' => null,
        ]);

        $this->audit('create', 'credit_notes', $id, 'Created credit note ' . $cnNumber);
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/credit-notes/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $cn = $db->fetchOne(
            "SELECT cn.*, m.full_name_en AS member_name, m.membership_number,
                    i.invoice_number AS original_invoice_number
             FROM credit_notes cn
             JOIN members m ON cn.member_id = m.id
             LEFT JOIN invoices i ON cn.original_invoice_id = i.id
             WHERE cn.id = ?",
            [$id]
        );
        if (!$cn) { $this->abort(404, 'Not found'); return; }

        $items = $db->fetchAll("SELECT * FROM credit_note_items WHERE credit_note_id = ?", [$id]);
        $outstandingInvoices = $db->fetchAll(
            "SELECT id, invoice_number, balance_due FROM invoices WHERE member_id = ? AND status IN ('issued','partially_paid','overdue') AND balance_due > 0 AND is_deleted = 0",
            [$cn['member_id']]
        );

        $this->view('credit-notes/show', [
            'title' => $cn['credit_note_number'],
            'breadcrumbs' => [['label' => lang('adv_finance.credit_notes'), 'url' => '/advanced-finance/credit-notes'], ['label' => $cn['credit_note_number']]],
            'cn' => $cn,
            'items' => $items,
            'outstandingInvoices' => $outstandingInvoices,
        ], 'app');
    }

    public function issue(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $cn = $db->fetchOne("SELECT * FROM credit_notes WHERE id = ? AND status = 'draft'", [$id]);
        if (!$cn) { $this->json(['success' => false, 'message' => 'Cannot issue â€” not in draft'], 422); return; }

        $db->update('credit_notes', ['status' => 'issued', 'approved_by' => Auth::id(), 'approved_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'credit_notes', $id, 'Issued credit note');
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated'), 'redirect' => '/advanced-finance/credit-notes/' . $id]);
    }

    public function apply(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $invoiceId = (int)$request->post('invoice_id', 0);
        $applyAmount = (float)$request->post('apply_amount', 0);

        $db = $this->db();
        $cn = $db->fetchOne("SELECT * FROM credit_notes WHERE id = ? AND status IN ('issued','partially_applied')", [$id]);
        if (!$cn) { $this->json(['success' => false, 'message' => 'Credit note not available for application'], 422); return; }

        if ($applyAmount <= 0 || $applyAmount > $cn['remaining_amount']) {
            $this->json(['success' => false, 'message' => 'Invalid amount. Remaining: ' . format_money($cn['remaining_amount'])], 422);
            return;
        }

        $invoice = $db->fetchOne("SELECT * FROM invoices WHERE id = ? AND is_deleted = 0", [$invoiceId]);
        if (!$invoice || $invoice['balance_due'] <= 0) {
            $this->json(['success' => false, 'message' => 'Invoice not found or fully paid'], 422);
            return;
        }

        $actualApply = min($applyAmount, (float)$invoice['balance_due']);

        try {
            $db->beginTransaction();

            // Update invoice
            $db->update('invoices', [
                'paid_amount' => $invoice['paid_amount'] + $actualApply,
                'updated_at' => date('Y-m-d H:i:s'),
            ], 'id = ?', [$invoiceId]);

            // Update credit note
            $newApplied = $cn['applied_amount'] + $actualApply;
            $newRemaining = $cn['total_amount'] - $newApplied;
            $newStatus = $newRemaining <= 0.001 ? 'fully_applied' : 'partially_applied';

            $db->update('credit_notes', [
                'applied_amount'   => $newApplied,
                'remaining_amount' => max(0, $newRemaining),
                'status'           => $newStatus,
                'updated_at'       => date('Y-m-d H:i:s'),
            ], 'id = ?', [$id]);

            // Update member balance
            $db->execute("CALL sp_recalculate_member_balance(?)", [$cn['member_id']]);

            $db->commit();
            $this->audit('update', 'credit_notes', $id, "Applied " . format_money($actualApply) . " to invoice #{$invoice['invoice_number']}");
            $this->json(['success' => true, 'message' => lang('adv_finance.applied_success'), 'redirect' => '/advanced-finance/credit-notes/' . $id]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Credit note application failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Application failed'], 500);
        }
    }
}
```

## File: modules\advanced_finance\Controllers\GLController.php

```php
<?php

class GLController extends Controller
{
    public function accounts(Request $request): void
    {
        $db = $this->db();
        $accounts = $db->fetchAll("SELECT * FROM gl_accounts WHERE is_active = 1 ORDER BY account_code");
        $tree = $this->buildTree($accounts);

        $this->view('gl/accounts', [
            'title' => lang('adv_finance.chart_of_accounts'),
            'breadcrumbs' => [['label' => lang('adv_finance.gl'), 'url' => '/advanced-finance/gl'], ['label' => lang('adv_finance.chart_of_accounts')]],
            'accounts' => $accounts,
            'tree' => $tree,
        ], 'app');
    }

    private function buildTree(array $accounts, ?int $parentId = null): array
    {
        $branch = [];
        foreach ($accounts as $a) {
            if ($a['parent_id'] == $parentId) {
                $a['children'] = $this->buildTree($accounts, $a['id']);
                $branch[] = $a;
            }
        }
        return $branch;
    }

    public function accountsDatatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 50);
        $search = $request->get('search')['value'] ?? '';

        $where = 'is_active = 1'; $params = [];
        if ($search) { $where .= ' AND (account_code LIKE ? OR name_en LIKE ? OR name_ar LIKE ?)'; $like = "%{$search}%"; $params = [$like,$like,$like]; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM gl_accounts WHERE is_active = 1");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM gl_accounts WHERE {$where}", $params);
        $rows = $db->fetchAll("SELECT * FROM gl_accounts WHERE {$where} ORDER BY account_code LIMIT {$length} OFFSET {$start}", $params);

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'], 'account_code' => $r['account_code'],
                'name_en' => $r['name_en'], 'name_ar' => $r['name_ar'],
                'account_type' => ucfirst($r['account_type']), 'normal_balance' => ucfirst($r['normal_balance']),
                'is_header' => $r['is_header'] ? '<span class="badge bg-info">Header</span>' : '<span class="badge bg-secondary">Postable</span>',
                'level' => $r['level'],
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function journalEntries(Request $request): void
    {
        $this->view('gl/journal-entries', [
            'title' => lang('adv_finance.journal_entries'),
            'breadcrumbs' => [['label' => lang('adv_finance.gl'), 'url' => '/advanced-finance/gl'], ['label' => lang('adv_finance.journal_entries')]],
        ], 'app');
    }

    public function journalEntriesDatatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1'; $params = [];
        if ($search) { $where .= ' AND (je.journal_number LIKE ? OR je.description_en LIKE ?)'; $like = "%{$search}%"; $params = [$like,$like]; }
        $sf = $request->get('status_filter', '');
        if ($sf) { $where .= ' AND je.status = ?'; $params[] = $sf; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM gl_journal_entries je");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM gl_journal_entries je WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT je.* FROM gl_journal_entries je WHERE {$where} ORDER BY je.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'], 'journal_number' => $r['journal_number'],
                'entry_date' => format_date($r['entry_date']),
                'description' => str_limit($r['description_en'], 60),
                'total_debit' => format_money($r['total_debit']),
                'total_credit' => format_money($r['total_credit']),
                'status' => $r['status'], 'status_badge' => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function createJournalEntry(Request $request): void
    {
        $db = $this->db();
        $accounts = $db->fetchAll("SELECT id, account_code, name_en, name_ar FROM gl_accounts WHERE is_active = 1 AND is_postable = 1 ORDER BY account_code");
        $fiscalYears = $db->fetchAll("SELECT * FROM fiscal_years WHERE status = 'open'");

        $this->view('gl/journal-entry-form', [
            'title' => lang('common.create') . ' ' . lang('adv_finance.journal_entries'),
            'breadcrumbs' => [['label' => lang('adv_finance.journal_entries'), 'url' => '/advanced-finance/gl/journal-entries'], ['label' => lang('common.create')]],
            'accounts' => $accounts, 'fiscalYears' => $fiscalYears, 'entry' => null,
        ], 'app');
    }

    public function storeJournalEntry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'entry_date'     => 'required|date',
            'description_en' => 'required|string|max:500',
            'description_ar' => 'nullable|string|max:500',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();

        $lines = $request->post('lines', []);
        if (!is_array($lines) || count($lines) < 2) {
            $this->json(['success' => false, 'message' => 'At least 2 lines required'], 422);
            return;
        }

        $totalDebit = 0; $totalCredit = 0;
        foreach ($lines as $line) {
            $totalDebit += (float)($line['debit_amount'] ?? 0);
            $totalCredit += (float)($line['credit_amount'] ?? 0);
        }

        if (abs($totalDebit - $totalCredit) > 0.01) {
            $this->json(['success' => false, 'message' => 'Debits must equal credits. Debit: ' . format_money($totalDebit) . ', Credit: ' . format_money($totalCredit)], 422);
            return;
        }

        $db = $this->db();
        $fy = $db->fetchOne("SELECT id FROM fiscal_years WHERE status = 'open' AND start_date <= ? AND end_date >= ? LIMIT 1", [$d['entry_date'], $d['entry_date']]);
        $fp = null;
        if ($fy) {
            $fp = $db->fetchOne("SELECT id FROM fiscal_periods WHERE fiscal_year_id = ? AND status = 'open' AND start_date <= ? AND end_date >= ? LIMIT 1", [$fy['id'], $d['entry_date'], $d['entry_date']]);
        }

        $jnNumber = SequenceGenerator::next('journal_number');

        try {
            $db->beginTransaction();
            $jeId = $db->insert('gl_journal_entries', [
                'journal_number'  => $jnNumber,
                'entry_date'      => $d['entry_date'],
                'fiscal_year_id'  => $fy['id'] ?? null,
                'fiscal_period_id'=> $fp['id'] ?? null,
                'description_en'  => $d['description_en'],
                'description_ar'  => $d['description_ar'] ?? null,
                'total_debit'     => $totalDebit,
                'total_credit'    => $totalCredit,
                'status'          => 'draft',
                'created_by'      => Auth::id(),
            ]);

            $lineNum = 1;
            foreach ($lines as $line) {
                if (empty($line['account_id'])) continue;
                $db->insert('gl_journal_entry_lines', [
                    'journal_entry_id' => $jeId,
                    'line_number'      => $lineNum++,
                    'account_id'       => (int)$line['account_id'],
                    'description'      => $line['description'] ?? null,
                    'debit_amount'     => (float)($line['debit_amount'] ?? 0),
                    'credit_amount'    => (float)($line['credit_amount'] ?? 0),
                    'member_id'        => !empty($line['member_id']) ? (int)$line['member_id'] : null,
                    'department_id'    => !empty($line['department_id']) ? (int)$line['department_id'] : null,
                ]);
            }

            $db->commit();
            $this->audit('create', 'gl_journal_entries', $jeId, 'Created journal entry ' . $jnNumber);
            $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/gl/journal-entries/' . $jeId]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('JE creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create entry'], 500);
        }
    }

    public function showJournalEntry(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $je = $db->fetchOne("SELECT * FROM gl_journal_entries WHERE id = ?", [$id]);
        if (!$je) { $this->abort(404, 'Not found'); return; }
        $lines = $db->fetchAll(
            "SELECT jel.*, ga.account_code, ga.name_en AS account_name
             FROM gl_journal_entry_lines jel
             JOIN gl_accounts ga ON jel.account_id = ga.id
             WHERE jel.journal_entry_id = ?
             ORDER BY jel.line_number",
            [$id]
        );
        $this->view('gl/journal-entry-show', [
            'title' => $je['journal_number'],
            'breadcrumbs' => [['label' => lang('adv_finance.journal_entries'), 'url' => '/advanced-finance/gl/journal-entries'], ['label' => $je['journal_number']]],
            'je' => $je, 'lines' => $lines,
        ], 'app');
    }

    public function postJournalEntry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $je = $db->fetchOne("SELECT * FROM gl_journal_entries WHERE id = ? AND status = 'draft'", [$id]);
        if (!$je) { $this->json(['success' => false, 'message' => 'Cannot post â€” not draft'], 422); return; }

        $db->update('gl_journal_entries', [
            'status' => 'posted', 'posted_by' => Auth::id(), 'posted_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('update', 'gl_journal_entries', $id, 'Posted journal entry ' . $je['journal_number']);
        $this->json(['success' => true, 'message' => lang('adv_finance.posted'), 'redirect' => '/advanced-finance/gl/journal-entries/' . $id]);
    }

    public function reverseJournalEntry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $je = $db->fetchOne("SELECT * FROM gl_journal_entries WHERE id = ? AND status = 'posted'", [$id]);
        if (!$je) { $this->json(['success' => false, 'message' => 'Cannot reverse â€” not posted'], 422); return; }

        $db->update('gl_journal_entries', [
            'status' => 'reversed', 'reversed_by' => Auth::id(), 'reversed_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('update', 'gl_journal_entries', $id, 'Reversed journal entry ' . $je['journal_number']);
        $this->json(['success' => true, 'message' => lang('adv_finance.reversed'), 'redirect' => '/advanced-finance/gl/journal-entries/' . $id]);
    }

    public function balances(Request $request): void
    {
        $db = $this->db();
        $fiscalYears = $db->fetchAll("SELECT * FROM fiscal_years ORDER BY start_date DESC");
        $selectedYear = (int)$request->get('fiscal_year_id', 0);
        $balances = [];
        if ($selectedYear) {
            $balances = $db->fetchAll(
                "SELECT gab.*, ga.account_code, ga.name_en, ga.name_ar, ga.account_type, fp.name_en AS period_name
                 FROM gl_account_balances gab
                 JOIN gl_accounts ga ON gab.account_id = ga.id
                 JOIN fiscal_periods fp ON gab.fiscal_period_id = fp.id
                 WHERE gab.fiscal_year_id = ?
                 ORDER BY ga.account_code, fp.period_number",
                [$selectedYear]
            );
        }
        $this->view('gl/balances', [
            'title' => lang('adv_finance.balances'),
            'breadcrumbs' => [['label' => lang('adv_finance.gl'), 'url' => '/advanced-finance/gl'], ['label' => lang('adv_finance.balances')]],
            'fiscalYears' => $fiscalYears, 'selectedYear' => $selectedYear, 'balances' => $balances,
        ], 'app');
    }
}
```

## File: modules\advanced_finance\Controllers\InstallmentController.php

```php
<?php

class InstallmentController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $stats = $db->fetchOne(
            "SELECT
                COUNT(*) AS total,
                SUM(CASE WHEN status='active' THEN 1 ELSE 0 END) AS active,
                SUM(CASE WHEN status='defaulted' THEN 1 ELSE 0 END) AS defaulted,
                SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) AS completed,
                COALESCE(SUM(CASE WHEN status='active' THEN total_remaining ELSE 0 END),0) AS total_remaining
             FROM installment_plans"
        );

        $this->view('installments/index', [
            'title' => lang('adv_finance.installments'),
            'breadcrumbs' => [
                ['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'],
                ['label' => lang('adv_finance.installments')],
            ],
            'stats' => $stats,
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = ($request->get('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

        $cols = ['ip.id','ip.plan_number','m.full_name_en','ip.total_amount','ip.total_paid','ip.total_remaining','ip.number_of_installments','ip.status','ip.next_due_date'];
        $orderColumn = $cols[$orderCol] ?? 'ip.id';

        $where = '1=1';
        $params = [];
        $statusFilter = $request->get('status_filter', '');
        if ($statusFilter) {
            $where .= ' AND ip.status = ?';
            $params[] = $statusFilter;
        }
        if ($search) {
            $where .= ' AND (ip.plan_number LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ?)';
            $like = "%{$search}%";
            $params = array_merge($params, [$like, $like, $like]);
        }

        $totalAll = (int) $db->fetchColumn("SELECT COUNT(*) FROM installment_plans ip JOIN members m ON ip.member_id=m.id");
        $totalFiltered = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM installment_plans ip JOIN members m ON ip.member_id=m.id WHERE {$where}",
            $params
        );

        $rows = $db->fetchAll(
            "SELECT ip.*, m.membership_number, m.full_name_en AS member_name, m.primary_phone
             FROM installment_plans ip
             JOIN members m ON ip.member_id = m.id
             WHERE {$where}
             ORDER BY {$orderColumn} {$orderDir}
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $overdue = (int) $db->fetchColumn(
                "SELECT COUNT(*) FROM installment_plan_schedules WHERE plan_id = ? AND status = 'overdue'",
                [$r['id']]
            );
            $data[] = [
                'id'             => $r['id'],
                'plan_number'    => $r['plan_number'],
                'member_name'    => $r['member_name'],
                'membership_no'  => $r['membership_number'],
                'total_amount'   => format_money($r['total_amount']),
                'total_paid'     => format_money($r['total_paid']),
                'total_remaining'=> format_money($r['total_remaining']),
                'installments'   => $r['installments_paid'] . '/' . $r['number_of_installments'],
                'next_due_date'  => format_date($r['next_due_date']),
                'status'         => $r['status'],
                'status_badge'   => status_badge($r['status']),
                'overdue_count'  => $overdue,
            ];
        }

        $this->json([
            'draw' => $draw,
            'recordsTotal' => $totalAll,
            'recordsFiltered' => $totalFiltered,
            'data' => $data,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $templates = $db->fetchAll("SELECT * FROM installment_plan_templates WHERE is_active = 1 ORDER BY name_en");
        $frequencies = ['weekly','biweekly','monthly','bimonthly','quarterly','semi_annual','annual'];

        $this->view('installments/create', [
            'title' => lang('adv_finance.create_plan'),
            'breadcrumbs' => [
                ['label' => lang('adv_finance.installments'), 'url' => '/advanced-finance/installments'],
                ['label' => lang('adv_finance.create_plan')],
            ],
            'templates' => $templates,
            'frequencies' => $frequencies,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'member_id'          => 'required|integer|exists:members,id',
            'invoice_id'         => 'nullable|integer',
            'total_amount'       => 'required|decimal|min:1',
            'down_payment'       => 'required|decimal|min:0',
            'num_installments'   => 'required|integer|min:1|max:120',
            'frequency'          => 'required|in:weekly,biweekly,monthly,bimonthly,quarterly,semi_annual,annual',
            'start_date'         => 'required|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $d = $validator->validated();
        $db = $this->db();

        try {
            $db->execute(
                "CALL sp_create_installment_plan(?, ?, ?, ?, ?, ?, ?, ?, @p_plan_id, @p_plan_number, @p_result)",
                [
                    $d['member_id'],
                    $d['invoice_id'] ?: null,
                    $d['total_amount'],
                    $d['down_payment'],
                    $d['num_installments'],
                    $d['frequency'],
                    $d['start_date'],
                    Auth::id(),
                ]
            );
            $result = $db->fetchOne("SELECT @p_plan_id AS plan_id, @p_plan_number AS plan_number, @p_result AS result_msg");

            if ($result && $result['plan_id']) {
                $this->audit('create', 'installment_plans', (int)$result['plan_id'], 'Created installment plan ' . $result['plan_number']);
                $this->json([
                    'success'  => true,
                    'message'  => lang('adv_finance.saved'),
                    'redirect' => '/advanced-finance/installments/' . $result['plan_id'],
                ]);
            } else {
                $this->json(['success' => false, 'message' => $result['result_msg'] ?? 'Failed to create plan'], 422);
            }
        } catch (\Exception $e) {
            Logger::error('Installment plan creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Server error creating plan'], 500);
        }
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $plan = $db->fetchOne(
            "SELECT ip.*, m.membership_number, m.full_name_en AS member_name, m.full_name_ar AS member_name_ar,
                    m.primary_phone, i.invoice_number
             FROM installment_plans ip
             JOIN members m ON ip.member_id = m.id
             LEFT JOIN invoices i ON ip.invoice_id = i.id
             WHERE ip.id = ?",
            [$id]
        );
        if (!$plan) {
            $this->abort(404, 'Plan not found');
            return;
        }

        $schedules = $db->fetchAll(
            "SELECT * FROM installment_plan_schedules WHERE plan_id = ? ORDER BY installment_number",
            [$id]
        );

        $payments = $db->fetchAll(
            "SELECT ipay.*, ips.installment_number, ips.due_date,
                    p.payment_number, pm.name_en AS method_name
             FROM installment_payments ipay
             JOIN installment_plan_schedules ips ON ipay.schedule_id = ips.id
             JOIN payments p ON ipay.payment_id = p.id
             LEFT JOIN payment_methods pm ON p.payment_method_id = pm.id
             WHERE ips.plan_id = ?
             ORDER BY ipay.payment_date DESC",
            [$id]
        );

        $paymentMethods = $db->fetchAll("SELECT * FROM payment_methods WHERE is_active = 1 ORDER BY sort_order");

        $this->view('installments/show', [
            'title' => lang('adv_finance.installments') . ' â€” ' . $plan['plan_number'],
            'breadcrumbs' => [
                ['label' => lang('adv_finance.installments'), 'url' => '/advanced-finance/installments'],
                ['label' => $plan['plan_number']],
            ],
            'plan' => $plan,
            'schedules' => $schedules,
            'payments' => $payments,
            'paymentMethods' => $paymentMethods,
        ], 'app');
    }

    public function recordPayment(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $planId = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'schedule_id'        => 'required|integer|exists:installment_plan_schedules,id',
            'amount'             => 'required|decimal|min:0.01',
            'late_fee_amount'    => 'nullable|decimal|min:0',
            'payment_method_id'  => 'required|integer|exists:payment_methods,id',
            'payment_date'       => 'required|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $d = $validator->validated();
        $db = $this->db();

        $plan = $db->fetchOne("SELECT * FROM installment_plans WHERE id = ?", [$planId]);
        if (!$plan || $plan['status'] !== 'active') {
            $this->json(['success' => false, 'message' => 'Plan is not active'], 422);
            return;
        }

        $schedule = $db->fetchOne("SELECT * FROM installment_plan_schedules WHERE id = ? AND plan_id = ?", [$d['schedule_id'], $planId]);
        if (!$schedule) {
            $this->json(['success' => false, 'message' => 'Schedule not found for this plan'], 422);
            return;
        }

        try {
            $db->beginTransaction();

            // Create payment record via stored procedure
            $db->execute(
                "CALL sp_record_payment(?, ?, ?, ?, ?, ?, ?, ?, @p_payment_id, @p_payment_number, @p_result)",
                [
                    $plan['member_id'],
                    $d['payment_method_id'],
                    $d['amount'],
                    $d['payment_date'],
                    null,
                    $plan['invoice_id'],
                    Auth::id(),
                    null,
                ]
            );
            $payResult = $db->fetchOne("SELECT @p_payment_id AS payment_id, @p_payment_number AS payment_number, @p_result AS result_msg");

            if (!$payResult || !$payResult['payment_id']) {
                $db->rollBack();
                $this->json(['success' => false, 'message' => $payResult['result_msg'] ?? 'Payment creation failed'], 422);
                return;
            }

            // Record installment payment â€” triggers update schedule and plan via DB trigger
            $db->insert('installment_payments', [
                'schedule_id'     => $d['schedule_id'],
                'payment_id'      => $payResult['payment_id'],
                'amount'          => $d['amount'],
                'late_fee_amount' => $d['late_fee_amount'] ?? 0,
                'payment_date'    => $d['payment_date'],
                'created_by'      => Auth::id(),
            ]);

            $db->commit();
            $this->audit('create', 'installment_payments', $planId, 'Recorded installment payment for plan ' . $plan['plan_number']);
            $this->json(['success' => true, 'message' => lang('adv_finance.payment_recorded'), 'redirect' => '/advanced-finance/installments/' . $planId]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Installment payment failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Payment processing failed'], 500);
        }
    }

    public function markDefaulted(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $plan = $db->fetchOne("SELECT * FROM installment_plans WHERE id = ?", [$id]);
        if (!$plan || $plan['status'] !== 'active') {
            $this->json(['success' => false, 'message' => 'Plan is not active'], 422);
            return;
        }

        $db->update('installment_plans', ['status' => 'defaulted', 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('update', 'installment_plans', $id, 'Marked plan ' . $plan['plan_number'] . ' as defaulted');
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated')]);
    }
}
```

## File: modules\advanced_finance\Controllers\InstallmentTemplateController.php

```php
<?php

class InstallmentTemplateController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('installment-templates/index', [
            'title' => lang('adv_finance.installment_templates'),
            'breadcrumbs' => [
                ['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'],
                ['label' => lang('adv_finance.installment_templates')],
            ],
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= ' AND (template_code LIKE ? OR name_en LIKE ? OR name_ar LIKE ?)';
            $like = "%{$search}%";
            $params = [$like, $like, $like];
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM installment_plan_templates");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM installment_plan_templates WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT * FROM installment_plan_templates WHERE {$where} ORDER BY id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'                     => $r['id'],
                'template_code'          => $r['template_code'],
                'name_en'                => $r['name_en'],
                'name_ar'                => $r['name_ar'],
                'number_of_installments' => $r['number_of_installments'],
                'frequency'              => $r['frequency'],
                'down_payment_pct'       => $r['down_payment_percentage'] . '%',
                'is_active'              => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $this->view('installment-templates/form', [
            'title' => lang('common.create') . ' ' . lang('adv_finance.template'),
            'breadcrumbs' => [
                ['label' => lang('adv_finance.installment_templates'), 'url' => '/advanced-finance/installment-templates'],
                ['label' => lang('common.create')],
            ],
            'template' => null,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'template_code'          => 'required|string|max:30|unique:installment_plan_templates,template_code',
            'name_en'                => 'required|string|max:200',
            'name_ar'                => 'required|string|max:200',
            'number_of_installments' => 'required|integer|min:1|max:120',
            'frequency'              => 'required|in:weekly,biweekly,monthly,bimonthly,quarterly,semi_annual,annual',
            'down_payment_percentage'=> 'required|decimal|min:0|max:100',
            'interest_rate'          => 'required|decimal|min:0|max:100',
            'late_payment_fee'       => 'required|decimal|min:0',
            'grace_days'             => 'required|integer|min:0',
            'is_active'              => 'nullable|boolean',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $d = $validator->validated();
        $d['is_active'] = isset($d['is_active']) ? 1 : 1;
        $id = $this->db()->insert('installment_plan_templates', $d);
        $this->audit('create', 'installment_plan_templates', $id, 'Created installment template ' . $d['template_code']);
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/installment-templates']);
    }

    public function edit(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $template = $this->db()->fetchOne("SELECT * FROM installment_plan_templates WHERE id = ?", [$id]);
        if (!$template) { $this->abort(404, 'Not found'); return; }

        $this->view('installment-templates/form', [
            'title' => lang('common.edit') . ' ' . $template['template_code'],
            'breadcrumbs' => [
                ['label' => lang('adv_finance.installment_templates'), 'url' => '/advanced-finance/installment-templates'],
                ['label' => lang('common.edit')],
            ],
            'template' => $template,
        ], 'app');
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');

        $validator = new Validator($request->all(), [
            'template_code'          => "required|string|max:30|unique:installment_plan_templates,template_code,{$id}",
            'name_en'                => 'required|string|max:200',
            'name_ar'                => 'required|string|max:200',
            'number_of_installments' => 'required|integer|min:1|max:120',
            'frequency'              => 'required|in:weekly,biweekly,monthly,bimonthly,quarterly,semi_annual,annual',
            'down_payment_percentage'=> 'required|decimal|min:0|max:100',
            'interest_rate'          => 'required|decimal|min:0|max:100',
            'late_payment_fee'       => 'required|decimal|min:0',
            'grace_days'             => 'required|integer|min:0',
            'is_active'              => 'nullable|boolean',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $d['is_active'] = !empty($d['is_active']) ? 1 : 0;

        $this->db()->update('installment_plan_templates', $d, 'id = ?', [$id]);
        $this->audit('update', 'installment_plan_templates', $id, 'Updated template');
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/installment-templates']);
    }

    public function delete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $this->db()->update('installment_plan_templates', ['is_active' => 0], 'id = ?', [$id]);
        $this->audit('delete', 'installment_plan_templates', $id, 'Deactivated template');
        $this->json(['success' => true, 'message' => lang('adv_finance.deleted')]);
    }
}
```

## File: modules\advanced_finance\Controllers\RefundController.php

```php
<?php

class RefundController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('refunds/index', [
            'title' => lang('adv_finance.refunds'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.refunds')]],
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= ' AND (r.refund_number LIKE ? OR m.full_name_en LIKE ?)';
            $like = "%{$search}%";
            $params = [$like, $like];
        }
        $sf = $request->get('status_filter', '');
        if ($sf) { $where .= ' AND r.status = ?'; $params[] = $sf; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM refunds r JOIN members m ON r.member_id=m.id");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM refunds r JOIN members m ON r.member_id=m.id WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT r.*, m.full_name_en AS member_name, m.membership_number, pm.name_en AS method_name
             FROM refunds r
             JOIN members m ON r.member_id = m.id
             JOIN payment_methods pm ON r.refund_method_id = pm.id
             WHERE {$where}
             ORDER BY r.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'            => $r['id'],
                'refund_number' => $r['refund_number'],
                'member_name'   => $r['member_name'],
                'refund_amount' => format_money($r['refund_amount']),
                'refund_date'   => format_date($r['refund_date']),
                'method_name'   => $r['method_name'],
                'status'        => $r['status'],
                'status_badge'  => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $methods = $this->db()->fetchAll("SELECT * FROM payment_methods WHERE is_active = 1 ORDER BY sort_order");
        $this->view('refunds/create', [
            'title' => lang('adv_finance.create_refund'),
            'breadcrumbs' => [['label' => lang('adv_finance.refunds'), 'url' => '/advanced-finance/refunds'], ['label' => lang('common.create')]],
            'methods' => $methods,
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'member_id'        => 'required|integer|exists:members,id',
            'refund_amount'    => 'required|decimal|min:0.01',
            'refund_method_id' => 'required|integer|exists:payment_methods,id',
            'reason'           => 'required|string|max:2000',
            'original_payment_id' => 'nullable|integer',
            'original_invoice_id' => 'nullable|integer',
            'notes'            => 'nullable|string|max:2000',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $db = $this->db();

        $currency = $db->fetchOne("SELECT id FROM sys_currencies WHERE is_default = 1 LIMIT 1");
        $refundNumber = SequenceGenerator::next('refund_number');

        $id = $db->insert('refunds', [
            'refund_number'       => $refundNumber,
            'member_id'           => $d['member_id'],
            'original_payment_id' => $d['original_payment_id'] ?: null,
            'original_invoice_id' => $d['original_invoice_id'] ?: null,
            'refund_amount'       => $d['refund_amount'],
            'currency_id'         => $currency['id'] ?? 1,
            'refund_method_id'    => $d['refund_method_id'],
            'refund_date'         => date('Y-m-d'),
            'reason'              => $d['reason'],
            'status'              => 'pending',
            'notes'               => $d['notes'] ?? null,
            'created_by'          => Auth::id(),
        ]);

        $this->audit('create', 'refunds', $id, 'Created refund request ' . $refundNumber);
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/refunds/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $refund = $db->fetchOne(
            "SELECT r.*, m.full_name_en AS member_name, m.membership_number, pm.name_en AS method_name,
                    p.payment_number AS original_payment_number,
                    i.invoice_number AS original_invoice_number
             FROM refunds r
             JOIN members m ON r.member_id = m.id
             JOIN payment_methods pm ON r.refund_method_id = pm.id
             LEFT JOIN payments p ON r.original_payment_id = p.id
             LEFT JOIN invoices i ON r.original_invoice_id = i.id
             WHERE r.id = ?",
            [$id]
        );
        if (!$refund) { $this->abort(404, 'Not found'); return; }

        $this->view('refunds/show', [
            'title' => $refund['refund_number'],
            'breadcrumbs' => [['label' => lang('adv_finance.refunds'), 'url' => '/advanced-finance/refunds'], ['label' => $refund['refund_number']]],
            'refund' => $refund,
        ], 'app');
    }

    public function approve(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $r = $db->fetchOne("SELECT * FROM refunds WHERE id = ? AND status = 'pending'", [$id]);
        if (!$r) { $this->json(['success' => false, 'message' => 'Not pending'], 422); return; }
        $db->update('refunds', ['status' => 'approved', 'approved_by' => Auth::id(), 'approved_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('approve', 'refunds', $id, 'Approved refund');
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated'), 'redirect' => '/advanced-finance/refunds/' . $id]);
    }

    public function process(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $r = $db->fetchOne("SELECT * FROM refunds WHERE id = ? AND status = 'approved'", [$id]);
        if (!$r) { $this->json(['success' => false, 'message' => 'Not approved'], 422); return; }

        $refNum = $request->post('reference_number', '');
        $db->update('refunds', [
            'status' => 'processed',
            'processed_by' => Auth::id(),
            'processed_at' => date('Y-m-d H:i:s'),
            'reference_number' => $refNum ?: null,
        ], 'id = ?', [$id]);

        $db->execute("CALL sp_recalculate_member_balance(?)", [$r['member_id']]);

        $this->audit('update', 'refunds', $id, 'Processed refund');
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated'), 'redirect' => '/advanced-finance/refunds/' . $id]);
    }

    public function reject(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $r = $db->fetchOne("SELECT * FROM refunds WHERE id = ? AND status = 'pending'", [$id]);
        if (!$r) { $this->json(['success' => false, 'message' => 'Not pending'], 422); return; }
        $db->update('refunds', [
            'status' => 'rejected',
            'rejection_reason' => $request->post('rejection_reason', ''),
        ], 'id = ?', [$id]);
        $this->audit('reject', 'refunds', $id, 'Rejected refund');
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated'), 'redirect' => '/advanced-finance/refunds/' . $id]);
    }
}
```

## File: modules\advanced_finance\Controllers\WriteOffController.php

```php
<?php

class WriteOffController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('write-offs/index', [
            'title' => lang('adv_finance.write_offs'),
            'breadcrumbs' => [['label' => lang('adv_finance.title'), 'url' => '/advanced-finance/installments'], ['label' => lang('adv_finance.write_offs')]],
        ], 'app');
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1'; $params = [];
        if ($search) { $where .= ' AND (w.write_off_number LIKE ? OR m.full_name_en LIKE ?)'; $like = "%{$search}%"; $params = [$like,$like]; }

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM write_offs w JOIN members m ON w.member_id=m.id");
        $filtered = (int)$db->fetchColumn("SELECT COUNT(*) FROM write_offs w JOIN members m ON w.member_id=m.id WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT w.*, m.full_name_en AS member_name, m.membership_number, i.invoice_number
             FROM write_offs w
             JOIN members m ON w.member_id = m.id
             LEFT JOIN invoices i ON w.invoice_id = i.id
             WHERE {$where} ORDER BY w.id DESC LIMIT {$length} OFFSET {$start}",
            $params
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id' => $r['id'], 'write_off_number' => $r['write_off_number'],
                'member_name' => $r['member_name'], 'invoice_number' => $r['invoice_number'] ?? 'â€”',
                'amount' => format_money($r['amount']), 'status' => $r['status'], 'status_badge' => status_badge($r['status']),
            ];
        }
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $this->view('write-offs/create', [
            'title' => lang('adv_finance.create_write_off'),
            'breadcrumbs' => [['label' => lang('adv_finance.write_offs'), 'url' => '/advanced-finance/write-offs'], ['label' => lang('common.create')]],
        ], 'app');
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $validator = new Validator($request->all(), [
            'member_id' => 'required|integer|exists:members,id',
            'invoice_id'=> 'nullable|integer',
            'amount'    => 'required|decimal|min:0.01',
            'reason'    => 'required|string|max:2000',
            'notes'     => 'nullable|string|max:2000',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $d = $validator->validated();
        $woNumber = SequenceGenerator::next('write_off_number');
        $id = $this->db()->insert('write_offs', [
            'write_off_number' => $woNumber, 'member_id' => $d['member_id'],
            'invoice_id' => $d['invoice_id'] ?: null, 'amount' => $d['amount'],
            'reason' => $d['reason'], 'status' => 'pending', 'notes' => $d['notes'] ?? null,
            'created_by' => Auth::id(),
        ]);
        $this->audit('create', 'write_offs', $id, 'Created write-off ' . $woNumber);
        $this->json(['success' => true, 'message' => lang('adv_finance.saved'), 'redirect' => '/advanced-finance/write-offs/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $wo = $this->db()->fetchOne(
            "SELECT w.*, m.full_name_en AS member_name, m.membership_number, i.invoice_number
             FROM write_offs w JOIN members m ON w.member_id=m.id LEFT JOIN invoices i ON w.invoice_id=i.id WHERE w.id = ?",
            [$id]
        );
        if (!$wo) { $this->abort(404, 'Not found'); return; }
        $this->view('write-offs/show', [
            'title' => $wo['write_off_number'],
            'breadcrumbs' => [['label' => lang('adv_finance.write_offs'), 'url' => '/advanced-finance/write-offs'], ['label' => $wo['write_off_number']]],
            'wo' => $wo,
        ], 'app');
    }

    public function approve(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $wo = $db->fetchOne("SELECT * FROM write_offs WHERE id = ? AND status = 'pending'", [$id]);
        if (!$wo) { $this->json(['success' => false, 'message' => 'Not pending'], 422); return; }
        $db->update('write_offs', ['status' => 'approved', 'approved_by' => Auth::id(), 'approved_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
        $this->audit('approve', 'write_offs', $id, 'Approved write-off');
        $this->json(['success' => true, 'message' => lang('adv_finance.status_updated'), 'redirect' => '/advanced-finance/write-offs/' . $id]);
    }

    public function post(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid CSRF'], 403); return; }
        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $wo = $db->fetchOne("SELECT * FROM write_offs WHERE id = ? AND status = 'approved'", [$id]);
        if (!$wo) { $this->json(['success' => false, 'message' => 'Not approved'], 422); return; }

        $db->update('write_offs', ['status' => 'posted', 'posted_by' => Auth::id(), 'posted_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);

        if ($wo['invoice_id']) {
            $inv = $db->fetchOne("SELECT * FROM invoices WHERE id = ?", [$wo['invoice_id']]);
            if ($inv) {
                $db->update('invoices', [
                    'status' => 'written_off',
                    'status_changed_at' => date('Y-m-d H:i:s'),
                ], 'id = ?', [$wo['invoice_id']]);
            }
        }

        $db->execute("CALL sp_recalculate_member_balance(?)", [$wo['member_id']]);
        $this->audit('update', 'write_offs', $id, 'Posted write-off');
        $this->json(['success' => true, 'message' => lang('adv_finance.posted'), 'redirect' => '/advanced-finance/write-offs/' . $id]);
    }
}
```

## File: modules\advanced_finance\lang\ar.php

```php
<?php
return [
    'adv_finance.title'                 => 'Ø§Ù„Ù…Ø§Ù„ÙŠØ© Ø§Ù„Ù…ØªÙ‚Ø¯Ù…Ø©',
    'adv_finance.installments'          => 'Ø®Ø·Ø· Ø§Ù„Ø£Ù‚Ø³Ø§Ø·',
    'adv_finance.installment_templates' => 'Ù‚ÙˆØ§Ù„Ø¨ Ø§Ù„Ø£Ù‚Ø³Ø§Ø·',
    'adv_finance.checks'               => 'Ø§Ù„Ø´ÙŠÙƒØ§Øª',
    'adv_finance.credit_notes'         => 'Ø¥Ø´Ø¹Ø§Ø±Ø§Øª Ø¯Ø§Ø¦Ù†Ø©',
    'adv_finance.refunds'              => 'Ø§Ù„Ù…Ø±ØªØ¬Ø¹Ø§Øª',
    'adv_finance.write_offs'           => 'Ø§Ù„Ø´Ø·Ø¨',
    'adv_finance.bank_accounts'        => 'Ø­Ø³Ø§Ø¨Ø§Øª Ø¨Ù†ÙƒÙŠØ©',
    'adv_finance.deposits'             => 'Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹Ø§Øª Ø§Ù„Ø¨Ù†ÙƒÙŠØ©',
    'adv_finance.gl'                   => 'Ø§Ù„Ø£Ø³ØªØ§Ø° Ø§Ù„Ø¹Ø§Ù…',
    'adv_finance.journal_entries'      => 'Ø§Ù„Ù‚ÙŠÙˆØ¯ Ø§Ù„ÙŠÙˆÙ…ÙŠØ©',
    'adv_finance.budgets'              => 'Ø§Ù„Ù…ÙŠØ²Ø§Ù†ÙŠØ§Øª',
    'adv_finance.chart_of_accounts'    => 'Ø´Ø¬Ø±Ø© Ø§Ù„Ø­Ø³Ø§Ø¨Ø§Øª',
    'adv_finance.balances'             => 'Ø£Ø±ØµØ¯Ø© Ø§Ù„Ø­Ø³Ø§Ø¨Ø§Øª',

    'adv_finance.plan_number'          => 'Ø±Ù‚Ù… Ø§Ù„Ø®Ø·Ø©',
    'adv_finance.member'               => 'Ø§Ù„Ø¹Ø¶Ùˆ',
    'adv_finance.total_amount'         => 'Ø§Ù„Ù…Ø¨Ù„Øº Ø§Ù„Ø¥Ø¬Ù…Ø§Ù„ÙŠ',
    'adv_finance.down_payment'         => 'Ø§Ù„Ø¯ÙØ¹Ø© Ø§Ù„Ù…Ù‚Ø¯Ù…Ø©',
    'adv_finance.financed'             => 'Ø§Ù„Ù…Ø¨Ù„Øº Ø§Ù„Ù…Ù…ÙˆÙ„',
    'adv_finance.num_installments'     => 'Ø¹Ø¯Ø¯ Ø§Ù„Ø£Ù‚Ø³Ø§Ø·',
    'adv_finance.frequency'            => 'Ø§Ù„ØªÙƒØ±Ø§Ø±',
    'adv_finance.start_date'           => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¨Ø¯Ø¡',
    'adv_finance.next_due'             => 'Ø§Ù„Ø§Ø³ØªØ­Ù‚Ø§Ù‚ Ø§Ù„ØªØ§Ù„ÙŠ',
    'adv_finance.total_paid'           => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ù…Ø¯ÙÙˆØ¹',
    'adv_finance.remaining'            => 'Ø§Ù„Ù…ØªØ¨Ù‚ÙŠ',
    'adv_finance.overdue'              => 'Ù…ØªØ£Ø®Ø±',
    'adv_finance.template'             => 'Ø§Ù„Ù‚Ø§Ù„Ø¨',
    'adv_finance.invoice'              => 'Ø§Ù„ÙØ§ØªÙˆØ±Ø©',
    'adv_finance.create_plan'          => 'Ø¥Ù†Ø´Ø§Ø¡ Ø®Ø·Ø© Ø£Ù‚Ø³Ø§Ø·',
    'adv_finance.record_payment'       => 'ØªØ³Ø¬ÙŠÙ„ Ø¯ÙØ¹Ø©',
    'adv_finance.payment_amount'       => 'Ù…Ø¨Ù„Øº Ø§Ù„Ø¯ÙØ¹Ø©',
    'adv_finance.late_fee'             => 'ØºØ±Ø§Ù…Ø© ØªØ£Ø®ÙŠØ±',
    'adv_finance.schedule'             => 'Ø¬Ø¯ÙˆÙ„ Ø§Ù„Ø³Ø¯Ø§Ø¯',
    'adv_finance.installment_no'       => 'Ø±Ù‚Ù… Ø§Ù„Ù‚Ø³Ø·',
    'adv_finance.due_date'             => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ø³ØªØ­Ù‚Ø§Ù‚',
    'adv_finance.amount'               => 'Ø§Ù„Ù…Ø¨Ù„Øº',
    'adv_finance.paid'                 => 'Ø§Ù„Ù…Ø¯ÙÙˆØ¹',
    'adv_finance.balance'              => 'Ø§Ù„Ø±ØµÙŠØ¯',
    'adv_finance.mark_defaulted'       => 'ØªØ¹Ù„ÙŠÙ… ÙƒÙ…ØªØ¹Ø«Ø±',

    'adv_finance.check_number'         => 'Ø±Ù‚Ù… Ø§Ù„Ø´ÙŠÙƒ',
    'adv_finance.bank_name'            => 'Ø§Ø³Ù… Ø§Ù„Ø¨Ù†Ùƒ',
    'adv_finance.drawer_name'          => 'Ø§Ø³Ù… Ø§Ù„Ø³Ø§Ø­Ø¨',
    'adv_finance.check_date'           => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø´ÙŠÙƒ',
    'adv_finance.receive_date'         => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ø³ØªÙ„Ø§Ù…',
    'adv_finance.deposit_date'         => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹',
    'adv_finance.clearance_date'       => 'ØªØ§Ø±ÙŠØ® Ø§Ù„ØªØ­ØµÙŠÙ„',
    'adv_finance.bounce_reason'        => 'Ø³Ø¨Ø¨ Ø§Ù„Ø§Ø±ØªØ¬Ø§Ø¹',
    'adv_finance.bounce_fee'           => 'Ø±Ø³Ù… Ø§Ù„Ø§Ø±ØªØ¬Ø§Ø¹',
    'adv_finance.register_check'       => 'ØªØ³Ø¬ÙŠÙ„ Ø´ÙŠÙƒ',
    'adv_finance.update_status'        => 'ØªØ­Ø¯ÙŠØ« Ø§Ù„Ø­Ø§Ù„Ø©',

    'adv_finance.credit_note_number'   => 'Ø±Ù‚Ù… Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±',
    'adv_finance.credit_date'          => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±',
    'adv_finance.applied_amount'       => 'Ø§Ù„Ù…Ø·Ø¨Ù‚',
    'adv_finance.remaining_amount'     => 'Ø§Ù„Ù…ØªØ¨Ù‚ÙŠ',
    'adv_finance.reason'               => 'Ø§Ù„Ø³Ø¨Ø¨',
    'adv_finance.create_credit_note'   => 'Ø¥Ù†Ø´Ø§Ø¡ Ø¥Ø´Ø¹Ø§Ø± Ø¯Ø§Ø¦Ù†',
    'adv_finance.apply_to_invoice'     => 'ØªØ·Ø¨ÙŠÙ‚ Ø¹Ù„Ù‰ ÙØ§ØªÙˆØ±Ø©',

    'adv_finance.refund_number'        => 'Ø±Ù‚Ù… Ø§Ù„Ù…Ø±ØªØ¬Ø¹',
    'adv_finance.refund_amount'        => 'Ù…Ø¨Ù„Øº Ø§Ù„Ø§Ø³ØªØ±Ø¯Ø§Ø¯',
    'adv_finance.refund_date'          => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ø³ØªØ±Ø¯Ø§Ø¯',
    'adv_finance.refund_method'        => 'Ø·Ø±ÙŠÙ‚Ø© Ø§Ù„Ø§Ø³ØªØ±Ø¯Ø§Ø¯',
    'adv_finance.create_refund'        => 'Ø¥Ù†Ø´Ø§Ø¡ Ù…Ø±ØªØ¬Ø¹',
    'adv_finance.approve'              => 'Ù…ÙˆØ§ÙÙ‚Ø©',
    'adv_finance.reject'               => 'Ø±ÙØ¶',
    'adv_finance.process'              => 'Ù…Ø¹Ø§Ù„Ø¬Ø©',

    'adv_finance.write_off_number'     => 'Ø±Ù‚Ù… Ø§Ù„Ø´Ø·Ø¨',
    'adv_finance.write_off_amount'     => 'Ø§Ù„Ù…Ø¨Ù„Øº',
    'adv_finance.board_resolution'     => 'Ù‚Ø±Ø§Ø± Ù…Ø¬Ù„Ø³ Ø§Ù„Ø¥Ø¯Ø§Ø±Ø©',
    'adv_finance.create_write_off'     => 'Ø¥Ù†Ø´Ø§Ø¡ Ø´Ø·Ø¨',
    'adv_finance.post'                 => 'ØªØ±Ø­ÙŠÙ„',

    'adv_finance.account_code'         => 'ÙƒÙˆØ¯ Ø§Ù„Ø­Ø³Ø§Ø¨',
    'adv_finance.account_name'         => 'Ø§Ø³Ù… Ø§Ù„Ø­Ø³Ø§Ø¨',
    'adv_finance.account_number'       => 'Ø±Ù‚Ù… Ø§Ù„Ø­Ø³Ø§Ø¨',
    'adv_finance.iban'                 => 'Ø¢ÙŠØ¨Ø§Ù†',
    'adv_finance.swift'                => 'Ø³ÙˆÙŠÙØª',
    'adv_finance.branch'               => 'Ø§Ù„ÙØ±Ø¹',
    'adv_finance.account_type'         => 'Ù†ÙˆØ¹ Ø§Ù„Ø­Ø³Ø§Ø¨',
    'adv_finance.current_balance'      => 'Ø§Ù„Ø±ØµÙŠØ¯ Ø§Ù„Ø­Ø§Ù„ÙŠ',

    'adv_finance.deposit_number'       => 'Ø±Ù‚Ù… Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹',
    'adv_finance.deposit_date_label'   => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹',
    'adv_finance.cash_amount'          => 'Ù…Ø¨Ù„Øº Ù†Ù‚Ø¯ÙŠ',
    'adv_finance.check_amount'         => 'Ù…Ø¨Ù„Øº Ø´ÙŠÙƒØ§Øª',
    'adv_finance.bank_reference'       => 'Ù…Ø±Ø¬Ø¹ Ø§Ù„Ø¨Ù†Ùƒ',
    'adv_finance.confirm_deposit'      => 'ØªØ£ÙƒÙŠØ¯ Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹',

    'adv_finance.journal_number'       => 'Ø±Ù‚Ù… Ø§Ù„Ù‚ÙŠØ¯',
    'adv_finance.entry_date'           => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ù‚ÙŠØ¯',
    'adv_finance.description'          => 'Ø§Ù„ÙˆØµÙ',
    'adv_finance.total_debit'          => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ù…Ø¯ÙŠÙ†',
    'adv_finance.total_credit'         => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ø¯Ø§Ø¦Ù†',
    'adv_finance.post_entry'           => 'ØªØ±Ø­ÙŠÙ„ Ø§Ù„Ù‚ÙŠØ¯',
    'adv_finance.reverse_entry'        => 'Ø¹ÙƒØ³ Ø§Ù„Ù‚ÙŠØ¯',
    'adv_finance.debit'                => 'Ù…Ø¯ÙŠÙ†',
    'adv_finance.credit'               => 'Ø¯Ø§Ø¦Ù†',
    'adv_finance.line_items'           => 'Ø¨Ù†ÙˆØ¯ Ø§Ù„Ù‚ÙŠØ¯',
    'adv_finance.add_line'             => 'Ø¥Ø¶Ø§ÙØ© Ø¨Ù†Ø¯',

    'adv_finance.fiscal_year'          => 'Ø§Ù„Ø³Ù†Ø© Ø§Ù„Ù…Ø§Ù„ÙŠØ©',
    'adv_finance.department'           => 'Ø§Ù„Ù‚Ø³Ù…',
    'adv_finance.annual_budget'        => 'Ø§Ù„Ù…ÙŠØ²Ø§Ù†ÙŠØ© Ø§Ù„Ø³Ù†ÙˆÙŠØ©',
    'adv_finance.q1_budget'            => 'Ø±1',
    'adv_finance.q2_budget'            => 'Ø±2',
    'adv_finance.q3_budget'            => 'Ø±3',
    'adv_finance.q4_budget'            => 'Ø±4',
    'adv_finance.actual_spent'         => 'Ø§Ù„Ø¥Ù†ÙØ§Ù‚ Ø§Ù„ÙØ¹Ù„ÙŠ',
    'adv_finance.variance'             => 'Ø§Ù„ÙØ±Ù‚',

    'adv_finance.overdue_installments_widget' => 'Ø£Ù‚Ø³Ø§Ø· Ù…ØªØ£Ø®Ø±Ø©',
    'adv_finance.pending_checks_widget'       => 'Ø´ÙŠÙƒØ§Øª Ù…Ø¹Ù„Ù‚Ø©',
    'adv_finance.gl_balance_widget'           => 'Ø§Ù„ÙˆØ¶Ø¹ Ø§Ù„Ù†Ù‚Ø¯ÙŠ',

    'adv_finance.saved'                => 'ØªÙ… Ø§Ù„Ø­ÙØ¸ Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.deleted'              => 'ØªÙ… Ø§Ù„Ø­Ø°Ù Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.status_updated'       => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ø­Ø§Ù„Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.payment_recorded'     => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯ÙØ¹Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.applied_success'      => 'ØªÙ… ØªØ·Ø¨ÙŠÙ‚ Ø§Ù„Ø¥Ø´Ø¹Ø§Ø± Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.posted'               => 'ØªÙ… ØªØ±Ø­ÙŠÙ„ Ø§Ù„Ù‚ÙŠØ¯ Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.reversed'             => 'ØªÙ… Ø¹ÙƒØ³ Ø§Ù„Ù‚ÙŠØ¯ Ø¨Ù†Ø¬Ø§Ø­',
    'adv_finance.confirmed'            => 'ØªÙ… ØªØ£ÙƒÙŠØ¯ Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹ Ø¨Ù†Ø¬Ø§Ø­',
];
```

## File: modules\advanced_finance\lang\en.php

```php
<?php
return [
    'adv_finance.title'                 => 'Advanced Finance',
    'adv_finance.installments'          => 'Installment Plans',
    'adv_finance.installment_templates' => 'Installment Templates',
    'adv_finance.checks'               => 'Checks',
    'adv_finance.credit_notes'         => 'Credit Notes',
    'adv_finance.refunds'              => 'Refunds',
    'adv_finance.write_offs'           => 'Write-Offs',
    'adv_finance.bank_accounts'        => 'Bank Accounts',
    'adv_finance.deposits'             => 'Bank Deposits',
    'adv_finance.gl'                   => 'General Ledger',
    'adv_finance.journal_entries'      => 'Journal Entries',
    'adv_finance.budgets'              => 'Budgets',
    'adv_finance.chart_of_accounts'    => 'Chart of Accounts',
    'adv_finance.balances'             => 'Account Balances',

    'adv_finance.plan_number'          => 'Plan #',
    'adv_finance.member'               => 'Member',
    'adv_finance.total_amount'         => 'Total Amount',
    'adv_finance.down_payment'         => 'Down Payment',
    'adv_finance.financed'             => 'Financed Amount',
    'adv_finance.num_installments'     => '# Installments',
    'adv_finance.frequency'            => 'Frequency',
    'adv_finance.start_date'           => 'Start Date',
    'adv_finance.next_due'             => 'Next Due Date',
    'adv_finance.total_paid'           => 'Total Paid',
    'adv_finance.remaining'            => 'Remaining',
    'adv_finance.overdue'              => 'Overdue',
    'adv_finance.template'             => 'Template',
    'adv_finance.invoice'              => 'Invoice',
    'adv_finance.create_plan'          => 'Create Installment Plan',
    'adv_finance.record_payment'       => 'Record Payment',
    'adv_finance.payment_amount'       => 'Payment Amount',
    'adv_finance.late_fee'             => 'Late Fee',
    'adv_finance.schedule'             => 'Payment Schedule',
    'adv_finance.installment_no'       => 'Inst. #',
    'adv_finance.due_date'             => 'Due Date',
    'adv_finance.amount'               => 'Amount',
    'adv_finance.paid'                 => 'Paid',
    'adv_finance.balance'              => 'Balance',
    'adv_finance.mark_defaulted'       => 'Mark Defaulted',

    'adv_finance.check_number'         => 'Check #',
    'adv_finance.bank_name'            => 'Bank Name',
    'adv_finance.drawer_name'          => 'Drawer Name',
    'adv_finance.check_date'           => 'Check Date',
    'adv_finance.receive_date'         => 'Receive Date',
    'adv_finance.deposit_date'         => 'Deposit Date',
    'adv_finance.clearance_date'       => 'Clearance Date',
    'adv_finance.bounce_reason'        => 'Bounce Reason',
    'adv_finance.bounce_fee'           => 'Bounce Fee',
    'adv_finance.register_check'       => 'Register Check',
    'adv_finance.update_status'        => 'Update Status',

    'adv_finance.credit_note_number'   => 'Credit Note #',
    'adv_finance.credit_date'          => 'Credit Date',
    'adv_finance.applied_amount'       => 'Applied',
    'adv_finance.remaining_amount'     => 'Remaining',
    'adv_finance.reason'               => 'Reason',
    'adv_finance.create_credit_note'   => 'Create Credit Note',
    'adv_finance.apply_to_invoice'     => 'Apply to Invoice',

    'adv_finance.refund_number'        => 'Refund #',
    'adv_finance.refund_amount'        => 'Refund Amount',
    'adv_finance.refund_date'          => 'Refund Date',
    'adv_finance.refund_method'        => 'Refund Method',
    'adv_finance.create_refund'        => 'Create Refund',
    'adv_finance.approve'              => 'Approve',
    'adv_finance.reject'               => 'Reject',
    'adv_finance.process'              => 'Process',

    'adv_finance.write_off_number'     => 'Write-Off #',
    'adv_finance.write_off_amount'     => 'Amount',
    'adv_finance.board_resolution'     => 'Board Resolution',
    'adv_finance.create_write_off'     => 'Create Write-Off',
    'adv_finance.post'                 => 'Post',

    'adv_finance.account_code'         => 'Account Code',
    'adv_finance.account_name'         => 'Account Name',
    'adv_finance.account_number'       => 'Account Number',
    'adv_finance.iban'                 => 'IBAN',
    'adv_finance.swift'                => 'SWIFT',
    'adv_finance.branch'               => 'Branch',
    'adv_finance.account_type'         => 'Account Type',
    'adv_finance.current_balance'      => 'Current Balance',

    'adv_finance.deposit_number'       => 'Deposit #',
    'adv_finance.deposit_date'         => 'Deposit Date',
    'adv_finance.cash_amount'          => 'Cash Amount',
    'adv_finance.check_amount'         => 'Check Amount',
    'adv_finance.bank_reference'       => 'Bank Reference',
    'adv_finance.confirm_deposit'      => 'Confirm Deposit',

    'adv_finance.journal_number'       => 'Journal #',
    'adv_finance.entry_date'           => 'Entry Date',
    'adv_finance.description'          => 'Description',
    'adv_finance.total_debit'          => 'Total Debit',
    'adv_finance.total_credit'         => 'Total Credit',
    'adv_finance.post_entry'           => 'Post Entry',
    'adv_finance.reverse_entry'        => 'Reverse Entry',
    'adv_finance.debit'                => 'Debit',
    'adv_finance.credit'               => 'Credit',
    'adv_finance.line_items'           => 'Line Items',
    'adv_finance.add_line'             => 'Add Line',

    'adv_finance.fiscal_year'          => 'Fiscal Year',
    'adv_finance.department'           => 'Department',
    'adv_finance.annual_budget'        => 'Annual Budget',
    'adv_finance.q1_budget'            => 'Q1',
    'adv_finance.q2_budget'            => 'Q2',
    'adv_finance.q3_budget'            => 'Q3',
    'adv_finance.q4_budget'            => 'Q4',
    'adv_finance.actual_spent'         => 'Actual Spent',
    'adv_finance.variance'             => 'Variance',

    'adv_finance.overdue_installments_widget' => 'Overdue Installments',
    'adv_finance.pending_checks_widget'       => 'Pending Checks',
    'adv_finance.gl_balance_widget'           => 'Cash Position',

    'adv_finance.saved'                => 'Record saved successfully',
    'adv_finance.deleted'              => 'Record deleted successfully',
    'adv_finance.status_updated'       => 'Status updated successfully',
    'adv_finance.payment_recorded'     => 'Payment recorded successfully',
    'adv_finance.applied_success'      => 'Credit note applied successfully',
    'adv_finance.posted'               => 'Entry posted successfully',
    'adv_finance.reversed'             => 'Entry reversed successfully',
    'adv_finance.confirmed'            => 'Deposit confirmed successfully',
];
```

## File: modules\advanced_finance\module.json

```json
{
    "name": "advanced_finance",
    "label_en": "Advanced Finance",
    "label_ar": "Ø§Ù„Ù…Ø§Ù„ÙŠØ© Ø§Ù„Ù…ØªÙ‚Ø¯Ù…Ø©",
    "version": "1.0.0",
    "description": "Installments, checks, credit notes, refunds, write-offs, banking, GL, budgets",
    "sort_order": 35,
    "icon": "fas fa-landmark",
    "routes_file": "routes.php",
    "css_files": ["css/advanced_finance.css"],
    "js_files": ["js/advanced_finance.js"],
    "menu": [
        {
            "label_en": "Advanced Finance",
            "label_ar": "Ø§Ù„Ù…Ø§Ù„ÙŠØ© Ø§Ù„Ù…ØªÙ‚Ø¯Ù…Ø©",
            "icon": "fas fa-landmark",
            "permission": "installments.view",
            "sort_order": 35,
            "children": [
                {
                    "label_en": "Installment Plans",
                    "label_ar": "Ø®Ø·Ø· Ø§Ù„Ø£Ù‚Ø³Ø§Ø·",
                    "route": "/advanced-finance/installments",
                    "permission": "installments.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Installment Templates",
                    "label_ar": "Ù‚ÙˆØ§Ù„Ø¨ Ø§Ù„Ø£Ù‚Ø³Ø§Ø·",
                    "route": "/advanced-finance/installment-templates",
                    "permission": "installments.create",
                    "sort_order": 2
                },
                {
                    "label_en": "Checks",
                    "label_ar": "Ø§Ù„Ø´ÙŠÙƒØ§Øª",
                    "route": "/advanced-finance/checks",
                    "permission": "installments.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Credit Notes",
                    "label_ar": "Ø¥Ø´Ø¹Ø§Ø±Ø§Øª Ø¯Ø§Ø¦Ù†Ø©",
                    "route": "/advanced-finance/credit-notes",
                    "permission": "installments.view",
                    "sort_order": 4
                },
                {
                    "label_en": "Refunds",
                    "label_ar": "Ø§Ù„Ù…Ø±ØªØ¬Ø¹Ø§Øª",
                    "route": "/advanced-finance/refunds",
                    "permission": "refunds.create",
                    "sort_order": 5
                },
                {
                    "label_en": "Write-Offs",
                    "label_ar": "Ø§Ù„Ø´Ø·Ø¨",
                    "route": "/advanced-finance/write-offs",
                    "permission": "installments.view",
                    "sort_order": 6
                },
                {
                    "label_en": "Bank Accounts",
                    "label_ar": "Ø­Ø³Ø§Ø¨Ø§Øª Ø¨Ù†ÙƒÙŠØ©",
                    "route": "/advanced-finance/bank-accounts",
                    "permission": "gl.view",
                    "sort_order": 7
                },
                {
                    "label_en": "Bank Deposits",
                    "label_ar": "Ø§Ù„Ø¥ÙŠØ¯Ø§Ø¹Ø§Øª Ø§Ù„Ø¨Ù†ÙƒÙŠØ©",
                    "route": "/advanced-finance/deposits",
                    "permission": "gl.view",
                    "sort_order": 8
                },
                {
                    "label_en": "General Ledger",
                    "label_ar": "Ø§Ù„Ø£Ø³ØªØ§Ø° Ø§Ù„Ø¹Ø§Ù…",
                    "route": "/advanced-finance/gl",
                    "permission": "gl.view",
                    "sort_order": 9
                },
                {
                    "label_en": "Journal Entries",
                    "label_ar": "Ø§Ù„Ù‚ÙŠÙˆØ¯ Ø§Ù„ÙŠÙˆÙ…ÙŠØ©",
                    "route": "/advanced-finance/gl/journal-entries",
                    "permission": "gl.view",
                    "sort_order": 10
                },
                {
                    "label_en": "Budgets",
                    "label_ar": "Ø§Ù„Ù…ÙŠØ²Ø§Ù†ÙŠØ§Øª",
                    "route": "/advanced-finance/budgets",
                    "permission": "budget.view",
                    "sort_order": 11
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "overdue_installments",
            "label_en": "Overdue Installments",
            "label_ar": "Ø£Ù‚Ø³Ø§Ø· Ù…ØªØ£Ø®Ø±Ø©",
            "permission": "installments.view",
            "partial": "views/widgets/overdue_installments.php",
            "size": "col-md-4",
            "sort_order": 40
        },
        {
            "widget_id": "pending_checks",
            "label_en": "Pending Checks",
            "label_ar": "Ø´ÙŠÙƒØ§Øª Ù…Ø¹Ù„Ù‚Ø©",
            "permission": "installments.view",
            "partial": "views/widgets/pending_checks.php",
            "size": "col-md-4",
            "sort_order": 41
        },
        {
            "widget_id": "gl_cash_balance",
            "label_en": "Cash Balance",
            "label_ar": "Ø±ØµÙŠØ¯ Ù†Ù‚Ø¯ÙŠ",
            "permission": "gl.view",
            "partial": "views/widgets/gl_balance.php",
            "size": "col-md-4",
            "sort_order": 42
        }
    ]
}
```

## File: modules\advanced_finance\routes.php

```php
<?php
return [
    // â”€â”€ Installment Plans â”€â”€
    ['GET',  '/advanced-finance/installments',                'InstallmentController@index',          'installments.view'],
    ['GET',  '/advanced-finance/installments/datatable',      'InstallmentController@datatable',      'installments.view'],
    ['GET',  '/advanced-finance/installments/create',         'InstallmentController@create',         'installments.create'],
    ['POST', '/advanced-finance/installments/store',          'InstallmentController@store',          'installments.create'],
    ['GET',  '/advanced-finance/installments/{id}',           'InstallmentController@show',           'installments.view'],
    ['POST', '/advanced-finance/installments/{id}/pay',       'InstallmentController@recordPayment',  'installments.create'],
    ['POST', '/advanced-finance/installments/{id}/default',   'InstallmentController@markDefaulted',  'installments.create'],

    // â”€â”€ Installment Templates â”€â”€
    ['GET',  '/advanced-finance/installment-templates',              'InstallmentTemplateController@index',     'installments.create'],
    ['GET',  '/advanced-finance/installment-templates/datatable',    'InstallmentTemplateController@datatable', 'installments.create'],
    ['GET',  '/advanced-finance/installment-templates/create',       'InstallmentTemplateController@create',    'installments.create'],
    ['POST', '/advanced-finance/installment-templates/store',        'InstallmentTemplateController@store',     'installments.create'],
    ['GET',  '/advanced-finance/installment-templates/{id}/edit',    'InstallmentTemplateController@edit',      'installments.create'],
    ['POST', '/advanced-finance/installment-templates/{id}',         'InstallmentTemplateController@update',    'installments.create'],
    ['POST', '/advanced-finance/installment-templates/{id}/delete',  'InstallmentTemplateController@delete',    'installments.create'],

    // â”€â”€ Checks â”€â”€
    ['GET',  '/advanced-finance/checks',                'CheckController@index',        'installments.view'],
    ['GET',  '/advanced-finance/checks/datatable',      'CheckController@datatable',    'installments.view'],
    ['GET',  '/advanced-finance/checks/create',         'CheckController@create',       'installments.create'],
    ['POST', '/advanced-finance/checks/store',          'CheckController@store',        'installments.create'],
    ['GET',  '/advanced-finance/checks/{id}',           'CheckController@show',         'installments.view'],
    ['POST', '/advanced-finance/checks/{id}/status',    'CheckController@updateStatus', 'installments.create'],

    // â”€â”€ Credit Notes â”€â”€
    ['GET',  '/advanced-finance/credit-notes',              'CreditNoteController@index',     'installments.view'],
    ['GET',  '/advanced-finance/credit-notes/datatable',    'CreditNoteController@datatable', 'installments.view'],
    ['GET',  '/advanced-finance/credit-notes/create',       'CreditNoteController@create',    'installments.create'],
    ['POST', '/advanced-finance/credit-notes/store',        'CreditNoteController@store',     'installments.create'],
    ['GET',  '/advanced-finance/credit-notes/{id}',         'CreditNoteController@show',      'installments.view'],
    ['POST', '/advanced-finance/credit-notes/{id}/apply',   'CreditNoteController@apply',     'installments.create'],
    ['POST', '/advanced-finance/credit-notes/{id}/issue',   'CreditNoteController@issue',     'installments.create'],

    // â”€â”€ Refunds â”€â”€
    ['GET',  '/advanced-finance/refunds',                  'RefundController@index',    'refunds.create'],
    ['GET',  '/advanced-finance/refunds/datatable',        'RefundController@datatable','refunds.create'],
    ['GET',  '/advanced-finance/refunds/create',           'RefundController@create',   'refunds.create'],
    ['POST', '/advanced-finance/refunds/store',            'RefundController@store',    'refunds.create'],
    ['GET',  '/advanced-finance/refunds/{id}',             'RefundController@show',     'refunds.create'],
    ['POST', '/advanced-finance/refunds/{id}/approve',     'RefundController@approve',  'refunds.approve'],
    ['POST', '/advanced-finance/refunds/{id}/process',     'RefundController@process',  'refunds.approve'],
    ['POST', '/advanced-finance/refunds/{id}/reject',      'RefundController@reject',   'refunds.approve'],

    // â”€â”€ Write-Offs â”€â”€
    ['GET',  '/advanced-finance/write-offs',                  'WriteOffController@index',    'installments.view'],
    ['GET',  '/advanced-finance/write-offs/datatable',        'WriteOffController@datatable','installments.view'],
    ['GET',  '/advanced-finance/write-offs/create',           'WriteOffController@create',   'installments.create'],
    ['POST', '/advanced-finance/write-offs/store',            'WriteOffController@store',    'installments.create'],
    ['GET',  '/advanced-finance/write-offs/{id}',             'WriteOffController@show',     'installments.view'],
    ['POST', '/advanced-finance/write-offs/{id}/approve',     'WriteOffController@approve',  'refunds.approve'],
    ['POST', '/advanced-finance/write-offs/{id}/post',        'WriteOffController@post',     'gl.manage'],

    // â”€â”€ Bank Accounts â”€â”€
    ['GET',  '/advanced-finance/bank-accounts',              'BankAccountController@index',     'gl.view'],
    ['GET',  '/advanced-finance/bank-accounts/datatable',    'BankAccountController@datatable', 'gl.view'],
    ['GET',  '/advanced-finance/bank-accounts/create',       'BankAccountController@create',    'gl.manage'],
    ['POST', '/advanced-finance/bank-accounts/store',        'BankAccountController@store',     'gl.manage'],
    ['GET',  '/advanced-finance/bank-accounts/{id}/edit',    'BankAccountController@edit',      'gl.manage'],
    ['POST', '/advanced-finance/bank-accounts/{id}',         'BankAccountController@update',    'gl.manage'],

    // â”€â”€ Bank Deposits â”€â”€
    ['GET',  '/advanced-finance/deposits',                  'BankDepositController@index',     'gl.view'],
    ['GET',  '/advanced-finance/deposits/datatable',        'BankDepositController@datatable', 'gl.view'],
    ['GET',  '/advanced-finance/deposits/create',           'BankDepositController@create',    'gl.manage'],
    ['POST', '/advanced-finance/deposits/store',            'BankDepositController@store',     'gl.manage'],
    ['GET',  '/advanced-finance/deposits/{id}',             'BankDepositController@show',      'gl.view'],
    ['POST', '/advanced-finance/deposits/{id}/confirm',     'BankDepositController@confirm',   'gl.manage'],

    // â”€â”€ General Ledger â”€â”€
    ['GET',  '/advanced-finance/gl',                                 'GLController@accounts',              'gl.view'],
    ['GET',  '/advanced-finance/gl/accounts/datatable',              'GLController@accountsDatatable',     'gl.view'],
    ['GET',  '/advanced-finance/gl/journal-entries',                  'GLController@journalEntries',        'gl.view'],
    ['GET',  '/advanced-finance/gl/journal-entries/datatable',        'GLController@journalEntriesDatatable','gl.view'],
    ['GET',  '/advanced-finance/gl/journal-entries/create',           'GLController@createJournalEntry',    'gl.manage'],
    ['POST', '/advanced-finance/gl/journal-entries/store',            'GLController@storeJournalEntry',     'gl.manage'],
    ['GET',  '/advanced-finance/gl/journal-entries/{id}',             'GLController@showJournalEntry',      'gl.view'],
    ['POST', '/advanced-finance/gl/journal-entries/{id}/post',        'GLController@postJournalEntry',      'gl.manage'],
    ['POST', '/advanced-finance/gl/journal-entries/{id}/reverse',     'GLController@reverseJournalEntry',   'gl.manage'],
    ['GET',  '/advanced-finance/gl/balances',                         'GLController@balances',              'gl.view'],

    // â”€â”€ Budgets â”€â”€
    ['GET',  '/advanced-finance/budgets',              'BudgetController@index',     'budget.view'],
    ['GET',  '/advanced-finance/budgets/datatable',    'BudgetController@datatable', 'budget.view'],
    ['GET',  '/advanced-finance/budgets/create',       'BudgetController@create',    'budget.manage'],
    ['POST', '/advanced-finance/budgets/store',        'BudgetController@store',     'budget.manage'],
    ['GET',  '/advanced-finance/budgets/{id}/edit',    'BudgetController@edit',      'budget.manage'],
    ['POST', '/advanced-finance/budgets/{id}',         'BudgetController@update',    'budget.manage'],

    // â”€â”€ API: member search helper â”€â”€
    ['GET',  '/advanced-finance/api/member-search',    'AdvFinanceApiController@memberSearch',  'installments.view'],
    ['GET',  '/advanced-finance/api/invoice-search',   'AdvFinanceApiController@invoiceSearch',  'installments.view'],
];
```

## File: modules\advanced_finance\views\bank-accounts\form.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $account ? lang('common.edit') : lang('common.create') ?> <?= lang('adv_finance.bank_accounts') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $account ? '/advanced-finance/bank-accounts/' . $account['id'] : '/advanced-finance/bank-accounts/store' ?>" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="account_code" class="form-control" value="<?= e($account['account_code'] ?? '') ?>" required><div class="invalid-feedback" data-field="account_code"></div></div>
                <div class="col-md-4"><label class="form-label">Bank (EN) <span class="text-danger">*</span></label><input type="text" name="bank_name_en" class="form-control" value="<?= e($account['bank_name_en'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Bank (AR) <span class="text-danger">*</span></label><input type="text" name="bank_name_ar" class="form-control" value="<?= e($account['bank_name_ar'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Account Name <span class="text-danger">*</span></label><input type="text" name="account_name" class="form-control" value="<?= e($account['account_name'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Account # <span class="text-danger">*</span></label><input type="text" name="account_number" class="form-control" value="<?= e($account['account_number'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">IBAN</label><input type="text" name="iban" class="form-control" value="<?= e($account['iban'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">SWIFT</label><input type="text" name="swift_code" class="form-control" value="<?= e($account['swift_code'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Branch</label><input type="text" name="branch" class="form-control" value="<?= e($account['branch'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Currency <span class="text-danger">*</span></label><select name="currency_id" class="form-select" required><?php foreach($currencies as $c): ?><option value="<?= $c['id'] ?>" <?= selected($c['id'], $account['currency_id'] ?? '') ?>><?= e($c['currency_code']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Type <span class="text-danger">*</span></label><select name="account_type" class="form-select" required><option value="current" <?= selected('current', $account['account_type'] ?? '') ?>>Current</option><option value="savings" <?= selected('savings', $account['account_type'] ?? '') ?>>Savings</option><option value="deposit" <?= selected('deposit', $account['account_type'] ?? '') ?>>Deposit</option></select></div>
                <?php if(!$account): ?><div class="col-md-4"><label class="form-label">Opening Balance <span class="text-danger">*</span></label><input type="number" name="opening_balance" class="form-control" step="0.01" value="0" required></div><?php endif; ?>
                <?php if($account): ?><div class="col-md-4 d-flex align-items-end"><div class="form-check"><input type="checkbox" name="is_active" value="1" class="form-check-input" <?= checked(1, $account['is_active'] ?? 1) ?>><label class="form-check-label">Active</label></div></div><?php endif; ?>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/advanced-finance/bank-accounts" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
```

## File: modules\advanced_finance\views\bank-accounts\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h5 class="mb-0"><?= lang('adv_finance.bank_accounts') ?></h5>
    <?php if(auth_can('gl.manage')): ?><a href="/advanced-finance/bank-accounts/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a><?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="ba-table" class="table table-striped w-100" data-url="/advanced-finance/bank-accounts/datatable"
           data-columns='[{"data":"account_code","title":"Code"},{"data":"bank_name","title":"Bank"},{"data":"account_number","title":"Acct #"},{"data":"iban","title":"IBAN"},{"data":"account_type","title":"Type"},{"data":"current_balance","title":"Balance"},{"data":"is_active","title":"Status","orderable":false},{"data":"id","title":"Actions","orderable":false,"searchable":false}]'>
        <thead><tr><th>Code</th><th>Bank</th><th>Acct #</th><th>IBAN</th><th>Type</th><th>Balance</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('ba-table',{columnDefs:[{targets:-1,render:function(d){return '<a href="/advanced-finance/bank-accounts/'+d+'/edit" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i></a>';}}]});});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\checks\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-money-check me-2"></i><?= lang('adv_finance.register_check') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/advanced-finance/checks/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Check # <span class="text-danger">*</span></label><input type="text" name="check_number" class="form-control" required><div class="invalid-feedback" data-field="check_number"></div></div>
                <div class="col-md-4"><label class="form-label">Bank <span class="text-danger">*</span></label><input type="text" name="bank_name" class="form-control" required><div class="invalid-feedback" data-field="bank_name"></div></div>
                <div class="col-md-4"><label class="form-label">Branch</label><input type="text" name="branch" class="form-control"></div>
                <div class="col-md-6"><label class="form-label">Drawer Name <span class="text-danger">*</span></label><input type="text" name="drawer_name" class="form-control" required><div class="invalid-feedback" data-field="drawer_name"></div></div>
                <div class="col-md-3"><label class="form-label">Amount <span class="text-danger">*</span></label><input type="number" name="amount" class="form-control" step="0.01" required><div class="invalid-feedback" data-field="amount"></div></div>
                <div class="col-md-3"><label class="form-label">Currency <span class="text-danger">*</span></label><select name="currency_id" class="form-select" required><?php foreach($currencies as $c): ?><option value="<?= $c['id'] ?>" <?= $c['is_default'] ? 'selected' : '' ?>><?= e($c['currency_code']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Check Date <span class="text-danger">*</span></label><input type="date" name="check_date" class="form-control" required></div>
                <div class="col-md-4"><label class="form-label">Receive Date <span class="text-danger">*</span></label><input type="date" name="receive_date" class="form-control" value="<?= date('Y-m-d') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Member</label><select name="member_id" class="form-select select2-ajax" data-url="/advanced-finance/api/member-search"></select></div>
                <div class="col-md-6"><label class="form-label">Bank Account</label><select name="bank_account_id" class="form-select"><option value="">â€”</option><?php foreach($bankAccounts as $ba): ?><option value="<?= $ba['id'] ?>"><?= e($ba['bank_name_en']) ?> - <?= e($ba['account_number']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-6"><label class="form-label">Account #</label><input type="text" name="account_number" class="form-control"></div>
                <div class="col-12"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/advanced-finance/checks" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded', function() { AdvFinance.initMemberSelect('.select2-ajax'); });</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\checks\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><h6 class="text-muted mb-1">Total</h6><h3 class="mb-0"><?= number_format($stats['total'] ?? 0) ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-warning h-100"><div class="card-body"><h6 class="text-muted mb-1">Received</h6><h3 class="mb-0"><?= number_format($stats['received'] ?? 0) ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-info h-100"><div class="card-body"><h6 class="text-muted mb-1">Deposited</h6><h3 class="mb-0"><?= number_format($stats['deposited'] ?? 0) ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body"><h6 class="text-muted mb-1">Bounced</h6><h3 class="mb-0"><?= number_format($stats['bounced'] ?? 0) ?></h3></div></div></div>
</div>
<div class="card mb-3"><div class="card-body">
    <div class="row g-2 align-items-end" data-filter-table="checks-table">
        <div class="col-md-3">
            <select name="status_filter" class="form-select form-select-sm" onchange="DataTableHelper.reload('checks-table')">
                <option value=""><?= lang('common.all') ?></option>
                <option value="received">Received</option><option value="deposited">Deposited</option>
                <option value="cleared">Cleared</option><option value="bounced">Bounced</option>
            </select>
        </div>
        <div class="col-md-3 ms-auto text-end">
            <?php if (auth_can('installments.create')): ?>
            <a href="/advanced-finance/checks/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('adv_finance.register_check') ?></a>
            <?php endif; ?>
        </div>
    </div>
</div></div>
<div class="card"><div class="card-body">
    <table id="checks-table" class="table table-striped w-100"
           data-url="/advanced-finance/checks/datatable"
           data-columns='[{"data":"check_number","title":"Check #"},{"data":"bank_name","title":"Bank"},{"data":"drawer_name","title":"Drawer"},{"data":"amount","title":"Amount"},{"data":"check_date","title":"Check Date"},{"data":"member_name","title":"Member"},{"data":"status_badge","title":"Status","orderable":false},{"data":"id","title":"Actions","orderable":false,"searchable":false}]'>
        <thead><tr><th>Check #</th><th>Bank</th><th>Drawer</th><th>Amount</th><th>Check Date</th><th>Member</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('checks-table', {
        columnDefs: [{targets: -1, render: function(data) { return '<a href="/advanced-finance/checks/' + data + '" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>'; }}]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\checks\show.php

```php
<div class="row g-4">
    <div class="col-lg-5">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Check: <?= e($check['check_number']) ?></h6></div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <tr><th>Bank</th><td><?= e($check['bank_name']) ?></td></tr>
                    <tr><th>Branch</th><td><?= e($check['branch'] ?? 'â€”') ?></td></tr>
                    <tr><th>Drawer</th><td><?= e($check['drawer_name']) ?></td></tr>
                    <tr><th>Amount</th><td class="fw-bold"><?= format_money($check['amount']) ?> <?= e($check['currency_code']) ?></td></tr>
                    <tr><th>Check Date</th><td><?= format_date($check['check_date']) ?></td></tr>
                    <tr><th>Received</th><td><?= format_date($check['receive_date']) ?></td></tr>
                    <tr><th>Deposited</th><td><?= format_date($check['deposit_date']) ?></td></tr>
                    <tr><th>Cleared</th><td><?= format_date($check['clearance_date']) ?></td></tr>
                    <tr><th>Member</th><td><?= e($check['member_name'] ?? 'â€”') ?></td></tr>
                    <tr><th>Status</th><td><?= status_badge($check['status']) ?></td></tr>
                    <?php if ($check['bounce_reason']): ?><tr><th>Bounce Reason</th><td class="text-danger"><?= e($check['bounce_reason']) ?></td></tr><?php endif; ?>
                    <?php if ($check['bounce_fee']): ?><tr><th>Bounce Fee</th><td><?= format_money($check['bounce_fee']) ?></td></tr><?php endif; ?>
                </table>
            </div>
        </div>
    </div>
    <div class="col-lg-7">
        <?php if (in_array($check['status'], ['received','deposited','bounced']) && auth_can('installments.create')): ?>
        <div class="card mb-4">
            <div class="card-header"><h6 class="mb-0"><?= lang('adv_finance.update_status') ?></h6></div>
            <div class="card-body">
                <form method="POST" action="/advanced-finance/checks/<?= $check['id'] ?>/status" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-4">
                            <label class="form-label">New Status <span class="text-danger">*</span></label>
                            <select name="new_status" class="form-select" required id="newStatusSelect">
                                <option value="">Select...</option>
                                <?php
                                $allowed = ['received'=>['deposited','cancelled','returned'],'deposited'=>['cleared','bounced'],'bounced'=>['replaced','received']];
                                foreach ($allowed[$check['status']] ?? [] as $s):
                                ?>
                                <option value="<?= $s ?>"><?= ucfirst($s) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4" id="depositDateWrap" style="display:none">
                            <label class="form-label">Deposit Date</label>
                            <input type="date" name="deposit_date" class="form-control" value="<?= date('Y-m-d') ?>">
                        </div>
                        <div class="col-md-4" id="bankAccountWrap" style="display:none">
                            <label class="form-label">Bank Account</label>
                            <select name="bank_account_id" class="form-select">
                                <option value="">â€”</option>
                                <?php foreach($bankAccounts as $ba): ?><option value="<?= $ba['id'] ?>"><?= e($ba['bank_name_en']) ?></option><?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4" id="clearDateWrap" style="display:none">
                            <label class="form-label">Clearance Date</label>
                            <input type="date" name="clearance_date" class="form-control" value="<?= date('Y-m-d') ?>">
                        </div>
                        <div class="col-md-6" id="bounceWrap" style="display:none">
                            <label class="form-label">Bounce Reason</label>
                            <input type="text" name="bounce_reason" class="form-control">
                        </div>
                        <div class="col-md-3" id="bounceFeeWrap" style="display:none">
                            <label class="form-label">Bounce Fee</label>
                            <input type="number" name="bounce_fee" class="form-control" step="0.01" value="0">
                        </div>
                        <div class="col-12"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
                    </div>
                    <div class="mt-3"><button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button></div>
                </form>
            </div>
        </div>
        <?php endif; ?>

        <div class="card">
            <div class="card-header"><h6 class="mb-0">Status History</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>From</th><th>To</th><th>Date</th><th>Notes</th></tr></thead>
                    <tbody>
                    <?php foreach($history as $h): ?>
                    <tr><td><?= status_badge($h['from_status'] ?? 'new') ?></td><td><?= status_badge($h['to_status']) ?></td><td><?= format_datetime($h['changed_at']) ?></td><td><?= e($h['notes'] ?? 'â€”') ?></td></tr>
                    <?php endforeach; ?>
                    <?php if (empty($history)): ?><tr><td colspan="4" class="text-center text-muted py-3">No history yet</td></tr><?php endif; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>
document.getElementById('newStatusSelect')?.addEventListener('change', function() {
    const v = this.value;
    document.getElementById('depositDateWrap').style.display = v === 'deposited' ? '' : 'none';
    document.getElementById('bankAccountWrap').style.display = v === 'deposited' ? '' : 'none';
    document.getElementById('clearDateWrap').style.display = v === 'cleared' ? '' : 'none';
    document.getElementById('bounceWrap').style.display = v === 'bounced' ? '' : 'none';
    document.getElementById('bounceFeeWrap').style.display = v === 'bounced' ? '' : 'none';
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\credit-notes\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= lang('adv_finance.create_credit_note') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/advanced-finance/credit-notes/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6"><label class="form-label">Member <span class="text-danger">*</span></label><select name="member_id" class="form-select select2-ajax" data-url="/advanced-finance/api/member-search" required></select><div class="invalid-feedback" data-field="member_id"></div></div>
                <div class="col-md-6"><label class="form-label">Original Invoice</label><select name="original_invoice_id" class="form-select select2-ajax" data-url="/advanced-finance/api/invoice-search"></select></div>
                <div class="col-md-4"><label class="form-label">Amount <span class="text-danger">*</span></label><input type="number" name="total_amount" class="form-control" step="0.01" required><div class="invalid-feedback" data-field="total_amount"></div></div>
                <div class="col-12"><label class="form-label">Reason <span class="text-danger">*</span></label><textarea name="reason" class="form-control" rows="3" required></textarea><div class="invalid-feedback" data-field="reason"></div></div>
                <div class="col-12"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/advanced-finance/credit-notes" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){AdvFinance.initMemberSelect('.select2-ajax');});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\credit-notes\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h5 class="mb-0"><?= lang('adv_finance.credit_notes') ?></h5>
    <?php if(auth_can('installments.create')): ?><a href="/advanced-finance/credit-notes/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('adv_finance.create_credit_note') ?></a><?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="cn-table" class="table table-striped w-100" data-url="/advanced-finance/credit-notes/datatable"
           data-columns='[{"data":"credit_note_number","title":"CN #"},{"data":"member_name","title":"Member"},{"data":"credit_date","title":"Date"},{"data":"total_amount","title":"Total"},{"data":"applied_amount","title":"Applied"},{"data":"remaining_amount","title":"Remaining"},{"data":"status_badge","title":"Status","orderable":false},{"data":"id","title":"Actions","orderable":false,"searchable":false}]'>
        <thead><tr><th>CN #</th><th>Member</th><th>Date</th><th>Total</th><th>Applied</th><th>Remaining</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('cn-table',{columnDefs:[{targets:-1,render:function(d){return '<a href="/advanced-finance/credit-notes/'+d+'" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';}}]});});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\credit-notes\show.php

```php
<div class="row g-4">
    <div class="col-lg-5">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= e($cn['credit_note_number']) ?></h6></div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <tr><th>Member</th><td><?= e($cn['member_name']) ?> (<?= e($cn['membership_number']) ?>)</td></tr>
                    <tr><th>Date</th><td><?= format_date($cn['credit_date']) ?></td></tr>
                    <tr><th>Total</th><td class="fw-bold"><?= format_money($cn['total_amount']) ?></td></tr>
                    <tr><th>Applied</th><td><?= format_money($cn['applied_amount']) ?></td></tr>
                    <tr><th>Remaining</th><td class="text-success fw-bold"><?= format_money($cn['remaining_amount']) ?></td></tr>
                    <tr><th>Status</th><td><?= status_badge($cn['status']) ?></td></tr>
                    <tr><th>Reason</th><td><?= e($cn['reason']) ?></td></tr>
                    <?php if($cn['original_invoice_number']): ?><tr><th>Original Invoice</th><td><?= e($cn['original_invoice_number']) ?></td></tr><?php endif; ?>
                </table>
                <?php if($cn['status'] === 'draft' && auth_can('installments.create')): ?>
                <div class="mt-3">
                    <form method="POST" action="/advanced-finance/credit-notes/<?= $cn['id'] ?>/issue" data-ajax="true">
                        <?= csrf_field() ?>
                        <button type="submit" class="btn btn-success btn-sm"><i class="fas fa-check me-1"></i>Issue</button>
                    </form>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-lg-7">
        <?php if(in_array($cn['status'], ['issued','partially_applied']) && !empty($outstandingInvoices)): ?>
        <div class="card mb-4">
            <div class="card-header"><h6 class="mb-0"><?= lang('adv_finance.apply_to_invoice') ?></h6></div>
            <div class="card-body">
                <form method="POST" action="/advanced-finance/credit-notes/<?= $cn['id'] ?>/apply" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Invoice</label>
                            <select name="invoice_id" class="form-select" required>
                                <option value="">Select...</option>
                                <?php foreach($outstandingInvoices as $inv): ?>
                                <option value="<?= $inv['id'] ?>"><?= e($inv['invoice_number']) ?> â€” Due: <?= format_money($inv['balance_due']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Amount to Apply</label>
                            <input type="number" name="apply_amount" class="form-control" step="0.01" max="<?= $cn['remaining_amount'] ?>" required>
                        </div>
                        <div class="col-md-2 d-flex align-items-end">
                            <button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-arrow-right me-1"></i>Apply</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
        <?php endif; ?>
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Items</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Description</th><th>Amount</th></tr></thead>
                    <tbody>
                    <?php foreach($items as $item): ?>
                    <tr><td><?= e($item['description_en']) ?></td><td><?= format_money($item['amount']) ?></td></tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
```

## File: modules\advanced_finance\views\deposits\create.php

```php
<div class="row justify-content-center"><div class="col-lg-10"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= lang('common.create') ?> <?= lang('adv_finance.deposits') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/advanced-finance/deposits/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Bank Account <span class="text-danger">*</span></label><select name="bank_account_id" class="form-select" required><?php foreach($bankAccounts as $ba): ?><option value="<?= $ba['id'] ?>"><?= e($ba['bank_name_en']) ?> â€” <?= e($ba['account_number']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Deposit Date <span class="text-danger">*</span></label><input type="date" name="deposit_date" class="form-control" value="<?= date('Y-m-d') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Cash Amount <span class="text-danger">*</span></label><input type="number" name="cash_amount" class="form-control" step="0.01" value="0" required></div>
                <div class="col-12"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
            </div>
            <?php if(!empty($receivedChecks)): ?>
            <h6 class="mt-4 mb-3">Include Checks</h6>
            <div class="table-responsive">
                <table class="table table-sm table-bordered">
                    <thead><tr><th><input type="checkbox" id="selectAllChecks"></th><th>Check #</th><th>Bank</th><th>Drawer</th><th>Amount</th><th>Date</th></tr></thead>
                    <tbody>
                    <?php foreach($receivedChecks as $ch): ?>
                    <tr><td><input type="checkbox" name="check_ids[]" value="<?= $ch['id'] ?>" class="check-select"></td><td><?= e($ch['check_number']) ?></td><td><?= e($ch['bank_name']) ?></td><td><?= e($ch['drawer_name']) ?></td><td><?= format_money($ch['amount']) ?></td><td><?= format_date($ch['check_date']) ?></td></tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
            <?php endif; ?>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/advanced-finance/deposits" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
<?php $__view->push('scripts') ?><script>
document.getElementById('selectAllChecks')?.addEventListener('change', function() {
    document.querySelectorAll('.check-select').forEach(c => c.checked = this.checked);
});
</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\deposits\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h5 class="mb-0"><?= lang('adv_finance.deposits') ?></h5>
    <?php if(auth_can('gl.manage')): ?><a href="/advanced-finance/deposits/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a><?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="dep-table" class="table table-striped w-100" data-url="/advanced-finance/deposits/datatable"
           data-columns='[{"data":"deposit_number","title":"Deposit #"},{"data":"bank_name","title":"Bank"},{"data":"deposit_date","title":"Date"},{"data":"total_amount","title":"Total"},{"data":"cash_amount","title":"Cash"},{"data":"check_amount","title":"Checks"},{"data":"status_badge","title":"Status","orderable":false},{"data":"id","title":"Actions","orderable":false,"searchable":false}]'>
        <thead><tr><th>Deposit #</th><th>Bank</th><th>Date</th><th>Total</th><th>Cash</th><th>Checks</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('dep-table',{columnDefs:[{targets:-1,render:function(d){return '<a href="/advanced-finance/deposits/'+d+'" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';}}]});});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\deposits\show.php

```php
<div class="row g-4">
    <div class="col-lg-5">
        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h6 class="mb-0"><i class="fas fa-university me-2"></i>
                    <?= e($dep['deposit_number']) ?>
                </h6>
                <?= status_badge($dep['status']) ?>
            </div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <tr>
                        <th width="40%">
                            <?= lang('adv_finance.bank_name') ?>
                        </th>
                        <td>
                            <?= e($dep['bank_name_en']) ?>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <?= lang('adv_finance.deposit_date') ?>
                        </th>
                        <td>
                            <?= format_date($dep['deposit_date']) ?>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <?= lang('adv_finance.total_amount') ?>
                        </th>
                        <td class="fw-bold fs-5">
                            <?= format_money($dep['total_amount']) ?>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <?= lang('adv_finance.cash_amount') ?>
                        </th>
                        <td>
                            <?= format_money($dep['cash_amount']) ?>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <?= lang('adv_finance.check_amount') ?>
                        </th>
                        <td>
                            <?= format_money($dep['check_amount']) ?> <span class="text-muted">(
                                <?= (int) $dep['check_count'] ?> checks)
                            </span>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            <?= lang('common.status') ?>
                        </th>
                        <td>
                            <?= status_badge($dep['status']) ?>
                        </td>
                    </tr>
                    <?php if ($dep['bank_reference']): ?>
                        <tr>
                            <th>
                                <?= lang('adv_finance.bank_reference') ?>
                            </th>
                            <td><code><?= e($dep['bank_reference']) ?></code></td>
                        </tr>
                    <?php endif; ?>
                    <?php if ($dep['notes']): ?>
                        <tr>
                            <th>Notes</th>
                            <td>
                                <?= e($dep['notes']) ?>
                            </td>
                        </tr>
                    <?php endif; ?>
                    <?php if ($dep['confirmed_at']): ?>
                        <tr>
                            <th>Confirmed</th>
                            <td>
                                <?= format_datetime($dep['confirmed_at']) ?>
                            </td>
                        </tr>
                    <?php endif; ?>
                </table>
            </div>
        </div>

        <?php if (in_array($dep['status'], ['prepared', 'deposited']) && auth_can('gl.manage')): ?>
            <div class="card mt-3">
                <div class="card-header">
                    <h6 class="mb-0"><i class="fas fa-check-circle me-2"></i>
                        <?= lang('adv_finance.confirm_deposit') ?>
                    </h6>
                </div>
                <div class="card-body">
                    <form method="POST" action="/advanced-finance/deposits/<?= $dep['id'] ?>/confirm" data-ajax="true">
                        <?= csrf_field() ?>
                        <div class="mb-3">
                            <label class="form-label">
                                <?= lang('adv_finance.bank_reference') ?>
                            </label>
                            <input type="text" name="bank_reference" class="form-control"
                                placeholder="Bank transaction reference...">
                        </div>
                        <button type="submit" class="btn btn-success"
                            onclick="return confirm('<?= lang('common.are_you_sure') ?>')">
                            <i class="fas fa-check me-1"></i>
                            <?= lang('adv_finance.confirm_deposit') ?>
                        </button>
                    </form>
                </div>
            </div>
        <?php endif; ?>
    </div>

    <div class="col-lg-7">
        <div class="card">
            <div class="card-header">
                <h6 class="mb-0"><i class="fas fa-list me-2"></i>Deposit Items</h6>
            </div>
            <div class="card-body p-0">
                <table class="table table-sm table-striped mb-0">
                    <thead>
                        <tr>
                            <th width="5%">#</th>
                            <th width="15%">Type</th>
                            <th width="25%">
                                <?= lang('adv_finance.amount') ?>
                            </th>
                            <th>Details</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php $lineNo = 1;
                        foreach ($items as $it): ?>
                            <tr>
                                <td>
                                    <?= $lineNo++ ?>
                                </td>
                                <td>
                                    <?php if ($it['item_type'] === 'cash'): ?>
                                        <span class="badge bg-success"><i class="fas fa-money-bill-wave me-1"></i>Cash</span>
                                    <?php else: ?>
                                        <span class="badge bg-info"><i class="fas fa-money-check me-1"></i>Check</span>
                                    <?php endif; ?>
                                </td>
                                <td class="fw-bold">
                                    <?= format_money($it['amount']) ?>
                                </td>
                                <td>
                                    <?php if ($it['item_type'] === 'check' && $it['check_number']): ?>
                                        <div>
                                            <strong>
                                                <?= lang('adv_finance.check_number') ?>:
                                            </strong>
                                            <?= e($it['check_number']) ?><br>
                                            <strong>
                                                <?= lang('adv_finance.drawer_name') ?>:
                                            </strong>
                                            <?= e($it['drawer_name'] ?? 'â€”') ?><br>
                                            <strong>
                                                <?= lang('adv_finance.bank_name') ?>:
                                            </strong>
                                            <?= e($it['check_bank'] ?? 'â€”') ?>
                                        </div>
                                    <?php else: ?>
                                        <span class="text-muted">â€”</span>
                                    <?php endif; ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        <?php if (empty($items)): ?>
                            <tr>
                                <td colspan="4" class="text-center text-muted py-4">No items in this deposit</td>
                            </tr>
                        <?php endif; ?>
                    </tbody>
                    <tfoot class="table-light">
                        <tr>
                            <td colspan="2" class="text-end fw-bold">Total:</td>
                            <td class="fw-bold">
                                <?= format_money($dep['total_amount']) ?>
                            </td>
                            <td></td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        </div>
    </div>
</div>
```

## File: modules\advanced_finance\views\installments\create.php

```php
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-file-invoice-dollar me-2"></i><?= lang('adv_finance.create_plan') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/advanced-finance/installments/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6">
                    <label class="form-label"><?= lang('adv_finance.member') ?> <span class="text-danger">*</span></label>
                    <select name="member_id" class="form-select select2-ajax" data-url="/advanced-finance/api/member-search" required></select>
                    <div class="invalid-feedback" data-field="member_id"></div>
                </div>
                <div class="col-md-6">
                    <label class="form-label"><?= lang('adv_finance.invoice') ?></label>
                    <select name="invoice_id" class="form-select select2-ajax" data-url="/advanced-finance/api/invoice-search"></select>
                    <div class="invalid-feedback" data-field="invoice_id"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('adv_finance.template') ?></label>
                    <select name="template_id" id="templateSelect" class="form-select">
                        <option value="">â€” Custom â€”</option>
                        <?php foreach ($templates as $t): ?>
                        <option value="<?= $t['id'] ?>"
                                data-installments="<?= $t['number_of_installments'] ?>"
                                data-frequency="<?= $t['frequency'] ?>"
                                data-downpct="<?= $t['down_payment_percentage'] ?>">
                            <?= e($t['name_en']) ?>
                        </option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('adv_finance.total_amount') ?> <span class="text-danger">*</span></label>
                    <input type="number" name="total_amount" class="form-control" step="0.01" required>
                    <div class="invalid-feedback" data-field="total_amount"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('adv_finance.down_payment') ?> <span class="text-danger">*</span></label>
                    <input type="number" name="down_payment" class="form-control" step="0.01" value="0" required>
                    <div class="invalid-feedback" data-field="down_payment"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('adv_finance.num_installments') ?> <span class="text-danger">*</span></label>
                    <input type="number" name="num_installments" class="form-control" min="1" max="120" required>
                    <div class="invalid-feedback" data-field="num_installments"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('adv_finance.frequency') ?> <span class="text-danger">*</span></label>
                    <select name="frequency" class="form-select" required>
                        <?php foreach ($frequencies as $f): ?>
                        <option value="<?= $f ?>"><?= ucfirst(str_replace('_', ' ', $f)) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <div class="invalid-feedback" data-field="frequency"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('adv_finance.start_date') ?> <span class="text-danger">*</span></label>
                    <input type="date" name="start_date" class="form-control" value="<?= date('Y-m-d') ?>" required>
                    <div class="invalid-feedback" data-field="start_date"></div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/advanced-finance/installments" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div>
</div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    AdvFinance.initMemberSelect('.select2-ajax[data-url*="member-search"]');
    AdvFinance.initMemberSelect('.select2-ajax[data-url*="invoice-search"]');

    document.getElementById('templateSelect').addEventListener('change', function() {
        const opt = this.options[this.selectedIndex];
        if (opt.value) {
            document.querySelector('[name="num_installments"]').value = opt.dataset.installments || '';
            document.querySelector('[name="frequency"]').value = opt.dataset.frequency || 'monthly';
        }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\installments\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-3">
        <div class="card border-primary h-100"><div class="card-body">
            <h6 class="text-muted mb-1"><?= lang('common.all') ?></h6>
            <h3 class="mb-0"><?= number_format($stats['total'] ?? 0) ?></h3>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-success h-100"><div class="card-body">
            <h6 class="text-muted mb-1">Active</h6>
            <h3 class="mb-0"><?= number_format($stats['active'] ?? 0) ?></h3>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100"><div class="card-body">
            <h6 class="text-muted mb-1">Defaulted</h6>
            <h3 class="mb-0"><?= number_format($stats['defaulted'] ?? 0) ?></h3>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-warning h-100"><div class="card-body">
            <h6 class="text-muted mb-1"><?= lang('adv_finance.remaining') ?></h6>
            <h3 class="mb-0"><?= format_money($stats['total_remaining'] ?? 0) ?></h3>
        </div></div>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body">
        <div class="row g-2 align-items-end" data-filter-table="installment-table">
            <div class="col-md-3">
                <label class="form-label"><?= lang('common.status') ?></label>
                <select name="status_filter" class="form-select form-select-sm" onchange="DataTableHelper.reload('installment-table')">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="active">Active</option>
                    <option value="completed">Completed</option>
                    <option value="defaulted">Defaulted</option>
                    <option value="cancelled">Cancelled</option>
                </select>
            </div>
            <div class="col-md-3 ms-auto text-end">
                <?php if (auth_can('installments.create')): ?>
                <a href="/advanced-finance/installments/create" class="btn btn-primary btn-sm">
                    <i class="fas fa-plus me-1"></i><?= lang('adv_finance.create_plan') ?>
                </a>
                <?php endif; ?>
            </div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="installment-table" class="table table-striped table-hover w-100"
               data-url="/advanced-finance/installments/datatable"
               data-columns='[
                   {"data":"plan_number","title":"<?= lang('adv_finance.plan_number') ?>"},
                   {"data":"member_name","title":"<?= lang('adv_finance.member') ?>"},
                   {"data":"total_amount","title":"<?= lang('adv_finance.total_amount') ?>"},
                   {"data":"total_paid","title":"<?= lang('adv_finance.total_paid') ?>"},
                   {"data":"total_remaining","title":"<?= lang('adv_finance.remaining') ?>"},
                   {"data":"installments","title":"<?= lang('adv_finance.num_installments') ?>"},
                   {"data":"next_due_date","title":"<?= lang('adv_finance.next_due') ?>"},
                   {"data":"status_badge","title":"<?= lang('common.status') ?>","orderable":false},
                   {"data":"id","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}
               ]'>
            <thead><tr>
                <th><?= lang('adv_finance.plan_number') ?></th>
                <th><?= lang('adv_finance.member') ?></th>
                <th><?= lang('adv_finance.total_amount') ?></th>
                <th><?= lang('adv_finance.total_paid') ?></th>
                <th><?= lang('adv_finance.remaining') ?></th>
                <th><?= lang('adv_finance.num_installments') ?></th>
                <th><?= lang('adv_finance.next_due') ?></th>
                <th><?= lang('common.status') ?></th>
                <th><?= lang('common.actions') ?></th>
            </tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('installment-table', {
        columnDefs: [{
            targets: -1,
            render: function(data, type, row) {
                return '<a href="/advanced-finance/installments/' + data + '" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';
            }
        }]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\installments\show.php

```php
<div class="row g-4">
    <div class="col-lg-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('adv_finance.plan_number') ?>: <?= e($plan['plan_number']) ?></h6></div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <tr><th><?= lang('adv_finance.member') ?></th><td><?= e($plan['member_name']) ?> <small class="text-muted">(<?= e($plan['membership_number']) ?>)</small></td></tr>
                    <tr><th><?= lang('common.status') ?></th><td><?= status_badge($plan['status']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.total_amount') ?></th><td><?= format_money($plan['total_amount']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.down_payment') ?></th><td><?= format_money($plan['down_payment']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.financed') ?></th><td><?= format_money($plan['financed_amount']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.total_paid') ?></th><td class="text-success fw-bold"><?= format_money($plan['total_paid']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.remaining') ?></th><td class="text-danger fw-bold"><?= format_money($plan['total_remaining']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.num_installments') ?></th><td><?= $plan['installments_paid'] ?>/<?= $plan['number_of_installments'] ?></td></tr>
                    <tr><th><?= lang('adv_finance.frequency') ?></th><td><?= ucfirst(str_replace('_', ' ', $plan['frequency'])) ?></td></tr>
                    <tr><th><?= lang('adv_finance.start_date') ?></th><td><?= format_date($plan['start_date']) ?></td></tr>
                    <tr><th><?= lang('adv_finance.next_due') ?></th><td><?= format_date($plan['next_due_date']) ?></td></tr>
                    <?php if ($plan['invoice_number']): ?>
                    <tr><th><?= lang('adv_finance.invoice') ?></th><td><?= e($plan['invoice_number']) ?></td></tr>
                    <?php endif; ?>
                </table>
                <?php if ($plan['status'] === 'active' && auth_can('installments.create')): ?>
                <div class="mt-3">
                    <form method="POST" action="/advanced-finance/installments/<?= $plan['id'] ?>/default" data-ajax="true"
                          onsubmit="return confirm('<?= lang('common.are_you_sure') ?>')">
                        <?= csrf_field() ?>
                        <button type="submit" class="btn btn-danger btn-sm"><i class="fas fa-exclamation-triangle me-1"></i><?= lang('adv_finance.mark_defaulted') ?></button>
                    </form>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-lg-8">
        <div class="card mb-4">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h6 class="mb-0"><?= lang('adv_finance.schedule') ?></h6>
                <?php if ($plan['status'] === 'active' && auth_can('installments.create')): ?>
                <button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#payModal"><i class="fas fa-plus me-1"></i><?= lang('adv_finance.record_payment') ?></button>
                <?php endif; ?>
            </div>
            <div class="card-body p-0">
                <table class="table table-sm table-striped mb-0">
                    <thead><tr>
                        <th><?= lang('adv_finance.installment_no') ?></th>
                        <th><?= lang('adv_finance.due_date') ?></th>
                        <th><?= lang('adv_finance.amount') ?></th>
                        <th><?= lang('adv_finance.paid') ?></th>
                        <th><?= lang('adv_finance.balance') ?></th>
                        <th><?= lang('common.status') ?></th>
                    </tr></thead>
                    <tbody>
                    <?php foreach ($schedules as $s): ?>
                    <tr class="<?= $s['status'] === 'overdue' ? 'table-danger' : '' ?>">
                        <td><?= $s['installment_number'] ?></td>
                        <td><?= format_date($s['due_date']) ?></td>
                        <td><?= format_money($s['amount']) ?></td>
                        <td><?= format_money($s['paid_amount']) ?></td>
                        <td><?= format_money($s['balance']) ?></td>
                        <td><?= status_badge($s['status']) ?></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>

        <?php if (!empty($payments)): ?>
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Payment History</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Inst. #</th><th>Payment #</th><th>Date</th><th>Amount</th><th>Late Fee</th><th>Method</th></tr></thead>
                    <tbody>
                    <?php foreach ($payments as $p): ?>
                    <tr>
                        <td><?= $p['installment_number'] ?></td>
                        <td><?= e($p['payment_number']) ?></td>
                        <td><?= format_date($p['payment_date']) ?></td>
                        <td><?= format_money($p['amount']) ?></td>
                        <td><?= format_money($p['late_fee_amount']) ?></td>
                        <td><?= e($p['method_name'] ?? 'â€”') ?></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>
    </div>
</div>

<?php if ($plan['status'] === 'active'): ?>
<div class="modal fade" id="payModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form method="POST" action="/advanced-finance/installments/<?= $plan['id'] ?>/pay" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-header"><h5 class="modal-title"><?= lang('adv_finance.record_payment') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
                <div class="modal-body">
                    <div class="mb-3">
                        <label class="form-label">Installment <span class="text-danger">*</span></label>
                        <select name="schedule_id" class="form-select" required>
                            <option value="">Select...</option>
                            <?php foreach ($schedules as $s): ?>
                            <?php if (in_array($s['status'], ['pending','partially_paid','overdue'])): ?>
                            <option value="<?= $s['id'] ?>">#<?= $s['installment_number'] ?> â€” Due: <?= format_date($s['due_date']) ?> â€” Balance: <?= format_money($s['balance']) ?></option>
                            <?php endif; ?>
                            <?php endforeach; ?>
                        </select>
                        <div class="invalid-feedback" data-field="schedule_id"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('adv_finance.payment_amount') ?> <span class="text-danger">*</span></label>
                        <input type="number" name="amount" class="form-control" step="0.01" required>
                        <div class="invalid-feedback" data-field="amount"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('adv_finance.late_fee') ?></label>
                        <input type="number" name="late_fee_amount" class="form-control" step="0.01" value="0">
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Payment Method <span class="text-danger">*</span></label>
                        <select name="payment_method_id" class="form-select" required>
                            <?php foreach ($paymentMethods as $pm): ?>
                            <option value="<?= $pm['id'] ?>"><?= e($pm['name_en']) ?></option>
                            <?php endforeach; ?>
                        </select>
                        <div class="invalid-feedback" data-field="payment_method_id"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Payment Date <span class="text-danger">*</span></label>
                        <input type="date" name="payment_date" class="form-control" value="<?= date('Y-m-d') ?>" required>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                    <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                </div>
            </form>
        </div>
    </div>
</div>
<?php endif; ?>
```

## File: modules\advanced_finance\views\installment-templates\form.php

```php
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $template ? lang('common.edit') : lang('common.create') ?> <?= lang('adv_finance.template') ?></h5></div>
    <div class="card-body">
        <form method="POST"
              action="<?= $template ? '/advanced-finance/installment-templates/' . $template['id'] : '/advanced-finance/installment-templates/store' ?>"
              data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4">
                    <label class="form-label">Code <span class="text-danger">*</span></label>
                    <input type="text" name="template_code" class="form-control" value="<?= e($template['template_code'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="template_code"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                    <input type="text" name="name_en" class="form-control" value="<?= e($template['name_en'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="name_en"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                    <input type="text" name="name_ar" class="form-control" value="<?= e($template['name_ar'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="name_ar"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label"># Installments <span class="text-danger">*</span></label>
                    <input type="number" name="number_of_installments" class="form-control" value="<?= e($template['number_of_installments'] ?? '12') ?>" min="1" max="120" required>
                    <div class="invalid-feedback" data-field="number_of_installments"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Frequency <span class="text-danger">*</span></label>
                    <select name="frequency" class="form-select" required>
                        <?php foreach (['weekly','biweekly','monthly','bimonthly','quarterly','semi_annual','annual'] as $f): ?>
                        <option value="<?= $f ?>" <?= selected($f, $template['frequency'] ?? 'monthly') ?>><?= ucfirst(str_replace('_', ' ', $f)) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Down Payment % <span class="text-danger">*</span></label>
                    <input type="number" name="down_payment_percentage" class="form-control" step="0.01" value="<?= e($template['down_payment_percentage'] ?? '0') ?>" required>
                    <div class="invalid-feedback" data-field="down_payment_percentage"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Interest Rate % <span class="text-danger">*</span></label>
                    <input type="number" name="interest_rate" class="form-control" step="0.01" value="<?= e($template['interest_rate'] ?? '0') ?>" required>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Late Payment Fee <span class="text-danger">*</span></label>
                    <input type="number" name="late_payment_fee" class="form-control" step="0.01" value="<?= e($template['late_payment_fee'] ?? '0') ?>" required>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Grace Days <span class="text-danger">*</span></label>
                    <input type="number" name="grace_days" class="form-control" value="<?= e($template['grace_days'] ?? '7') ?>" required>
                </div>
                <div class="col-md-4 d-flex align-items-end">
                    <div class="form-check">
                        <input class="form-check-input" type="checkbox" name="is_active" value="1" <?= checked(1, $template['is_active'] ?? 1) ?>>
                        <label class="form-check-label">Active</label>
                    </div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/advanced-finance/installment-templates" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div>
</div>
</div>
```

## File: modules\advanced_finance\views\installment-templates\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h5 class="mb-0"><?= lang('adv_finance.installment_templates') ?></h5>
    <a href="/advanced-finance/installment-templates/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card">
    <div class="card-body">
        <table id="template-table" class="table table-striped w-100"
               data-url="/advanced-finance/installment-templates/datatable"
               data-columns='[
                   {"data":"template_code","title":"Code"},
                   {"data":"name_en","title":"Name (EN)"},
                   {"data":"name_ar","title":"Name (AR)"},
                   {"data":"number_of_installments","title":"# Installments"},
                   {"data":"frequency","title":"Frequency"},
                   {"data":"down_payment_pct","title":"Down Payment %"},
                   {"data":"is_active","title":"Status","orderable":false},
                   {"data":"id","title":"Actions","orderable":false,"searchable":false}
               ]'>
            <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th># Inst.</th><th>Freq.</th><th>Down %</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('template-table', {
        columnDefs: [{
            targets: -1,
            render: function(data) {
                return '<a href="/advanced-finance/installment-templates/' + data + '/edit" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i></a>';
            }
        }]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\refunds\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= lang('adv_finance.create_refund') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/advanced-finance/refunds/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6"><label class="form-label">Member <span class="text-danger">*</span></label><select name="member_id" class="form-select select2-ajax" data-url="/advanced-finance/api/member-search" required></select><div class="invalid-feedback" data-field="member_id"></div></div>
                <div class="col-md-3"><label class="form-label">Amount <span class="text-danger">*</span></label><input type="number" name="refund_amount" class="form-control" step="0.01" required><div class="invalid-feedback" data-field="refund_amount"></div></div>
                <div class="col-md-3"><label class="form-label">Method <span class="text-danger">*</span></label><select name="refund_method_id" class="form-select" required><?php foreach($methods as $m): ?><option value="<?= $m['id'] ?>"><?= e($m['name_en']) ?></option><?php endforeach; ?></select></div>
                <div class="col-12"><label class="form-label">Reason <span class="text-danger">*</span></label><textarea name="reason" class="form-control" rows="3" required></textarea><div class="invalid-feedback" data-field="reason"></div></div>
                <div class="col-12"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/advanced-finance/refunds" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){AdvFinance.initMemberSelect('.select2-ajax');});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\refunds\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h5 class="mb-0"><?= lang('adv_finance.refunds') ?></h5>
    <?php if(auth_can('refunds.create')): ?><a href="/advanced-finance/refunds/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('adv_finance.create_refund') ?></a><?php endif; ?>
</div>
<div class="card mb-3"><div class="card-body"><div class="row g-2" data-filter-table="refund-table"><div class="col-md-3"><select name="status_filter" class="form-select form-select-sm" onchange="DataTableHelper.reload('refund-table')"><option value="">All</option><option value="pending">Pending</option><option value="approved">Approved</option><option value="processed">Processed</option><option value="rejected">Rejected</option></select></div></div></div></div>
<div class="card"><div class="card-body">
    <table id="refund-table" class="table table-striped w-100" data-url="/advanced-finance/refunds/datatable"
           data-columns='[{"data":"refund_number","title":"Refund #"},{"data":"member_name","title":"Member"},{"data":"refund_amount","title":"Amount"},{"data":"refund_date","title":"Date"},{"data":"method_name","title":"Method"},{"data":"status_badge","title":"Status","orderable":false},{"data":"id","title":"Actions","orderable":false,"searchable":false}]'>
        <thead><tr><th>Refund #</th><th>Member</th><th>Amount</th><th>Date</th><th>Method</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('refund-table',{columnDefs:[{targets:-1,render:function(d){return '<a href="/advanced-finance/refunds/'+d+'" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';}}]});});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\refunds\show.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h6 class="mb-0"><?= e($refund['refund_number']) ?></h6></div>
    <div class="card-body">
        <table class="table table-sm">
            <tr><th>Member</th><td><?= e($refund['member_name']) ?> (<?= e($refund['membership_number']) ?>)</td></tr>
            <tr><th>Amount</th><td class="fw-bold"><?= format_money($refund['refund_amount']) ?></td></tr>
            <tr><th>Date</th><td><?= format_date($refund['refund_date']) ?></td></tr>
            <tr><th>Method</th><td><?= e($refund['method_name']) ?></td></tr>
            <tr><th>Status</th><td><?= status_badge($refund['status']) ?></td></tr>
            <tr><th>Reason</th><td><?= e($refund['reason']) ?></td></tr>
            <?php if($refund['original_payment_number']): ?><tr><th>Orig. Payment</th><td><?= e($refund['original_payment_number']) ?></td></tr><?php endif; ?>
            <?php if($refund['original_invoice_number']): ?><tr><th>Orig. Invoice</th><td><?= e($refund['original_invoice_number']) ?></td></tr><?php endif; ?>
            <?php if($refund['reference_number']): ?><tr><th>Ref #</th><td><?= e($refund['reference_number']) ?></td></tr><?php endif; ?>
            <?php if($refund['rejection_reason']): ?><tr><th>Rejection</th><td class="text-danger"><?= e($refund['rejection_reason']) ?></td></tr><?php endif; ?>
        </table>
        <div class="d-flex gap-2 mt-3">
            <?php if($refund['status'] === 'pending' && auth_can('refunds.approve')): ?>
            <form method="POST" action="/advanced-finance/refunds/<?= $refund['id'] ?>/approve" data-ajax="true"><?= csrf_field() ?><button class="btn btn-success btn-sm"><i class="fas fa-check me-1"></i><?= lang('adv_finance.approve') ?></button></form>
            <button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#rejectModal"><i class="fas fa-times me-1"></i><?= lang('adv_finance.reject') ?></button>
            <?php endif; ?>
            <?php if($refund['status'] === 'approved' && auth_can('refunds.approve')): ?>
            <button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#processModal"><i class="fas fa-cog me-1"></i><?= lang('adv_finance.process') ?></button>
            <?php endif; ?>
        </div>
    </div>
</div></div></div>

<?php if($refund['status'] === 'pending'): ?>
<div class="modal fade" id="rejectModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <form method="POST" action="/advanced-finance/refunds/<?= $refund['id'] ?>/reject" data-ajax="true"><?= csrf_field() ?>
        <div class="modal-header"><h5 class="modal-title"><?= lang('adv_finance.reject') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body"><label class="form-label">Reason</label><textarea name="rejection_reason" class="form-control" rows="3" required></textarea></div>
        <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button><button type="submit" class="btn btn-danger"><?= lang('adv_finance.reject') ?></button></div>
    </form>
</div></div></div>
<?php endif; ?>

<?php if($refund['status'] === 'approved'): ?>
<div class="modal fade" id="processModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <form method="POST" action="/advanced-finance/refunds/<?= $refund['id'] ?>/process" data-ajax="true"><?= csrf_field() ?>
        <div class="modal-header"><h5 class="modal-title"><?= lang('adv_finance.process') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body"><label class="form-label">Reference Number</label><input type="text" name="reference_number" class="form-control"></div>
        <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button><button type="submit" class="btn btn-primary"><?= lang('adv_finance.process') ?></button></div>
    </form>
</div></div></div>
<?php endif; ?>
```

## File: modules\advanced_finance\views\write-offs\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= lang('adv_finance.create_write_off') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/advanced-finance/write-offs/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6"><label class="form-label">Member <span class="text-danger">*</span></label><select name="member_id" class="form-select select2-ajax" data-url="/advanced-finance/api/member-search" required></select><div class="invalid-feedback" data-field="member_id"></div></div>
                <div class="col-md-6"><label class="form-label">Invoice</label><select name="invoice_id" class="form-select select2-ajax" data-url="/advanced-finance/api/invoice-search"></select></div>
                <div class="col-md-4"><label class="form-label">Amount <span class="text-danger">*</span></label><input type="number" name="amount" class="form-control" step="0.01" required><div class="invalid-feedback" data-field="amount"></div></div>
                <div class="col-12"><label class="form-label">Reason <span class="text-danger">*</span></label><textarea name="reason" class="form-control" rows="3" required></textarea><div class="invalid-feedback" data-field="reason"></div></div>
                <div class="col-12"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/advanced-finance/write-offs" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){AdvFinance.initMemberSelect('.select2-ajax');});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\write-offs\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h5 class="mb-0"><?= lang('adv_finance.write_offs') ?></h5>
    <?php if(auth_can('installments.create')): ?><a href="/advanced-finance/write-offs/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('adv_finance.create_write_off') ?></a><?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="wo-table" class="table table-striped w-100" data-url="/advanced-finance/write-offs/datatable"
           data-columns='[{"data":"write_off_number","title":"WO #"},{"data":"member_name","title":"Member"},{"data":"invoice_number","title":"Invoice"},{"data":"amount","title":"Amount"},{"data":"status_badge","title":"Status","orderable":false},{"data":"id","title":"Actions","orderable":false,"searchable":false}]'>
        <thead><tr><th>WO #</th><th>Member</th><th>Invoice</th><th>Amount</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('wo-table',{columnDefs:[{targets:-1,render:function(d){return '<a href="/advanced-finance/write-offs/'+d+'" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';}}]});});</script><?php $__view->endPush() ?>
```

## File: modules\advanced_finance\views\write-offs\show.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h6 class="mb-0"><?= e($wo['write_off_number']) ?></h6></div>
    <div class="card-body">
        <table class="table table-sm">
            <tr><th>Member</th><td><?= e($wo['member_name']) ?> (<?= e($wo['membership_number']) ?>)</td></tr>
            <tr><th>Invoice</th><td><?= e($wo['invoice_number'] ?? 'â€”') ?></td></tr>
            <tr><th>Amount</th><td class="fw-bold"><?= format_money($wo['amount']) ?></td></tr>
            <tr><th>Reason</th><td><?= e($wo['reason']) ?></td></tr>
            <tr><th>Status</th><td><?= status_badge($wo['status']) ?></td></tr>
        </table>
        <div class="d-flex gap-2 mt-3">
            <?php if($wo['status'] === 'pending' && auth_can('refunds.approve')): ?>
            <form method="POST" action="/advanced-finance/write-offs/<?= $wo['id'] ?>/approve" data-ajax="true"><?= csrf_field() ?><button class="btn btn-success btn-sm"><i class="fas fa-check me-1"></i><?= lang('adv_finance.approve') ?></button></form>
            <?php endif; ?>
            <?php if($wo['status'] === 'approved' && auth_can('gl.manage')): ?>
            <form method="POST" action="/advanced-finance/write-offs/<?= $wo['id'] ?>/post" data-ajax="true"><?= csrf_field() ?><button class="btn btn-primary btn-sm"><i class="fas fa-book me-1"></i><?= lang('adv_finance.post') ?></button></form>
            <?php endif; ?>
        </div>
    </div>
</div></div></div>
```

## File: modules\applications\Controllers\ApplicationApiController.php

```php
<?php
class ApplicationApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();

        $draw    = (int) $request->get('draw', 1);
        $start   = (int) $request->get('start', 0);
        $length  = (int) $request->get('length', 25);
        $search  = $request->get('search')['value'] ?? '';
        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = ($request->get('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

        $columns = ['a.id', 'a.application_number', 'a.full_name_en', 'a.primary_phone', 'mt.name_en', 'a.status', 'a.priority', 'a.submission_date', 'u.full_name_en'];
        $orderColumn = $columns[$orderCol] ?? 'a.id';

        $where = "a.is_deleted = 0";
        $params = [];

        // Filters
        $statusFilter = $request->get('status');
        if ($statusFilter) {
            $where .= " AND a.status = ?";
            $params[] = $statusFilter;
        }
        $typeFilter = $request->get('membership_type_id');
        if ($typeFilter) {
            $where .= " AND a.membership_type_id = ?";
            $params[] = $typeFilter;
        }
        $priorityFilter = $request->get('priority');
        if ($priorityFilter) {
            $where .= " AND a.priority = ?";
            $params[] = $priorityFilter;
        }
        $dateFrom = $request->get('date_from');
        if ($dateFrom) {
            $where .= " AND a.created_at >= ?";
            $params[] = $dateFrom . ' 00:00:00';
        }
        $dateTo = $request->get('date_to');
        if ($dateTo) {
            $where .= " AND a.created_at <= ?";
            $params[] = $dateTo . ' 23:59:59';
        }
        $assignedTo = $request->get('assigned_to');
        if ($assignedTo) {
            $where .= " AND a.assigned_to = ?";
            $params[] = $assignedTo;
        }

        if ($search) {
            $where .= " AND (a.application_number LIKE ? OR a.full_name_en LIKE ? OR a.full_name_ar LIKE ? OR a.national_id LIKE ? OR a.primary_phone LIKE ? OR a.email LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s, $s, $s]);
        }

        $countSql = "SELECT COUNT(*) FROM applications a WHERE {$where}";
        $totalFiltered = (int) $db->fetchColumn($countSql, $params);
        $totalRecords = (int) $db->fetchColumn("SELECT COUNT(*) FROM applications WHERE is_deleted = 0");

        $sql = "SELECT a.id, a.application_number, a.full_name_en, a.full_name_ar, a.national_id,
                       a.primary_phone, a.email, a.status, a.priority, a.submission_date,
                       a.source, a.created_at, a.photo_path,
                       mt.name_en as type_name_en, mt.name_ar as type_name_ar,
                       u.full_name_en as assigned_to_name
                FROM applications a
                LEFT JOIN membership_types mt ON a.membership_type_id = mt.id
                LEFT JOIN auth_users u ON a.assigned_to = u.id
                WHERE {$where}
                ORDER BY {$orderColumn} {$orderDir}
                LIMIT " . (int)$length . " OFFSET " . (int)$start;

        $rows = $db->fetchAll($sql, $params);

        $data = [];
        foreach ($rows as $row) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<a href="/applications/' . $row['id'] . '" class="btn btn-outline-primary btn-sm" data-bs-toggle="tooltip" title="View"><i class="fas fa-eye"></i></a>';
            if (in_array($row['status'], ['draft', 'submitted', 'documents_pending'])) {
                $actions .= '<a href="/applications/' . $row['id'] . '/edit" class="btn btn-outline-warning btn-sm" data-bs-toggle="tooltip" title="Edit"><i class="fas fa-edit"></i></a>';
            }
            $actions .= '</div>';

            $data[] = [
                'id'               => $row['id'],
                'application_number' => $row['application_number'],
                'full_name'        => '<div>' . e($row['full_name_en']) . '</div>' . ($row['full_name_ar'] ? '<small class="text-muted">' . e($row['full_name_ar']) . '</small>' : ''),
                'phone'            => e($row['primary_phone'] ?? ''),
                'type'             => e(current_lang() === 'ar' ? $row['type_name_ar'] : $row['type_name_en']),
                'status'           => status_badge($row['status']),
                'priority'         => status_badge($row['priority']),
                'submission_date'  => $row['submission_date'] ? format_date($row['submission_date']) : '<span class="text-muted">-</span>',
                'assigned_to'      => e($row['assigned_to_name'] ?? '-'),
                'actions'          => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalRecords,
            'recordsFiltered' => $totalFiltered,
            'data'            => $data,
        ]);
    }

    public function stats(Request $request): void
    {
        $db = $this->db();
        $stats = $db->fetchOne(
            "SELECT
                SUM(CASE WHEN status = 'submitted' THEN 1 ELSE 0 END) as submitted,
                SUM(CASE WHEN status = 'under_review' THEN 1 ELSE 0 END) as under_review,
                SUM(CASE WHEN status = 'board_pending' THEN 1 ELSE 0 END) as board_pending,
                SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as approved,
                SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected,
                SUM(CASE WHEN status = 'documents_pending' THEN 1 ELSE 0 END) as documents_pending,
                SUM(CASE WHEN status = 'interview_scheduled' THEN 1 ELSE 0 END) as interview_scheduled,
                SUM(CASE WHEN status = 'draft' THEN 1 ELSE 0 END) as draft,
                COUNT(*) as total
             FROM applications WHERE is_deleted = 0"
        );

        $this->json(['success' => true, 'data' => $stats]);
    }

    public function submit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }
        if ($app['status'] !== 'draft') {
            $this->json(['success' => false, 'message' => 'Application can only be submitted from draft status'], 422);
            return;
        }

        $db->update('applications', [
            'status'            => 'submitted',
            'status_reason'     => 'Application submitted for review',
            'status_changed_at' => date('Y-m-d H:i:s'),
            'status_changed_by' => Auth::id(),
            'submission_date'   => date('Y-m-d'),
            'applicant_declaration' => 1,
            'declaration_date'  => date('Y-m-d'),
        ], 'id = ?', [$id]);

        $this->audit('update', 'applications', $id, "Application submitted: {$app['application_number']}");
        $this->json(['success' => true, 'message' => lang('applications.submitted_success')]);
    }

    public function changeStatus(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $newStatus = $request->post('status');
        $reason = $request->post('reason', '');
        $notes = $request->post('notes', '');

        $validStatuses = ['submitted', 'under_review', 'documents_pending', 'interview_scheduled',
                          'interview_completed', 'board_pending', 'approved', 'rejected', 'waitlisted', 'cancelled'];

        if (!in_array($newStatus, $validStatuses)) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 422);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }

        $updateData = [
            'status'            => $newStatus,
            'status_reason'     => $reason,
            'status_changed_at' => date('Y-m-d H:i:s'),
            'status_changed_by' => Auth::id(),
            'internal_notes'    => $notes ? ($app['internal_notes'] ? $app['internal_notes'] . "\n" . $notes : $notes) : $app['internal_notes'],
        ];

        if ($newStatus === 'approved') {
            $updateData['approval_date'] = date('Y-m-d');
        }
        if ($newStatus === 'rejected') {
            $updateData['rejection_date'] = date('Y-m-d');
            $updateData['rejection_reason'] = $reason;
        }

        $assignedTo = $request->post('assigned_to');
        if ($assignedTo) {
            $updateData['assigned_to'] = $assignedTo;
        }

        $db->update('applications', $updateData, 'id = ?', [$id]);
        $this->audit('update', 'applications', $id, "Status changed to {$newStatus}: {$app['application_number']}", ['status' => $app['status']], ['status' => $newStatus]);

        $this->json(['success' => true, 'message' => lang('applications.status_changed')]);
    }

    public function review(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'review_type' => 'required|in:initial,document,background,final',
            'decision'    => 'required|in:approve,reject,return,escalate',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }

        $db->insert('application_reviews', [
            'application_id' => $id,
            'reviewer_id'    => Auth::id(),
            'review_type'    => $request->post('review_type'),
            'decision'       => $request->post('decision'),
            'comments'       => $request->post('comments', ''),
        ]);

        $decision = $request->post('decision');
        $statusMap = [
            'approve'  => $app['status'] === 'under_review' ? 'board_pending' : $app['status'],
            'reject'   => 'rejected',
            'return'   => 'documents_pending',
            'escalate' => 'board_pending',
        ];
        $newStatus = $statusMap[$decision] ?? $app['status'];

        if ($newStatus !== $app['status']) {
            $db->update('applications', [
                'status'            => $newStatus,
                'status_reason'     => "Review decision: {$decision}",
                'status_changed_at' => date('Y-m-d H:i:s'),
                'status_changed_by' => Auth::id(),
            ], 'id = ?', [$id]);
        }

        $this->audit('update', 'applications', $id, "Review recorded: {$decision}");
        $this->json(['success' => true, 'message' => lang('applications.review_recorded')]);
    }

    public function interview(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }

        $action = $request->post('action', 'schedule');

        if ($action === 'schedule') {
            $db->insert('application_interviews', [
                'application_id' => $id,
                'scheduled_date' => $request->post('scheduled_date'),
                'location'       => $request->post('location', ''),
                'status'         => 'scheduled',
                'recorded_by'    => Auth::id(),
            ]);

            if ($app['status'] !== 'interview_scheduled') {
                $db->update('applications', [
                    'status' => 'interview_scheduled',
                    'status_changed_at' => date('Y-m-d H:i:s'),
                    'status_changed_by' => Auth::id(),
                ], 'id = ?', [$id]);
            }
        } elseif ($action === 'record') {
            $interviewId = $request->post('interview_id');
            $db->update('application_interviews', [
                'actual_date'       => date('Y-m-d H:i:s'),
                'status'            => 'completed',
                'interview_notes'   => $request->post('interview_notes', ''),
                'rating'            => $request->post('rating') ?: null,
                'recommendation'    => $request->post('recommendation', 'defer'),
                'recorded_by'       => Auth::id(),
            ], 'id = ?', [$interviewId]);

            $db->update('applications', [
                'status' => 'interview_completed',
                'status_changed_at' => date('Y-m-d H:i:s'),
                'status_changed_by' => Auth::id(),
            ], 'id = ?', [$id]);
        }

        $this->audit('update', 'applications', $id, "Interview {$action}");
        $this->json(['success' => true, 'message' => lang('applications.interview_saved')]);
    }

    public function recordBoardVote(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }

        $votes = $request->post('votes', []);
        if (!is_array($votes) || empty($votes)) {
            $this->json(['success' => false, 'message' => 'No votes provided'], 422);
            return;
        }

        $db->beginTransaction();
        try {
            foreach ($votes as $userId => $vote) {
                $voteValue = $vote['vote'] ?? 'abstain';
                $comments = $vote['comments'] ?? '';

                $existing = $db->fetchOne(
                    "SELECT id FROM application_board_votes WHERE application_id = ? AND board_member_user_id = ?",
                    [$id, $userId]
                );

                if ($existing) {
                    $db->update('application_board_votes', [
                        'vote'     => $voteValue,
                        'comments' => $comments,
                        'voted_at' => date('Y-m-d H:i:s'),
                    ], 'id = ?', [$existing['id']]);
                } else {
                    $db->insert('application_board_votes', [
                        'application_id'       => $id,
                        'board_member_user_id' => $userId,
                        'vote'                 => $voteValue,
                        'comments'             => $comments,
                    ]);
                }
            }

            // Calculate result
            $voteCounts = $db->fetchOne(
                "SELECT
                    SUM(CASE WHEN vote = 'approve' THEN 1 ELSE 0 END) as approve_count,
                    SUM(CASE WHEN vote = 'reject' THEN 1 ELSE 0 END) as reject_count,
                    SUM(CASE WHEN vote = 'abstain' THEN 1 ELSE 0 END) as abstain_count,
                    COUNT(*) as total
                 FROM application_board_votes WHERE application_id = ?",
                [$id]
            );

            $approves = (int) $voteCounts['approve_count'];
            $rejects = (int) $voteCounts['reject_count'];

            if ($approves > $rejects) {
                $db->update('applications', [
                    'status' => 'approved',
                    'status_reason' => "Board approved: {$approves} for, {$rejects} against",
                    'status_changed_at' => date('Y-m-d H:i:s'),
                    'status_changed_by' => Auth::id(),
                    'approval_date' => date('Y-m-d'),
                ], 'id = ?', [$id]);
            } elseif ($rejects > $approves) {
                $db->update('applications', [
                    'status' => 'rejected',
                    'status_reason' => "Board rejected: {$rejects} against, {$approves} for",
                    'status_changed_at' => date('Y-m-d H:i:s'),
                    'status_changed_by' => Auth::id(),
                    'rejection_date' => date('Y-m-d'),
                    'rejection_reason' => "Board vote: {$rejects} reject vs {$approves} approve",
                ], 'id = ?', [$id]);
            }

            $db->commit();
            $this->audit('update', 'applications', $id, "Board votes recorded");
            $this->json(['success' => true, 'message' => lang('applications.votes_recorded')]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Board vote failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to record votes'], 500);
        }
    }

    public function createMember(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }
        if ($app['status'] !== 'approved') {
            $this->json(['success' => false, 'message' => 'Application must be approved first'], 422);
            return;
        }
        if ($app['resulting_member_id']) {
            $this->json(['success' => false, 'message' => 'Member already created from this application'], 422);
            return;
        }

        try {
            $db->execute("CALL sp_create_member_from_application(?, ?, @member_id, @result_msg)", [$id, Auth::id()]);
            $result = $db->fetchOne("SELECT @member_id AS member_id, @result_msg AS result_msg");

            if ($result['member_id']) {
                $this->audit('create', 'members', (int)$result['member_id'], "Member created from application: {$app['application_number']}");
                $this->json([
                    'success'  => true,
                    'message'  => $result['result_msg'],
                    'redirect' => '/members/' . $result['member_id'],
                ]);
            } else {
                $this->json(['success' => false, 'message' => $result['result_msg'] ?? 'Failed to create member'], 422);
            }
        } catch (\Exception $e) {
            Logger::error('Create member from application failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create member: ' . $e->getMessage()], 500);
        }
    }

    public function delete(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }
        if (!in_array($app['status'], ['draft', 'cancelled'])) {
            $this->json(['success' => false, 'message' => 'Only draft or cancelled applications can be deleted'], 422);
            return;
        }

        $db->update('applications', ['is_deleted' => 1], 'id = ?', [$id]);
        $this->audit('delete', 'applications', $id, "Application deleted: {$app['application_number']}");
        $this->json(['success' => true, 'message' => lang('applications.deleted_success'), 'redirect' => '/applications']);
    }

    public function getDocuments(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $documents = $db->fetchAll(
            "SELECT ad.*, au.full_name_en as verified_by_name
             FROM application_documents ad
             LEFT JOIN auth_users au ON ad.verified_by = au.id
             WHERE ad.application_id = ? ORDER BY ad.uploaded_at DESC",
            [$id]
        );
        $this->json(['success' => true, 'data' => $documents]);
    }

    public function uploadDocument(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $file = $request->file('document_file');
        if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
            $this->json(['success' => false, 'message' => 'No file uploaded'], 422);
            return;
        }

        $upload = new FileUpload($file);
        $upload->setAllowedTypes(['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx'])
               ->setMaxSize(10 * 1024 * 1024)
               ->setDestination('uploads/applications/documents/' . $id);

        if (!$upload->validate()) {
            $this->json(['success' => false, 'message' => implode(', ', $upload->errors())], 422);
            return;
        }

        $path = $upload->save();
        if (!$path) {
            $this->json(['success' => false, 'message' => 'File upload failed'], 500);
            return;
        }

        $db = $this->db();
        $docId = $db->insert('application_documents', [
            'application_id'    => $id,
            'document_type'     => $request->post('document_type', 'general'),
            'document_name'     => $request->post('document_name', $upload->originalName()),
            'file_path'         => $path,
            'file_original_name'=> $upload->originalName(),
            'file_size_bytes'   => $upload->size(),
            'mime_type'         => $upload->mimeType(),
            'uploaded_by'       => Auth::id(),
        ]);

        $this->audit('create', 'application_documents', $docId, "Document uploaded for application #{$id}");
        $this->json(['success' => true, 'message' => lang('applications.document_uploaded')]);
    }

    public function deleteDocument(Request $request): void
    {
        $docId = (int) $request->routeParam('docId');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $db->delete('application_documents', 'id = ?', [$docId]);
        $this->json(['success' => true, 'message' => lang('applications.document_deleted')]);
    }

    public function verifyDocument(Request $request): void
    {
        $docId = (int) $request->routeParam('docId');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $db->update('application_documents', [
            'is_verified' => 1,
            'verified_by' => Auth::id(),
            'verified_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$docId]);

        $this->json(['success' => true, 'message' => lang('applications.document_verified')]);
    }

    public function export(Request $request): void
    {
        $db = $this->db();
        $status = $request->get('status', '');
        $where = "a.is_deleted = 0";
        $params = [];
        if ($status) {
            $where .= " AND a.status = ?";
            $params[] = $status;
        }

        $rows = $db->fetchAll(
            "SELECT a.application_number, a.full_name_en, a.full_name_ar, a.national_id,
                    a.primary_phone, a.email, a.status, a.priority, a.submission_date,
                    a.source, a.created_at, mt.name_en as type
             FROM applications a
             LEFT JOIN membership_types mt ON a.membership_type_id = mt.id
             WHERE {$where} ORDER BY a.id DESC",
            $params
        );

        $format = $request->get('format', 'csv');
        $headers = ['App #', 'Name (EN)', 'Name (AR)', 'National ID', 'Phone', 'Email', 'Status', 'Priority', 'Submission Date', 'Source', 'Type', 'Created'];

        if ($format === 'excel') {
            Export::excel('applications_export.xls', $headers, $rows);
        } else {
            Export::csv('applications_export.csv', $headers, $rows);
        }
    }
}
```

## File: modules\applications\Controllers\ApplicationController.php

```php
<?php
class ApplicationController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $membershipTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");
        $users = $db->fetchAll("SELECT id, full_name_en FROM auth_users WHERE is_active = 1 AND is_deleted = 0 ORDER BY full_name_en");

        $this->view('index', [
            'title' => lang('applications.title'),
            'breadcrumbs' => [
                ['label' => lang('applications.title')]
            ],
            'membershipTypes' => $membershipTypes,
            'users' => $users,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $membershipTypes = $db->fetchAll(
            "SELECT mt.*, mc.name_en as category_name_en, mc.name_ar as category_name_ar
             FROM membership_types mt
             LEFT JOIN membership_categories mc ON mt.category_id = mc.id
             WHERE mt.is_active = 1
             ORDER BY mt.sort_order"
        );
        $nationalities = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_nationalities WHERE is_active = 1 ORDER BY name_en");
        $countries = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_countries WHERE is_active = 1 ORDER BY sort_order, name_en");
        $periods = $db->fetchAll("SELECT id, period_name_en, period_name_ar FROM application_periods WHERE is_active = 1 AND end_date >= CURDATE() ORDER BY start_date");

        $this->view('create', [
            'title' => lang('applications.create'),
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => lang('applications.create')]
            ],
            'membershipTypes' => $membershipTypes,
            'nationalities' => $nationalities,
            'countries' => $countries,
            'periods' => $periods,
            'application' => null,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'membership_type_id'  => 'required|integer|exists:membership_types,id',
            'first_name_en'       => 'required|string|max:100',
            'last_name_en'        => 'required|string|max:100',
            'gender'              => 'required|in:male,female',
            'primary_phone'       => 'required|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = $this->db();

        try {
            $db->beginTransaction();

            // Generate application number
            $db->execute("CALL sp_generate_next_sequence('application_number', @app_num)");
            $result = $db->fetchOne("SELECT @app_num AS val");
            $appNumber = $result['val'] ?? ('APP-' . time());

            $data = [
                'application_number'   => $appNumber,
                'application_period_id'=> $request->post('application_period_id') ?: null,
                'membership_type_id'   => $request->post('membership_type_id'),
                'form_number'          => $request->post('form_number') ?: null,
                'title'                => $request->post('title') ?: null,
                'first_name_en'        => $request->post('first_name_en'),
                'second_name_en'       => $request->post('second_name_en') ?: null,
                'third_name_en'        => $request->post('third_name_en') ?: null,
                'last_name_en'         => $request->post('last_name_en'),
                'full_name_en'         => '',
                'first_name_ar'        => $request->post('first_name_ar') ?: null,
                'second_name_ar'       => $request->post('second_name_ar') ?: null,
                'third_name_ar'        => $request->post('third_name_ar') ?: null,
                'last_name_ar'         => $request->post('last_name_ar') ?: null,
                'national_id'          => $request->post('national_id') ?: null,
                'passport_number'      => $request->post('passport_number') ?: null,
                'nationality_id'       => $request->post('nationality_id') ?: null,
                'date_of_birth'        => $request->post('date_of_birth') ?: null,
                'gender'               => $request->post('gender'),
                'marital_status'       => $request->post('marital_status') ?: null,
                'primary_phone'        => $request->post('primary_phone'),
                'secondary_phone'      => $request->post('secondary_phone') ?: null,
                'email'                => $request->post('email') ?: null,
                'address_line1'        => $request->post('address_line1') ?: null,
                'city_id'              => $request->post('city_id') ?: null,
                'country_id'           => $request->post('country_id') ?: null,
                'occupation'           => $request->post('occupation') ?: null,
                'employer'             => $request->post('employer') ?: null,
                'education_level'      => $request->post('education_level') ?: null,
                'priority'             => $request->post('priority') ?: 'normal',
                'source'               => $request->post('source') ?: 'walk_in',
                'status'               => 'draft',
                'ip_address'           => $request->ip(),
                'created_by'           => Auth::id(),
            ];

            // Handle photo upload
            $photoFile = $request->file('photo');
            if ($photoFile && $photoFile['error'] === UPLOAD_ERR_OK) {
                $upload = new FileUpload($photoFile);
                $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'webp'])
                       ->setMaxSize(5 * 1024 * 1024)
                       ->setDestination('uploads/applications/photos');
                if ($upload->validate()) {
                    $path = $upload->save();
                    if ($path) {
                        $data['photo_path'] = $path;
                    }
                }
            }

            $appId = $db->insert('applications', $data);

            // Handle sponsors
            $sponsor1 = $request->post('sponsor1_member_id');
            $sponsor2 = $request->post('sponsor2_member_id');
            if ($sponsor1) {
                $db->update('applications', ['sponsor1_member_id' => $sponsor1], 'id = ?', [$appId]);
                $db->insert('application_sponsors', [
                    'application_id'    => $appId,
                    'sponsor_member_id' => $sponsor1,
                    'sponsor_order'     => 1,
                    'status'            => 'pending',
                ]);
            }
            if ($sponsor2) {
                $db->update('applications', ['sponsor2_member_id' => $sponsor2], 'id = ?', [$appId]);
                $db->insert('application_sponsors', [
                    'application_id'    => $appId,
                    'sponsor_member_id' => $sponsor2,
                    'sponsor_order'     => 2,
                    'status'            => 'pending',
                ]);
            }

            // Calculate fees
            $fees = $db->fetchAll(
                "SELECT mtf.*, ft.fee_code, ft.name_en, ft.name_ar
                 FROM membership_type_fees mtf
                 JOIN fee_types ft ON mtf.fee_type_id = ft.id
                 WHERE mtf.membership_type_id = ? AND mtf.is_active = 1
                   AND mtf.effective_from <= CURDATE()
                   AND (mtf.effective_to IS NULL OR mtf.effective_to >= CURDATE())
                   AND mtf.is_mandatory = 1",
                [$request->post('membership_type_id')]
            );

            $totalFees = 0;
            foreach ($fees as $fee) {
                $amount = (float) $fee['amount'];
                $totalFees += $amount;
                $db->insert('application_fees', [
                    'application_id'  => $appId,
                    'fee_type_id'     => $fee['fee_type_id'],
                    'description_en'  => $fee['name_en'],
                    'description_ar'  => $fee['name_ar'],
                    'amount'          => $amount,
                    'is_paid'         => 0,
                ]);
            }

            if ($totalFees > 0) {
                $db->update('applications', ['total_fees_calculated' => $totalFees], 'id = ?', [$appId]);
            }

            $this->audit('create', 'applications', $appId, "Application created: {$appNumber}");

            $db->commit();

            $this->json([
                'success'  => true,
                'message'  => lang('applications.created_success'),
                'redirect' => '/applications/' . $appId,
            ]);

        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Application creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create application'], 500);
        }
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $app = $db->fetchOne(
            "SELECT a.*,
                    mt.name_en as type_name_en, mt.name_ar as type_name_ar, mt.type_code,
                    mt.requires_sponsor, mt.sponsor_count, mt.requires_board_approval, mt.requires_interview,
                    mc.name_en as category_name_en, mc.name_ar as category_name_ar,
                    n.name_en as nationality_name_en, n.name_ar as nationality_name_ar,
                    c.name_en as city_name_en, co.name_en as country_name_en,
                    u.full_name_en as assigned_to_name,
                    rm.membership_number as resulting_member_number
             FROM applications a
             LEFT JOIN membership_types mt ON a.membership_type_id = mt.id
             LEFT JOIN membership_categories mc ON mt.category_id = mc.id
             LEFT JOIN sys_nationalities n ON a.nationality_id = n.id
             LEFT JOIN sys_cities c ON a.city_id = c.id
             LEFT JOIN sys_countries co ON a.country_id = co.id
             LEFT JOIN auth_users u ON a.assigned_to = u.id
             LEFT JOIN members rm ON a.resulting_member_id = rm.id
             WHERE a.id = ? AND a.is_deleted = 0",
            [$id]
        );

        if (!$app) {
            $this->abort(404, 'Application not found');
            return;
        }

        $documents = $db->fetchAll(
            "SELECT ad.*, au.full_name_en as verified_by_name
             FROM application_documents ad
             LEFT JOIN auth_users au ON ad.verified_by = au.id
             WHERE ad.application_id = ? ORDER BY ad.uploaded_at DESC",
            [$id]
        );

        $sponsors = $db->fetchAll(
            "SELECT asp.*, m.membership_number, m.full_name_en as sponsor_name, m.primary_phone as sponsor_phone
             FROM application_sponsors asp
             JOIN members m ON asp.sponsor_member_id = m.id
             WHERE asp.application_id = ? ORDER BY asp.sponsor_order",
            [$id]
        );

        $reviews = $db->fetchAll(
            "SELECT ar.*, au.full_name_en as reviewer_name
             FROM application_reviews ar
             JOIN auth_users au ON ar.reviewer_id = au.id
             WHERE ar.application_id = ? ORDER BY ar.reviewed_at DESC",
            [$id]
        );

        $interviews = $db->fetchAll(
            "SELECT ai.*, au.full_name_en as recorded_by_name
             FROM application_interviews ai
             LEFT JOIN auth_users au ON ai.recorded_by = au.id
             WHERE ai.application_id = ? ORDER BY ai.scheduled_date DESC",
            [$id]
        );

        $fees = $db->fetchAll(
            "SELECT af.*, ft.fee_code
             FROM application_fees af
             JOIN fee_types ft ON af.fee_type_id = ft.id
             WHERE af.application_id = ? ORDER BY af.id",
            [$id]
        );

        $statusHistory = $db->fetchAll(
            "SELECT ash.*, au.full_name_en as changed_by_name
             FROM application_status_history ash
             LEFT JOIN auth_users au ON ash.changed_by = au.id
             WHERE ash.application_id = ? ORDER BY ash.changed_at DESC",
            [$id]
        );

        $boardVotes = $db->fetchAll(
            "SELECT abv.*, au.full_name_en as voter_name
             FROM application_board_votes abv
             JOIN auth_users au ON abv.board_member_user_id = au.id
             WHERE abv.application_id = ? ORDER BY abv.voted_at",
            [$id]
        );

        $users = $db->fetchAll("SELECT id, full_name_en FROM auth_users WHERE is_active = 1 AND is_deleted = 0 ORDER BY full_name_en");

        $requiredDocs = $db->fetchAll(
            "SELECT * FROM membership_type_documents_required
             WHERE membership_type_id = ? AND is_active = 1 ORDER BY sort_order",
            [$app['membership_type_id']]
        );

        $this->view('show', [
            'title' => lang('applications.view') . ' - ' . $app['application_number'],
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => $app['application_number']]
            ],
            'application'    => $app,
            'documents'      => $documents,
            'sponsors'       => $sponsors,
            'reviews'        => $reviews,
            'interviews'     => $interviews,
            'fees'           => $fees,
            'statusHistory'  => $statusHistory,
            'boardVotes'     => $boardVotes,
            'users'          => $users,
            'requiredDocs'   => $requiredDocs,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->abort(404, 'Application not found');
            return;
        }
        if (!in_array($app['status'], ['draft', 'submitted', 'documents_pending'])) {
            Session::flash('error', 'Cannot edit application in current status');
            Response::redirect('/applications/' . $id);
            return;
        }

        $membershipTypes = $db->fetchAll(
            "SELECT mt.*, mc.name_en as category_name_en
             FROM membership_types mt
             LEFT JOIN membership_categories mc ON mt.category_id = mc.id
             WHERE mt.is_active = 1 ORDER BY mt.sort_order"
        );
        $nationalities = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_nationalities WHERE is_active = 1 ORDER BY name_en");
        $countries = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_countries WHERE is_active = 1 ORDER BY sort_order, name_en");
        $periods = $db->fetchAll("SELECT id, period_name_en, period_name_ar FROM application_periods WHERE is_active = 1 ORDER BY start_date");

        $this->view('create', [
            'title' => lang('applications.edit') . ' - ' . $app['application_number'],
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => $app['application_number'], 'url' => '/applications/' . $id],
                ['label' => lang('common.edit')]
            ],
            'membershipTypes' => $membershipTypes,
            'nationalities' => $nationalities,
            'countries' => $countries,
            'periods' => $periods,
            'application' => $app,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');

        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $app = $db->fetchOne("SELECT * FROM applications WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$app) {
            $this->json(['success' => false, 'message' => 'Application not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'membership_type_id' => 'required|integer',
            'first_name_en'      => 'required|string|max:100',
            'last_name_en'       => 'required|string|max:100',
            'gender'             => 'required|in:male,female',
            'primary_phone'      => 'required|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = [
            'membership_type_id'   => $request->post('membership_type_id'),
            'application_period_id'=> $request->post('application_period_id') ?: null,
            'form_number'          => $request->post('form_number') ?: null,
            'title'                => $request->post('title') ?: null,
            'first_name_en'        => $request->post('first_name_en'),
            'second_name_en'       => $request->post('second_name_en') ?: null,
            'third_name_en'        => $request->post('third_name_en') ?: null,
            'last_name_en'         => $request->post('last_name_en'),
            'first_name_ar'        => $request->post('first_name_ar') ?: null,
            'second_name_ar'       => $request->post('second_name_ar') ?: null,
            'third_name_ar'        => $request->post('third_name_ar') ?: null,
            'last_name_ar'         => $request->post('last_name_ar') ?: null,
            'national_id'          => $request->post('national_id') ?: null,
            'passport_number'      => $request->post('passport_number') ?: null,
            'nationality_id'       => $request->post('nationality_id') ?: null,
            'date_of_birth'        => $request->post('date_of_birth') ?: null,
            'gender'               => $request->post('gender'),
            'marital_status'       => $request->post('marital_status') ?: null,
            'primary_phone'        => $request->post('primary_phone'),
            'secondary_phone'      => $request->post('secondary_phone') ?: null,
            'email'                => $request->post('email') ?: null,
            'address_line1'        => $request->post('address_line1') ?: null,
            'city_id'              => $request->post('city_id') ?: null,
            'country_id'           => $request->post('country_id') ?: null,
            'occupation'           => $request->post('occupation') ?: null,
            'employer'             => $request->post('employer') ?: null,
            'education_level'      => $request->post('education_level') ?: null,
            'priority'             => $request->post('priority') ?: 'normal',
            'source'               => $request->post('source') ?: $app['source'],
            'updated_at'          => date('Y-m-d H:i:s'),
        ];

        $photoFile = $request->file('photo');
        if ($photoFile && $photoFile['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($photoFile);
            $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'webp'])
                   ->setMaxSize(5 * 1024 * 1024)
                   ->setDestination('uploads/applications/photos');
            if ($upload->validate()) {
                $path = $upload->save();
                if ($path) {
                    $data['photo_path'] = $path;
                }
            }
        }

        $db->update('applications', $data, 'id = ?', [$id]);
        $this->audit('update', 'applications', $id, "Application updated: {$app['application_number']}", $app, $data);

        $this->json([
            'success'  => true,
            'message'  => lang('applications.updated_success'),
            'redirect' => '/applications/' . $id,
        ]);
    }

    public function boardVote(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $app = $db->fetchOne(
            "SELECT a.*, mt.name_en as type_name_en
             FROM applications a
             JOIN membership_types mt ON a.membership_type_id = mt.id
             WHERE a.id = ? AND a.is_deleted = 0",
            [$id]
        );

        if (!$app) {
            $this->abort(404, 'Application not found');
            return;
        }

        $currentTerm = $db->fetchOne("SELECT * FROM board_terms WHERE status = 'active' LIMIT 1");
        $boardMembers = [];
        if ($currentTerm) {
            $boardMembers = $db->fetchAll(
                "SELECT bm.*, bp.title_en as position_title, au.full_name_en as user_name
                 FROM board_members bm
                 JOIN board_positions bp ON bm.position_id = bp.id
                 LEFT JOIN auth_users au ON bm.user_id = au.id
                 WHERE bm.term_id = ? AND bm.status = 'active'
                 ORDER BY bp.sort_order",
                [$currentTerm['id']]
            );
        }

        $existingVotes = $db->fetchAll(
            "SELECT abv.*, au.full_name_en as voter_name
             FROM application_board_votes abv
             JOIN auth_users au ON abv.board_member_user_id = au.id
             WHERE abv.application_id = ?",
            [$id]
        );

        $this->view('board-vote', [
            'title' => lang('applications.board_vote') . ' - ' . $app['application_number'],
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => $app['application_number'], 'url' => '/applications/' . $id],
                ['label' => lang('applications.board_vote')]
            ],
            'application'   => $app,
            'boardMembers'  => $boardMembers,
            'existingVotes' => $existingVotes,
        ]);
    }

    public function printView(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $app = $db->fetchOne(
            "SELECT a.*, mt.name_en as type_name_en, mt.name_ar as type_name_ar,
                    n.name_en as nationality_name_en, n.name_ar as nationality_name_ar,
                    c.name_en as city_name_en, co.name_en as country_name_en
             FROM applications a
             LEFT JOIN membership_types mt ON a.membership_type_id = mt.id
             LEFT JOIN sys_nationalities n ON a.nationality_id = n.id
             LEFT JOIN sys_cities c ON a.city_id = c.id
             LEFT JOIN sys_countries co ON a.country_id = co.id
             WHERE a.id = ? AND a.is_deleted = 0",
            [$id]
        );

        if (!$app) {
            $this->abort(404);
            return;
        }

        $sponsors = $db->fetchAll(
            "SELECT asp.*, m.full_name_en as sponsor_name, m.membership_number
             FROM application_sponsors asp
             JOIN members m ON asp.sponsor_member_id = m.id
             WHERE asp.application_id = ? ORDER BY asp.sponsor_order",
            [$id]
        );

        $this->view('print', [
            'title' => $app['application_number'],
            'application' => $app,
            'sponsors' => $sponsors,
        ], 'print');
    }
}
```

## File: modules\applications\Controllers\ApplicationPeriodApiController.php

```php
<?php
class ApplicationPeriodApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = "1=1";
        $params = [];
        if ($search) {
            $where .= " AND (period_name_en LIKE ? OR period_name_ar LIKE ?)";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM application_periods");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM application_periods WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT * FROM application_periods WHERE {$where} ORDER BY start_date DESC LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $isActive = $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>';
            $actions = '<a href="/application-periods/' . $r['id'] . '/edit" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i></a>';

            $data[] = [
                'id'          => $r['id'],
                'name'        => e(current_lang() === 'ar' ? $r['period_name_ar'] : $r['period_name_en']),
                'start_date'  => format_date($r['start_date']),
                'end_date'    => format_date($r['end_date']),
                'max'         => $r['max_applications'] ?? '-',
                'current'     => $r['current_applications'],
                'status'      => $isActive,
                'actions'     => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $total,
            'recordsFiltered' => $filtered,
            'data'            => $data,
        ]);
    }
}
```

## File: modules\applications\Controllers\ApplicationPeriodController.php

```php
<?php
class ApplicationPeriodController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('periods/index', [
            'title' => lang('applications.periods'),
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => lang('applications.periods')]
            ],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $membershipTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('periods/create', [
            'title' => lang('applications.create_period'),
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => lang('applications.periods'), 'url' => '/application-periods'],
                ['label' => lang('common.create')]
            ],
            'membershipTypes' => $membershipTypes,
            'period' => null,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'period_name_en' => 'required|string|max:200',
            'period_name_ar' => 'required|string|max:200',
            'start_date'     => 'required|date',
            'end_date'       => 'required|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $typeIds = $request->post('membership_type_ids', []);

        $id = $this->db()->insert('application_periods', [
            'period_name_en'     => $request->post('period_name_en'),
            'period_name_ar'     => $request->post('period_name_ar'),
            'start_date'         => $request->post('start_date'),
            'end_date'           => $request->post('end_date'),
            'membership_type_ids'=> !empty($typeIds) ? json_encode($typeIds) : null,
            'max_applications'   => $request->post('max_applications') ?: null,
            'is_active'          => $request->post('is_active', 1),
            'created_by'         => Auth::id(),
        ]);

        $this->audit('create', 'application_periods', $id, 'Application period created');
        $this->json(['success' => true, 'message' => lang('applications.period_created'), 'redirect' => '/application-periods']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $period = $db->fetchOne("SELECT * FROM application_periods WHERE id = ?", [$id]);
        if (!$period) {
            $this->abort(404, 'Period not found');
            return;
        }

        $membershipTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('periods/create', [
            'title' => lang('applications.edit_period'),
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => lang('applications.periods'), 'url' => '/application-periods'],
                ['label' => lang('common.edit')]
            ],
            'membershipTypes' => $membershipTypes,
            'period' => $period,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $typeIds = $request->post('membership_type_ids', []);

        $this->db()->update('application_periods', [
            'period_name_en'      => $request->post('period_name_en'),
            'period_name_ar'      => $request->post('period_name_ar'),
            'start_date'          => $request->post('start_date'),
            'end_date'            => $request->post('end_date'),
            'membership_type_ids' => !empty($typeIds) ? json_encode($typeIds) : null,
            'max_applications'    => $request->post('max_applications') ?: null,
            'is_active'           => $request->post('is_active', 1),
        ], 'id = ?', [$id]);

        $this->audit('update', 'application_periods', $id, 'Application period updated');
        $this->json(['success' => true, 'message' => lang('applications.period_updated'), 'redirect' => '/application-periods']);
    }
}
```

## File: modules\applications\Controllers\WaitingListApiController.php

```php
<?php
class WaitingListApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);

        $rows = $db->fetchAll(
            "SELECT wl.*, mt.name_en as type_name_en, mt.name_ar as type_name_ar,
                    (SELECT COUNT(*) FROM waiting_list_entries wle WHERE wle.waiting_list_id = wl.id AND wle.status = 'waiting') as waiting_count,
                    (SELECT COUNT(*) FROM waiting_list_entries wle WHERE wle.waiting_list_id = wl.id AND wle.status = 'offered') as offered_count
             FROM waiting_lists wl
             JOIN membership_types mt ON wl.membership_type_id = mt.id
             ORDER BY mt.sort_order
             LIMIT " . (int)$length . " OFFSET " . (int)$start
        );

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM waiting_lists");

        $data = [];
        foreach ($rows as $r) {
            $status = $r['is_open'] ? '<span class="badge bg-success">Open</span>' : '<span class="badge bg-danger">Closed</span>';

            $data[] = [
                'id'      => $r['id'],
                'type'    => e(current_lang() === 'ar' ? $r['type_name_ar'] : $r['type_name_en']),
                'max'     => $r['max_entries'] ?? 'âˆž',
                'current' => $r['current_entries'],
                'waiting' => $r['waiting_count'],
                'offered' => $r['offered_count'],
                'status'  => $status,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $total,
            'recordsFiltered' => $total,
            'data'            => $data,
        ]);
    }

    public function entriesDatatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $listId = $request->get('waiting_list_id');

        $where = "1=1";
        $params = [];

        if ($listId) {
            $where .= " AND wle.waiting_list_id = ?";
            $params[] = $listId;
        }
        $statusFilter = $request->get('status');
        if ($statusFilter) {
            $where .= " AND wle.status = ?";
            $params[] = $statusFilter;
        }
        if ($search) {
            $where .= " AND (wle.applicant_name LIKE ? OR wle.applicant_phone LIKE ? OR wle.applicant_national_id LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s]);
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM waiting_list_entries wle WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT wle.*, mt.name_en as type_name_en, mt.name_ar as type_name_ar
             FROM waiting_list_entries wle
             JOIN waiting_lists wl ON wle.waiting_list_id = wl.id
             JOIN membership_types mt ON wl.membership_type_id = mt.id
             WHERE {$where}
             ORDER BY wle.queue_number ASC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '<div class="btn-group btn-group-sm">';
            if ($r['status'] === 'waiting') {
                $actions .= '<button class="btn btn-outline-success btn-sm" onclick="offerEntry(' . $r['id'] . ')" title="Offer"><i class="fas fa-paper-plane"></i></button>';
                $actions .= '<button class="btn btn-outline-primary btn-sm" onclick="promoteEntry(' . $r['id'] . ')" title="Promote"><i class="fas fa-arrow-up"></i></button>';
            }
            $actions .= '<button class="btn btn-outline-danger btn-sm" onclick="removeEntry(' . $r['id'] . ')" title="Remove"><i class="fas fa-times"></i></button>';
            $actions .= '</div>';

            $data[] = [
                'queue_number'  => $r['queue_number'],
                'name'          => e($r['applicant_name']),
                'phone'         => e($r['applicant_phone']),
                'national_id'   => e($r['applicant_national_id'] ?? '-'),
                'type'          => e(current_lang() === 'ar' ? $r['type_name_ar'] : $r['type_name_en']),
                'date'          => format_date($r['registration_date']),
                'priority'      => status_badge($r['priority']),
                'status'        => status_badge($r['status']),
                'actions'       => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $total,
            'recordsFiltered' => $total,
            'data'            => $data,
        ]);
    }

    public function offer(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $entry = $db->fetchOne("SELECT wle.*, wl.offer_validity_days FROM waiting_list_entries wle JOIN waiting_lists wl ON wle.waiting_list_id = wl.id WHERE wle.id = ?", [$id]);
        if (!$entry) {
            $this->json(['success' => false, 'message' => 'Entry not found'], 404);
            return;
        }

        $expiresAt = date('Y-m-d H:i:s', strtotime('+' . ($entry['offer_validity_days'] ?? 30) . ' days'));

        $db->update('waiting_list_entries', [
            'status'           => 'offered',
            'offered_at'       => date('Y-m-d H:i:s'),
            'offer_expires_at' => $expiresAt,
        ], 'id = ?', [$id]);

        $db->insert('waiting_list_entry_status_history', [
            'entry_id'    => $id,
            'from_status' => $entry['status'],
            'to_status'   => 'offered',
            'changed_by'  => Auth::id(),
            'notes'       => 'Offer sent, expires: ' . $expiresAt,
        ]);

        $this->json(['success' => true, 'message' => lang('applications.offer_sent')]);
    }

    public function promote(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $entry = $db->fetchOne("SELECT * FROM waiting_list_entries WHERE id = ?", [$id]);
        if (!$entry) {
            $this->json(['success' => false, 'message' => 'Entry not found'], 404);
            return;
        }

        $db->update('waiting_list_entries', [
            'status'      => 'promoted',
            'promoted_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $db->insert('waiting_list_entry_status_history', [
            'entry_id'    => $id,
            'from_status' => $entry['status'],
            'to_status'   => 'promoted',
            'changed_by'  => Auth::id(),
        ]);

        $this->json(['success' => true, 'message' => lang('applications.entry_promoted')]);
    }

    public function remove(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $entry = $db->fetchOne("SELECT * FROM waiting_list_entries WHERE id = ?", [$id]);
        if (!$entry) {
            $this->json(['success' => false, 'message' => 'Entry not found'], 404);
            return;
        }

        $db->update('waiting_list_entries', [
            'status' => 'withdrawn',
        ], 'id = ?', [$id]);

        $db->insert('waiting_list_entry_status_history', [
            'entry_id'    => $id,
            'from_status' => $entry['status'],
            'to_status'   => 'withdrawn',
            'changed_by'  => Auth::id(),
            'notes'       => $request->post('reason', ''),
        ]);

        $this->json(['success' => true, 'message' => lang('applications.entry_removed')]);
    }

    public function move(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $direction = $request->post('direction', 'up');
        $db = $this->db();
        $entry = $db->fetchOne("SELECT * FROM waiting_list_entries WHERE id = ?", [$id]);
        if (!$entry) {
            $this->json(['success' => false, 'message' => 'Entry not found'], 404);
            return;
        }

        $currentQueue = (int) $entry['queue_number'];
        $newQueue = $direction === 'up' ? $currentQueue - 1 : $currentQueue + 1;

        if ($newQueue < 1) {
            $this->json(['success' => false, 'message' => 'Already at top'], 422);
            return;
        }

        $swapEntry = $db->fetchOne(
            "SELECT id FROM waiting_list_entries WHERE waiting_list_id = ? AND queue_number = ? AND status IN ('waiting','offered')",
            [$entry['waiting_list_id'], $newQueue]
        );

        if ($swapEntry) {
            $db->update('waiting_list_entries', ['queue_number' => $currentQueue], 'id = ?', [$swapEntry['id']]);
        }
        $db->update('waiting_list_entries', ['queue_number' => $newQueue], 'id = ?', [$id]);

        $this->json(['success' => true, 'message' => 'Position updated']);
    }
}
```

## File: modules\applications\Controllers\WaitingListController.php

```php
<?php
class WaitingListController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $membershipTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('waiting-list/index', [
            'title' => lang('applications.waiting_list'),
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => lang('applications.waiting_list')]
            ],
            'membershipTypes' => $membershipTypes,
        ]);
    }

    public function createEntry(Request $request): void
    {
        $db = $this->db();
        $waitingLists = $db->fetchAll(
            "SELECT wl.*, mt.name_en as type_name_en, mt.name_ar as type_name_ar
             FROM waiting_lists wl
             JOIN membership_types mt ON wl.membership_type_id = mt.id
             WHERE wl.is_open = 1 ORDER BY mt.sort_order"
        );

        $this->view('waiting-list/create-entry', [
            'title' => lang('applications.add_to_waitlist'),
            'breadcrumbs' => [
                ['label' => lang('applications.title'), 'url' => '/applications'],
                ['label' => lang('applications.waiting_list'), 'url' => '/waiting-list'],
                ['label' => lang('applications.add_to_waitlist')]
            ],
            'waitingLists' => $waitingLists,
        ]);
    }

    public function storeEntry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'waiting_list_id' => 'required|integer|exists:waiting_lists,id',
            'applicant_name'  => 'required|string|max:300',
            'applicant_phone' => 'required|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = $this->db();
        $listId = (int) $request->post('waiting_list_id');

        $list = $db->fetchOne("SELECT * FROM waiting_lists WHERE id = ? AND is_open = 1", [$listId]);
        if (!$list) {
            $this->json(['success' => false, 'message' => 'Waiting list is not open'], 422);
            return;
        }

        if ($list['max_entries'] && $list['current_entries'] >= $list['max_entries']) {
            $this->json(['success' => false, 'message' => 'Waiting list is full'], 422);
            return;
        }

        $maxQueue = (int) $db->fetchColumn(
            "SELECT COALESCE(MAX(queue_number), 0) FROM waiting_list_entries WHERE waiting_list_id = ?",
            [$listId]
        );

        $entryId = $db->insert('waiting_list_entries', [
            'waiting_list_id'        => $listId,
            'queue_number'           => $maxQueue + 1,
            'applicant_name'         => $request->post('applicant_name'),
            'applicant_phone'        => $request->post('applicant_phone'),
            'applicant_email'        => $request->post('applicant_email') ?: null,
            'applicant_national_id'  => $request->post('applicant_national_id') ?: null,
            'registration_date'      => date('Y-m-d'),
            'priority'               => $request->post('priority', 'normal'),
            'status'                 => 'waiting',
            'notes'                  => $request->post('notes') ?: null,
        ]);

        $this->audit('create', 'waiting_list_entries', $entryId, "Added to waiting list #{$listId}");
        $this->json(['success' => true, 'message' => lang('applications.added_to_waitlist'), 'redirect' => '/waiting-list']);
    }
}
```

## File: modules\applications\css\applications.css

```css
/* Applications Module Styles */
.timeline {
    position: relative;
    padding-left: 0;
}

.timeline::before {
    content: '';
    position: absolute;
    left: 15px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: #e2e8f0;
}

.tab-content .card {
    margin-bottom: 0;
}

.border-start.border-3 {
    border-width: 3px !important;
}

#stats-cards .card-body h3 {
    font-size: 1.8rem;
    font-weight: 700;
}

#stats-cards .card-body h6 {
    font-size: 0.75rem;
}

.application-photo-preview {
    max-width: 150px;
    max-height: 150px;
    border-radius: 8px;
    object-fit: cover;
}

.nav-tabs .nav-link {
    font-size: 0.875rem;
}

.nav-tabs .badge {
    font-size: 0.65rem;
    margin-left: 4px;
}
```

## File: modules\applications\js\applications.js

```js
/**
 * Applications Module JavaScript
 */
(function() {
    'use strict';

    // Auto-initialize select2 on module pages
    document.addEventListener('DOMContentLoaded', function() {
        // Nothing extra needed - core app.js handles select2 and flatpickr init
    });
})();
```

## File: modules\applications\lang\ar.php

```php
<?php
return [
    'applications.title'                => 'Ø§Ù„Ø·Ù„Ø¨Ø§Øª',
    'applications.create'               => 'Ø·Ù„Ø¨ Ø¬Ø¯ÙŠØ¯',
    'applications.edit'                 => 'ØªØ¹Ø¯ÙŠÙ„ Ø§Ù„Ø·Ù„Ø¨',
    'applications.view'                 => 'Ø¹Ø±Ø¶ Ø§Ù„Ø·Ù„Ø¨',
    'applications.submit'               => 'ØªÙ‚Ø¯ÙŠÙ… Ø§Ù„Ø·Ù„Ø¨',
    'applications.change_status'        => 'ØªØºÙŠÙŠØ± Ø§Ù„Ø­Ø§Ù„Ø©',
    'applications.review'               => 'Ù…Ø±Ø§Ø¬Ø¹Ø©',
    'applications.board_vote'           => 'ØªØµÙˆÙŠØª Ø§Ù„Ù…Ø¬Ù„Ø³',
    'applications.create_member'        => 'Ø¥Ù†Ø´Ø§Ø¡ Ø¹Ø¶Ùˆ',
    'applications.schedule_interview'   => 'Ø¬Ø¯ÙˆÙ„Ø© Ù…Ù‚Ø§Ø¨Ù„Ø©',
    'applications.pending_applications' => 'Ø§Ù„Ø·Ù„Ø¨Ø§Øª Ø§Ù„Ù…Ø¹Ù„Ù‚Ø©',
    'applications.waiting_list'         => 'Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±',
    'applications.add_to_waitlist'      => 'Ø¥Ø¶Ø§ÙØ© Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±',
    'applications.periods'              => 'ÙØªØ±Ø§Øª Ø§Ù„ØªÙ‚Ø¯ÙŠÙ…',
    'applications.create_period'        => 'Ø¥Ù†Ø´Ø§Ø¡ ÙØªØ±Ø©',
    'applications.edit_period'          => 'ØªØ¹Ø¯ÙŠÙ„ ÙØªØ±Ø©',
    'applications.personal_info'        => 'Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø´Ø®ØµÙŠØ©',
    'applications.contact'              => 'Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø§ØªØµØ§Ù„',
    'applications.address'              => 'Ø§Ù„Ø¹Ù†ÙˆØ§Ù†',
    'applications.employment'           => 'Ø§Ù„Ø¹Ù…Ù„',
    'applications.membership'           => 'Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'applications.app_number'           => 'Ø±Ù‚Ù… Ø§Ù„Ø·Ù„Ø¨',
    'applications.applicant_name'       => 'Ø§Ø³Ù… Ø§Ù„Ù…ØªÙ‚Ø¯Ù…',
    'applications.phone'                => 'Ø§Ù„Ù‡Ø§ØªÙ',
    'applications.type'                 => 'Ø§Ù„Ù†ÙˆØ¹',
    'applications.priority'             => 'Ø§Ù„Ø£ÙˆÙ„ÙˆÙŠØ©',
    'applications.submission_date'      => 'ØªØ§Ø±ÙŠØ® Ø§Ù„ØªÙ‚Ø¯ÙŠÙ…',
    'applications.assigned_to'          => 'Ù…Ø³Ù†Ø¯ Ø¥Ù„Ù‰',
    'applications.membership_type'      => 'Ù†ÙˆØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'applications.source'               => 'Ø§Ù„Ù…ØµØ¯Ø±',
    'applications.form_number'          => 'Ø±Ù‚Ù… Ø§Ù„Ø§Ø³ØªÙ…Ø§Ø±Ø©',
    'applications.period'               => 'ÙØªØ±Ø© Ø§Ù„ØªÙ‚Ø¯ÙŠÙ…',
    'applications.title_field'          => 'Ø§Ù„Ù„Ù‚Ø¨',
    'applications.first_name_en'        => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„ (EN)',
    'applications.second_name_en'       => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù†ÙŠ (EN)',
    'applications.third_name_en'        => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù„Ø« (EN)',
    'applications.last_name_en'         => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© (EN)',
    'applications.first_name_ar'        => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„ (Ø¹Ø±Ø¨ÙŠ)',
    'applications.second_name_ar'       => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù†ÙŠ (Ø¹Ø±Ø¨ÙŠ)',
    'applications.last_name_ar'         => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© (Ø¹Ø±Ø¨ÙŠ)',
    'applications.national_id'          => 'Ø§Ù„Ø±Ù‚Ù… Ø§Ù„Ù‚ÙˆÙ…ÙŠ',
    'applications.passport'             => 'Ø±Ù‚Ù… Ø¬ÙˆØ§Ø² Ø§Ù„Ø³ÙØ±',
    'applications.nationality'          => 'Ø§Ù„Ø¬Ù†Ø³ÙŠØ©',
    'applications.dob'                  => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ù…ÙŠÙ„Ø§Ø¯',
    'applications.gender'               => 'Ø§Ù„Ø¬Ù†Ø³',
    'applications.marital_status'       => 'Ø§Ù„Ø­Ø§Ù„Ø© Ø§Ù„Ø§Ø¬ØªÙ…Ø§Ø¹ÙŠØ©',
    'applications.photo'                => 'Ø§Ù„ØµÙˆØ±Ø©',
    'applications.primary_phone'        => 'Ø§Ù„Ù‡Ø§ØªÙ Ø§Ù„Ø£Ø³Ø§Ø³ÙŠ',
    'applications.secondary_phone'      => 'Ø§Ù„Ù‡Ø§ØªÙ Ø§Ù„Ø«Ø§Ù†ÙˆÙŠ',
    'applications.email'                => 'Ø§Ù„Ø¨Ø±ÙŠØ¯ Ø§Ù„Ø¥Ù„ÙƒØªØ±ÙˆÙ†ÙŠ',
    'applications.country'              => 'Ø§Ù„Ø¯ÙˆÙ„Ø©',
    'applications.city'                 => 'Ø§Ù„Ù…Ø¯ÙŠÙ†Ø©',
    'applications.occupation'           => 'Ø§Ù„Ù…Ù‡Ù†Ø©',
    'applications.employer'             => 'Ø¬Ù‡Ø© Ø§Ù„Ø¹Ù…Ù„',
    'applications.education_level'      => 'Ø§Ù„Ù…Ø³ØªÙˆÙ‰ Ø§Ù„ØªØ¹Ù„ÙŠÙ…ÙŠ',
    'applications.sponsor1'             => 'Ø§Ù„Ø±Ø§Ø¹ÙŠ Ø§Ù„Ø£ÙˆÙ„',
    'applications.sponsor2'             => 'Ø§Ù„Ø±Ø§Ø¹ÙŠ Ø§Ù„Ø«Ø§Ù†ÙŠ',
    'applications.date_from'            => 'Ù…Ù† ØªØ§Ø±ÙŠØ®',
    'applications.date_to'              => 'Ø¥Ù„Ù‰ ØªØ§Ø±ÙŠØ®',
    'applications.created'              => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ù†Ø´Ø§Ø¡',
    'applications.submitted'            => 'Ù…Ù‚Ø¯Ù…Ø©',
    'applications.under_review'         => 'Ù‚ÙŠØ¯ Ø§Ù„Ù…Ø±Ø§Ø¬Ø¹Ø©',
    'applications.board_pending'        => 'Ø¨Ø§Ù†ØªØ¸Ø§Ø± Ø§Ù„Ù…Ø¬Ù„Ø³',
    'applications.approved'             => 'Ù…ÙˆØ§ÙÙ‚ Ø¹Ù„ÙŠÙ‡Ø§',
    'applications.rejected'             => 'Ù…Ø±ÙÙˆØ¶Ø©',
    'applications.documents_pending'    => 'Ù…Ø³ØªÙ†Ø¯Ø§Øª Ù†Ø§Ù‚ØµØ©',
    'applications.created_success'      => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø·Ù„Ø¨ Ø¨Ù†Ø¬Ø§Ø­',
    'applications.updated_success'      => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ø·Ù„Ø¨ Ø¨Ù†Ø¬Ø§Ø­',
    'applications.submitted_success'    => 'ØªÙ… ØªÙ‚Ø¯ÙŠÙ… Ø§Ù„Ø·Ù„Ø¨ Ø¨Ù†Ø¬Ø§Ø­',
    'applications.deleted_success'      => 'ØªÙ… Ø­Ø°Ù Ø§Ù„Ø·Ù„Ø¨',
    'applications.status_changed'       => 'ØªÙ… ØªØºÙŠÙŠØ± Ø§Ù„Ø­Ø§Ù„Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'applications.review_recorded'      => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ù…Ø±Ø§Ø¬Ø¹Ø©',
    'applications.interview_saved'      => 'ØªÙ… Ø­ÙØ¸ Ø§Ù„Ù…Ù‚Ø§Ø¨Ù„Ø©',
    'applications.votes_recorded'       => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø£ØµÙˆØ§Øª ÙˆØ­Ø³Ø§Ø¨ Ø§Ù„Ù†ØªÙŠØ¬Ø©',
    'applications.document_uploaded'    => 'ØªÙ… Ø±ÙØ¹ Ø§Ù„Ù…Ø³ØªÙ†Ø¯',
    'applications.document_deleted'     => 'ØªÙ… Ø­Ø°Ù Ø§Ù„Ù…Ø³ØªÙ†Ø¯',
    'applications.document_verified'    => 'ØªÙ… Ø§Ù„ØªØ­Ù‚Ù‚ Ù…Ù† Ø§Ù„Ù…Ø³ØªÙ†Ø¯',
    'applications.period_created'       => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„ÙØªØ±Ø©',
    'applications.period_updated'       => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„ÙØªØ±Ø©',
    'applications.added_to_waitlist'    => 'ØªÙ…Øª Ø§Ù„Ø¥Ø¶Ø§ÙØ© Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±',
    'applications.offer_sent'           => 'ØªÙ… Ø¥Ø±Ø³Ø§Ù„ Ø§Ù„Ø¹Ø±Ø¶',
    'applications.entry_promoted'       => 'ØªÙ…Øª Ø§Ù„ØªØ±Ù‚ÙŠØ©',
    'applications.entry_removed'        => 'ØªÙ…Øª Ø§Ù„Ø¥Ø²Ø§Ù„Ø©',
    'applications.confirm_submit'       => 'ØªÙ‚Ø¯ÙŠÙ… Ø§Ù„Ø·Ù„Ø¨ØŸ',
    'applications.confirm_submit_text'  => 'Ø³ÙŠØªÙ… ØªÙ‚Ø¯ÙŠÙ… Ø§Ù„Ø·Ù„Ø¨ Ù„Ù„Ù…Ø±Ø§Ø¬Ø¹Ø© ÙˆÙ„Ù† ÙŠÙ…ÙƒÙ†Ùƒ ØªØ¹Ø¯ÙŠÙ„Ù‡ Ø¨Ø¹Ø¯ Ø°Ù„Ùƒ.',
    'applications.confirm_create_member'=> 'Ø¥Ù†Ø´Ø§Ø¡ Ø¹Ø¶ÙˆØŸ',
    'applications.confirm_create_member_text' => 'Ø³ÙŠØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø¹Ø¶Ùˆ Ø¬Ø¯ÙŠØ¯ Ù…Ù† Ù‡Ø°Ø§ Ø§Ù„Ø·Ù„Ø¨ Ø§Ù„Ù…ÙˆØ§ÙÙ‚ Ø¹Ù„ÙŠÙ‡.',
];
```

## File: modules\applications\lang\en.php

```php
<?php
return [
    'applications.title'                => 'Applications',
    'applications.create'               => 'New Application',
    'applications.edit'                 => 'Edit Application',
    'applications.view'                 => 'View Application',
    'applications.submit'               => 'Submit Application',
    'applications.change_status'        => 'Change Status',
    'applications.review'               => 'Review',
    'applications.board_vote'           => 'Board Vote',
    'applications.create_member'        => 'Create Member',
    'applications.schedule_interview'   => 'Schedule Interview',
    'applications.pending_applications' => 'Pending Applications',
    'applications.waiting_list'         => 'Waiting List',
    'applications.add_to_waitlist'      => 'Add to Waiting List',
    'applications.periods'              => 'Application Periods',
    'applications.create_period'        => 'Create Period',
    'applications.edit_period'          => 'Edit Period',
    'applications.personal_info'        => 'Personal Info',
    'applications.contact'              => 'Contact',
    'applications.address'              => 'Address',
    'applications.employment'           => 'Employment',
    'applications.membership'           => 'Membership',
    'applications.app_number'           => 'App #',
    'applications.applicant_name'       => 'Applicant Name',
    'applications.phone'                => 'Phone',
    'applications.type'                 => 'Type',
    'applications.priority'             => 'Priority',
    'applications.submission_date'      => 'Submission Date',
    'applications.assigned_to'          => 'Assigned To',
    'applications.membership_type'      => 'Membership Type',
    'applications.source'               => 'Source',
    'applications.form_number'          => 'Form Number',
    'applications.period'               => 'Application Period',
    'applications.title_field'          => 'Title',
    'applications.first_name_en'        => 'First Name (EN)',
    'applications.second_name_en'       => 'Second Name (EN)',
    'applications.third_name_en'        => 'Third Name (EN)',
    'applications.last_name_en'         => 'Last Name (EN)',
    'applications.first_name_ar'        => 'First Name (AR)',
    'applications.second_name_ar'       => 'Second Name (AR)',
    'applications.last_name_ar'         => 'Last Name (AR)',
    'applications.national_id'          => 'National ID',
    'applications.passport'             => 'Passport #',
    'applications.nationality'          => 'Nationality',
    'applications.dob'                  => 'Date of Birth',
    'applications.gender'               => 'Gender',
    'applications.marital_status'       => 'Marital Status',
    'applications.photo'                => 'Photo',
    'applications.primary_phone'        => 'Primary Phone',
    'applications.secondary_phone'      => 'Secondary Phone',
    'applications.email'                => 'Email',
    'applications.country'              => 'Country',
    'applications.city'                 => 'City',
    'applications.occupation'           => 'Occupation',
    'applications.employer'             => 'Employer',
    'applications.education_level'      => 'Education Level',
    'applications.sponsor1'             => 'Sponsor 1',
    'applications.sponsor2'             => 'Sponsor 2',
    'applications.date_from'            => 'Date From',
    'applications.date_to'              => 'Date To',
    'applications.created'              => 'Created',
    'applications.submitted'            => 'Submitted',
    'applications.under_review'         => 'Under Review',
    'applications.board_pending'        => 'Board Pending',
    'applications.approved'             => 'Approved',
    'applications.rejected'             => 'Rejected',
    'applications.documents_pending'    => 'Docs Pending',
    'applications.created_success'      => 'Application created successfully',
    'applications.updated_success'      => 'Application updated successfully',
    'applications.submitted_success'    => 'Application submitted successfully',
    'applications.deleted_success'      => 'Application deleted',
    'applications.status_changed'       => 'Status changed successfully',
    'applications.review_recorded'      => 'Review recorded',
    'applications.interview_saved'      => 'Interview saved',
    'applications.votes_recorded'       => 'Votes recorded and result calculated',
    'applications.document_uploaded'    => 'Document uploaded',
    'applications.document_deleted'     => 'Document deleted',
    'applications.document_verified'    => 'Document verified',
    'applications.period_created'       => 'Period created',
    'applications.period_updated'       => 'Period updated',
    'applications.added_to_waitlist'    => 'Added to waiting list',
    'applications.offer_sent'           => 'Offer sent',
    'applications.entry_promoted'       => 'Entry promoted',
    'applications.entry_removed'        => 'Entry removed',
    'applications.confirm_submit'       => 'Submit Application?',
    'applications.confirm_submit_text'  => 'This will submit the application for review. You will not be able to edit it afterward.',
    'applications.confirm_create_member'=> 'Create Member?',
    'applications.confirm_create_member_text' => 'This will create a new member from this approved application.',
];
```

## File: modules\applications\module.json

```json
{
    "name": "applications",
    "label_en": "Applications & Waiting Lists",
    "label_ar": "Ø§Ù„Ø·Ù„Ø¨Ø§Øª ÙˆÙ‚ÙˆØ§Ø¦Ù… Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±",
    "version": "1.0.0",
    "description": "Membership applications lifecycle, waiting list management",
    "sort_order": 20,
    "icon": "fas fa-file-alt",
    "routes_file": "routes.php",
    "css_files": ["css/applications.css"],
    "js_files": ["js/applications.js"],
    "menu": [
        {
            "label_en": "Applications",
            "label_ar": "Ø§Ù„Ø·Ù„Ø¨Ø§Øª",
            "icon": "fas fa-file-alt",
            "permission": "applications.view",
            "sort_order": 20,
            "children": [
                {
                    "label_en": "All Applications",
                    "label_ar": "ÙƒÙ„ Ø§Ù„Ø·Ù„Ø¨Ø§Øª",
                    "route": "/applications",
                    "permission": "applications.view",
                    "sort_order": 1
                },
                {
                    "label_en": "New Application",
                    "label_ar": "Ø·Ù„Ø¨ Ø¬Ø¯ÙŠØ¯",
                    "route": "/applications/create",
                    "permission": "applications.create",
                    "sort_order": 2
                },
                {
                    "label_en": "Application Periods",
                    "label_ar": "ÙØªØ±Ø§Øª Ø§Ù„ØªÙ‚Ø¯ÙŠÙ…",
                    "route": "/application-periods",
                    "permission": "applications.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Waiting List",
                    "label_ar": "Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±",
                    "route": "/waiting-list",
                    "permission": "waitlist.view",
                    "sort_order": 4
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "pending_applications",
            "label_en": "Pending Applications",
            "label_ar": "Ø§Ù„Ø·Ù„Ø¨Ø§Øª Ø§Ù„Ù…Ø¹Ù„Ù‚Ø©",
            "permission": "applications.view",
            "partial": "views/widgets/pending_applications.php",
            "size": "col-md-4",
            "sort_order": 20
        },
        {
            "widget_id": "waiting_list_overview",
            "label_en": "Waiting List Overview",
            "label_ar": "Ù†Ø¸Ø±Ø© Ø¹Ø§Ù…Ø© Ø¹Ù„Ù‰ Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±",
            "permission": "waitlist.view",
            "partial": "views/widgets/waiting_list_overview.php",
            "size": "col-md-4",
            "sort_order": 21
        }
    ]
}
```

## File: modules\applications\routes.php

```php
<?php
return [
    // Application CRUD
    ['GET',  '/applications',                          'ApplicationController@index',       'applications.view'],
    ['GET',  '/applications/create',                   'ApplicationController@create',      'applications.create'],
    ['POST', '/applications',                          'ApplicationController@store',       'applications.create'],
    ['GET',  '/applications/{id}',                     'ApplicationController@show',        'applications.view'],
    ['GET',  '/applications/{id}/edit',                'ApplicationController@edit',        'applications.create'],
    ['POST', '/applications/{id}',                     'ApplicationController@update',      'applications.create'],
    ['GET',  '/applications/{id}/print',               'ApplicationController@printView',   'applications.view'],
    ['GET',  '/applications/{id}/board-vote',          'ApplicationController@boardVote',   'applications.approve'],

    // Application API
    ['GET',  '/api/applications/datatable',            'ApplicationApiController@datatable',         'applications.view'],
    ['GET',  '/api/applications/stats',                'ApplicationApiController@stats',             'applications.view'],
    ['POST', '/api/applications/{id}/submit',          'ApplicationApiController@submit',            'applications.create'],
    ['POST', '/api/applications/{id}/status',          'ApplicationApiController@changeStatus',      'applications.review'],
    ['POST', '/api/applications/{id}/review',          'ApplicationApiController@review',            'applications.review'],
    ['POST', '/api/applications/{id}/interview',       'ApplicationApiController@interview',         'applications.review'],
    ['POST', '/api/applications/{id}/board-vote',      'ApplicationApiController@recordBoardVote',   'applications.approve'],
    ['POST', '/api/applications/{id}/create-member',   'ApplicationApiController@createMember',      'applications.approve'],
    ['POST', '/api/applications/{id}/delete',          'ApplicationApiController@delete',            'applications.create'],
    ['GET',  '/api/applications/{id}/documents',       'ApplicationApiController@getDocuments',      'applications.view'],
    ['POST', '/api/applications/{id}/documents',       'ApplicationApiController@uploadDocument',    'applications.create'],
    ['POST', '/api/applications/{id}/documents/{docId}/delete', 'ApplicationApiController@deleteDocument', 'applications.create'],
    ['POST', '/api/applications/{id}/documents/{docId}/verify', 'ApplicationApiController@verifyDocument', 'applications.review'],
    ['GET',  '/api/applications/export',               'ApplicationApiController@export',            'applications.view'],

    // Application Periods
    ['GET',  '/application-periods',                   'ApplicationPeriodController@index',   'applications.view'],
    ['GET',  '/application-periods/create',            'ApplicationPeriodController@create',  'applications.review'],
    ['POST', '/application-periods',                   'ApplicationPeriodController@store',   'applications.review'],
    ['GET',  '/application-periods/{id}/edit',         'ApplicationPeriodController@edit',    'applications.review'],
    ['POST', '/application-periods/{id}',              'ApplicationPeriodController@update',  'applications.review'],
    ['GET',  '/api/application-periods/datatable',     'ApplicationPeriodApiController@datatable', 'applications.view'],

    // Waiting List
    ['GET',  '/waiting-list',                          'WaitingListController@index',        'waitlist.view'],
    ['GET',  '/waiting-list/create-entry',             'WaitingListController@createEntry',  'waitlist.manage'],
    ['POST', '/waiting-list/entries',                  'WaitingListController@storeEntry',   'waitlist.manage'],
    ['GET',  '/api/waiting-list/datatable',            'WaitingListApiController@datatable',       'waitlist.view'],
    ['GET',  '/api/waiting-list/entries/datatable',    'WaitingListApiController@entriesDatatable','waitlist.view'],
    ['POST', '/api/waiting-list/entries/{id}/offer',   'WaitingListApiController@offer',     'waitlist.manage'],
    ['POST', '/api/waiting-list/entries/{id}/promote', 'WaitingListApiController@promote',   'waitlist.manage'],
    ['POST', '/api/waiting-list/entries/{id}/remove',  'WaitingListApiController@remove',    'waitlist.manage'],
    ['POST', '/api/waiting-list/entries/{id}/move',    'WaitingListApiController@move',      'waitlist.manage'],
];
```

## File: modules\applications\views\board-vote.php

```php
<?php $app = $application; ?>

<div class="container-fluid fade-in">
    <div class="card">
        <div class="card-header">
            <h5 class="mb-0"><i class="fas fa-vote-yea me-2"></i><?= lang('applications.board_vote') ?> - <?= e($app['application_number']) ?></h5>
            <small class="text-muted"><?= e($app['full_name_en']) ?> | <?= e($app['type_name_en']) ?></small>
        </div>
        <div class="card-body">
            <?php if (empty($boardMembers)): ?>
            <div class="alert alert-warning">No active board term or board members found.</div>
            <?php else: ?>
            <form id="vote-form">
                <?= csrf_field() ?>
                <table class="table">
                    <thead><tr><th>Board Member</th><th>Position</th><th>Vote</th><th>Comments</th></tr></thead>
                    <tbody>
                    <?php foreach ($boardMembers as $bm): ?>
                    <?php
                        $existingVote = null;
                        foreach ($existingVotes as $ev) {
                            if ($ev['voter_name'] && $bm['user_id'] && $ev['board_member_user_id'] == $bm['user_id']) {
                                $existingVote = $ev;
                                break;
                            }
                        }
                    ?>
                    <tr>
                        <td><?= e($bm['full_name_en'] ?: $bm['user_name'] ?? '-') ?></td>
                        <td><?= e($bm['position_title']) ?></td>
                        <td>
                            <?php if ($bm['user_id']): ?>
                            <select name="votes[<?= $bm['user_id'] ?>][vote]" class="form-select form-select-sm">
                                <option value="approve" <?= selected('approve', $existingVote['vote'] ?? '') ?>>Approve</option>
                                <option value="reject" <?= selected('reject', $existingVote['vote'] ?? '') ?>>Reject</option>
                                <option value="abstain" <?= selected('abstain', $existingVote['vote'] ?? '') ?>>Abstain</option>
                            </select>
                            <?php else: ?>
                            <span class="text-muted">No user linked</span>
                            <?php endif; ?>
                        </td>
                        <td>
                            <?php if ($bm['user_id']): ?>
                            <input type="text" name="votes[<?= $bm['user_id'] ?>][comments]" class="form-control form-control-sm" value="<?= e($existingVote['comments'] ?? '') ?>">
                            <?php endif; ?>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>

                <?php if (!empty($existingVotes)): ?>
                <div class="alert alert-info">
                    <strong>Current tally:</strong>
                    <?php
                    $for = $against = $abstain = 0;
                    foreach ($existingVotes as $v) {
                        if ($v['vote'] === 'approve') $for++;
                        elseif ($v['vote'] === 'reject') $against++;
                        else $abstain++;
                    }
                    ?>
                    <span class="badge bg-success">For: <?= $for ?></span>
                    <span class="badge bg-danger">Against: <?= $against ?></span>
                    <span class="badge bg-secondary">Abstain: <?= $abstain ?></span>
                </div>
                <?php endif; ?>

                <div class="d-flex gap-2 mt-3">
                    <button type="button" class="btn btn-primary" onclick="saveVotes()"><i class="fas fa-save me-1"></i>Save & Calculate Result</button>
                    <a href="/applications/<?= $app['id'] ?>" class="btn btn-secondary"><?= lang('common.back') ?></a>
                </div>
            </form>
            <?php endif; ?>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
function saveVotes() {
    const form = document.getElementById('vote-form');
    const fd = new FormData(form);
    const data = {};
    for (const [key, value] of fd.entries()) {
        const keys = key.match(/votes\[(\d+)]\[(\w+)]/);
        if (keys) {
            if (!data['votes']) data['votes'] = {};
            if (!data['votes'][keys[1]]) data['votes'][keys[1]] = {};
            data['votes'][keys[1]][keys[2]] = value;
        } else {
            data[key] = value;
        }
    }

    $.ajax({
        url: '/api/applications/<?= $app['id'] ?>/board-vote',
        type: 'POST',
        data: JSON.stringify(data),
        contentType: 'application/json',
        headers: { 'X-CSRF-TOKEN': '<?= csrf_token() ?>' },
        success: function(res) {
            if (res.success) {
                FormHelper.showToast('success', res.message);
                setTimeout(() => window.location.href = '/applications/<?= $app['id'] ?>', 800);
            } else {
                FormHelper.showToast('error', res.message);
            }
        }
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\applications\views\create.php

```php
<?php
$isEdit = !empty($application);
$app = $application ?? [];
$formAction = $isEdit ? '/applications/' . $app['id'] : '/applications';
$formTitle = $isEdit ? lang('applications.edit') : lang('applications.create');
?>

<?php $__view->push('styles') ?>
<link href="/modules/applications/css/applications.css" rel="stylesheet">
<?php $__view->endPush() ?>

<div class="container-fluid fade-in">
    <div class="row justify-content-center">
        <div class="col-lg-10">
            <div class="card">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h5 class="mb-0"><i class="fas fa-file-alt me-2"></i><?= $formTitle ?></h5>
                    <?php if ($isEdit): ?>
                    <span class="badge bg-secondary"><?= e($app['application_number']) ?></span>
                    <?php endif; ?>
                </div>
                <div class="card-body">
                    <form id="application-form" method="POST" action="<?= $formAction ?>" data-ajax="true" enctype="multipart/form-data">
                        <?= csrf_field() ?>

                        <!-- Tabs -->
                        <ul class="nav nav-tabs mb-4" role="tablist">
                            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-personal"><i class="fas fa-user me-1"></i><?= lang('applications.personal_info') ?></a></li>
                            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-contact"><i class="fas fa-phone me-1"></i><?= lang('applications.contact') ?></a></li>
                            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-address"><i class="fas fa-map-marker-alt me-1"></i><?= lang('applications.address') ?></a></li>
                            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-employment"><i class="fas fa-briefcase me-1"></i><?= lang('applications.employment') ?></a></li>
                            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-membership"><i class="fas fa-id-card me-1"></i><?= lang('applications.membership') ?></a></li>
                        </ul>

                        <div class="tab-content">
                            <!-- Personal Info Tab -->
                            <div class="tab-pane fade show active" id="tab-personal">
                                <div class="row g-3">
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.title_field') ?></label>
                                        <select name="title" class="form-select">
                                            <option value="">-</option>
                                            <?php foreach (['mr','mrs','ms','dr','eng','prof','sheikh'] as $t): ?>
                                            <option value="<?= $t ?>" <?= selected($t, $app['title'] ?? '') ?>><?= ucfirst($t) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.first_name_en') ?> <span class="text-danger">*</span></label>
                                        <input type="text" name="first_name_en" class="form-control" value="<?= e($app['first_name_en'] ?? '') ?>" required>
                                        <div class="invalid-feedback" data-field="first_name_en"></div>
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.second_name_en') ?></label>
                                        <input type="text" name="second_name_en" class="form-control" value="<?= e($app['second_name_en'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.third_name_en') ?></label>
                                        <input type="text" name="third_name_en" class="form-control" value="<?= e($app['third_name_en'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.last_name_en') ?> <span class="text-danger">*</span></label>
                                        <input type="text" name="last_name_en" class="form-control" value="<?= e($app['last_name_en'] ?? '') ?>" required>
                                        <div class="invalid-feedback" data-field="last_name_en"></div>
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.first_name_ar') ?></label>
                                        <input type="text" name="first_name_ar" class="form-control" dir="rtl" value="<?= e($app['first_name_ar'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.second_name_ar') ?></label>
                                        <input type="text" name="second_name_ar" class="form-control" dir="rtl" value="<?= e($app['second_name_ar'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.last_name_ar') ?></label>
                                        <input type="text" name="last_name_ar" class="form-control" dir="rtl" value="<?= e($app['last_name_ar'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.national_id') ?></label>
                                        <input type="text" name="national_id" class="form-control" maxlength="20" value="<?= e($app['national_id'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.passport') ?></label>
                                        <input type="text" name="passport_number" class="form-control" value="<?= e($app['passport_number'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.nationality') ?></label>
                                        <select name="nationality_id" class="form-select select2">
                                            <option value="">-</option>
                                            <?php foreach ($nationalities as $n): ?>
                                            <option value="<?= $n['id'] ?>" <?= selected($n['id'], $app['nationality_id'] ?? '') ?>><?= e(current_lang() === 'ar' ? $n['name_ar'] : $n['name_en']) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.dob') ?></label>
                                        <input type="text" name="date_of_birth" class="form-control datepicker" value="<?= e($app['date_of_birth'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.gender') ?> <span class="text-danger">*</span></label>
                                        <select name="gender" class="form-select" required>
                                            <option value="">-</option>
                                            <option value="male" <?= selected('male', $app['gender'] ?? '') ?>>Male</option>
                                            <option value="female" <?= selected('female', $app['gender'] ?? '') ?>>Female</option>
                                        </select>
                                        <div class="invalid-feedback" data-field="gender"></div>
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.marital_status') ?></label>
                                        <select name="marital_status" class="form-select">
                                            <option value="">-</option>
                                            <option value="single" <?= selected('single', $app['marital_status'] ?? '') ?>>Single</option>
                                            <option value="married" <?= selected('married', $app['marital_status'] ?? '') ?>>Married</option>
                                            <option value="divorced" <?= selected('divorced', $app['marital_status'] ?? '') ?>>Divorced</option>
                                            <option value="widowed" <?= selected('widowed', $app['marital_status'] ?? '') ?>>Widowed</option>
                                        </select>
                                    </div>
                                    <div class="col-md-3">
                                        <label class="form-label"><?= lang('applications.photo') ?></label>
                                        <input type="file" name="photo" class="form-control" accept="image/*">
                                    </div>
                                </div>
                            </div>

                            <!-- Contact Tab -->
                            <div class="tab-pane fade" id="tab-contact">
                                <div class="row g-3">
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.primary_phone') ?> <span class="text-danger">*</span></label>
                                        <input type="text" name="primary_phone" class="form-control" value="<?= e($app['primary_phone'] ?? '') ?>" required>
                                        <div class="invalid-feedback" data-field="primary_phone"></div>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.secondary_phone') ?></label>
                                        <input type="text" name="secondary_phone" class="form-control" value="<?= e($app['secondary_phone'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.email') ?></label>
                                        <input type="email" name="email" class="form-control" value="<?= e($app['email'] ?? '') ?>">
                                    </div>
                                </div>
                            </div>

                            <!-- Address Tab -->
                            <div class="tab-pane fade" id="tab-address">
                                <div class="row g-3">
                                    <div class="col-md-12">
                                        <label class="form-label"><?= lang('applications.address') ?></label>
                                        <input type="text" name="address_line1" class="form-control" value="<?= e($app['address_line1'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.country') ?></label>
                                        <select name="country_id" class="form-select select2" id="country-select">
                                            <option value="">-</option>
                                            <?php foreach ($countries as $c): ?>
                                            <option value="<?= $c['id'] ?>" <?= selected($c['id'], $app['country_id'] ?? '') ?>><?= e(current_lang() === 'ar' ? $c['name_ar'] : $c['name_en']) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.city') ?></label>
                                        <select name="city_id" class="form-select select2" id="city-select">
                                            <option value="">-</option>
                                        </select>
                                    </div>
                                </div>
                            </div>

                            <!-- Employment Tab -->
                            <div class="tab-pane fade" id="tab-employment">
                                <div class="row g-3">
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.occupation') ?></label>
                                        <input type="text" name="occupation" class="form-control" value="<?= e($app['occupation'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.employer') ?></label>
                                        <input type="text" name="employer" class="form-control" value="<?= e($app['employer'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.education_level') ?></label>
                                        <select name="education_level" class="form-select">
                                            <option value="">-</option>
                                            <option value="primary" <?= selected('primary', $app['education_level'] ?? '') ?>>Primary</option>
                                            <option value="secondary" <?= selected('secondary', $app['education_level'] ?? '') ?>>Secondary</option>
                                            <option value="diploma" <?= selected('diploma', $app['education_level'] ?? '') ?>>Diploma</option>
                                            <option value="bachelor" <?= selected('bachelor', $app['education_level'] ?? '') ?>>Bachelor</option>
                                            <option value="master" <?= selected('master', $app['education_level'] ?? '') ?>>Master</option>
                                            <option value="phd" <?= selected('phd', $app['education_level'] ?? '') ?>>PhD</option>
                                        </select>
                                    </div>
                                </div>
                            </div>

                            <!-- Membership Tab -->
                            <div class="tab-pane fade" id="tab-membership">
                                <div class="row g-3">
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.membership_type') ?> <span class="text-danger">*</span></label>
                                        <select name="membership_type_id" class="form-select select2" required>
                                            <option value="">-</option>
                                            <?php foreach ($membershipTypes as $t): ?>
                                            <option value="<?= $t['id'] ?>" <?= selected($t['id'], $app['membership_type_id'] ?? '') ?>><?= e(current_lang() === 'ar' ? $t['name_ar'] : $t['name_en']) ?> (<?= e($t['type_code']) ?>)</option>
                                            <?php endforeach; ?>
                                        </select>
                                        <div class="invalid-feedback" data-field="membership_type_id"></div>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.period') ?></label>
                                        <select name="application_period_id" class="form-select">
                                            <option value="">-</option>
                                            <?php foreach ($periods as $p): ?>
                                            <option value="<?= $p['id'] ?>" <?= selected($p['id'], $app['application_period_id'] ?? '') ?>><?= e(current_lang() === 'ar' ? $p['period_name_ar'] : $p['period_name_en']) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.source') ?></label>
                                        <select name="source" class="form-select">
                                            <option value="walk_in" <?= selected('walk_in', $app['source'] ?? '') ?>>Walk-in</option>
                                            <option value="online" <?= selected('online', $app['source'] ?? '') ?>>Online</option>
                                            <option value="referral" <?= selected('referral', $app['source'] ?? '') ?>>Referral</option>
                                            <option value="transfer" <?= selected('transfer', $app['source'] ?? '') ?>>Transfer</option>
                                            <option value="other" <?= selected('other', $app['source'] ?? '') ?>>Other</option>
                                        </select>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.priority') ?></label>
                                        <select name="priority" class="form-select">
                                            <option value="normal" <?= selected('normal', $app['priority'] ?? '') ?>>Normal</option>
                                            <option value="high" <?= selected('high', $app['priority'] ?? '') ?>>High</option>
                                            <option value="vip" <?= selected('vip', $app['priority'] ?? '') ?>>VIP</option>
                                        </select>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.form_number') ?></label>
                                        <input type="text" name="form_number" class="form-control" value="<?= e($app['form_number'] ?? '') ?>">
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.sponsor1') ?></label>
                                        <select name="sponsor1_member_id" class="form-select select2-member-search">
                                            <option value="">-</option>
                                        </select>
                                    </div>
                                    <div class="col-md-4">
                                        <label class="form-label"><?= lang('applications.sponsor2') ?></label>
                                        <select name="sponsor2_member_id" class="form-select select2-member-search">
                                            <option value="">-</option>
                                        </select>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <hr class="my-4">
                        <div class="d-flex gap-2">
                            <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                            <a href="/applications" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(document).ready(function() {
    // Member search Select2 for sponsors
    $('.select2-member-search').select2({
        theme: 'bootstrap-5',
        width: '100%',
        ajax: {
            url: '/api/members/search',
            dataType: 'json',
            delay: 300,
            data: function(params) { return { q: params.term }; },
            processResults: function(data) {
                return { results: (data.data || []).map(function(m) { return { id: m.id, text: m.membership_number + ' - ' + m.full_name_en }; }) };
            }
        },
        minimumInputLength: 2,
        placeholder: 'Search member...',
        allowClear: true,
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\applications\views\index.php

```php
<?php $__view->push('styles') ?>
<link href="/modules/applications/css/applications.css" rel="stylesheet">
<?php $__view->endPush() ?>

<div class="container-fluid fade-in">
    <!-- Stats Cards -->
    <div class="row g-3 mb-4" id="stats-cards">
        <div class="col-md-2"><div class="card border-warning h-100"><div class="card-body py-2"><h6 class="text-muted mb-1 small"><?= lang('applications.submitted') ?></h6><h3 class="mb-0" id="stat-submitted">-</h3></div></div></div>
        <div class="col-md-2"><div class="card border-info h-100"><div class="card-body py-2"><h6 class="text-muted mb-1 small"><?= lang('applications.under_review') ?></h6><h3 class="mb-0" id="stat-under_review">-</h3></div></div></div>
        <div class="col-md-2"><div class="card border-primary h-100"><div class="card-body py-2"><h6 class="text-muted mb-1 small"><?= lang('applications.board_pending') ?></h6><h3 class="mb-0" id="stat-board_pending">-</h3></div></div></div>
        <div class="col-md-2"><div class="card border-success h-100"><div class="card-body py-2"><h6 class="text-muted mb-1 small"><?= lang('applications.approved') ?></h6><h3 class="mb-0" id="stat-approved">-</h3></div></div></div>
        <div class="col-md-2"><div class="card border-danger h-100"><div class="card-body py-2"><h6 class="text-muted mb-1 small"><?= lang('applications.rejected') ?></h6><h3 class="mb-0" id="stat-rejected">-</h3></div></div></div>
        <div class="col-md-2"><div class="card border-secondary h-100"><div class="card-body py-2"><h6 class="text-muted mb-1 small"><?= lang('applications.documents_pending') ?></h6><h3 class="mb-0" id="stat-documents_pending">-</h3></div></div></div>
    </div>

    <!-- Page Header -->
    <div class="d-flex justify-content-between align-items-center mb-3">
        <h4 class="mb-0"><?= lang('applications.title') ?></h4>
        <div class="d-flex gap-2">
            <?php if (Gate::can('applications.create')): ?>
            <a href="/applications/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('applications.create') ?></a>
            <?php endif; ?>
            <div class="dropdown">
                <button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown"><i class="fas fa-download me-1"></i><?= lang('common.export_csv') ?></button>
                <ul class="dropdown-menu">
                    <li><a class="dropdown-item" href="/api/applications/export?format=csv"><i class="fas fa-file-csv me-2"></i>CSV</a></li>
                    <li><a class="dropdown-item" href="/api/applications/export?format=excel"><i class="fas fa-file-excel me-2"></i>Excel</a></li>
                </ul>
            </div>
        </div>
    </div>

    <!-- Filters -->
    <div class="card mb-3">
        <div class="card-body py-2">
            <form id="filter-form" class="row g-2 align-items-end" data-filter-table="applications-table">
                <div class="col-md-2">
                    <label class="form-label small"><?= lang('common.status') ?></label>
                    <select name="status" class="form-select form-select-sm">
                        <option value=""><?= lang('common.all') ?></option>
                        <option value="draft">Draft</option>
                        <option value="submitted">Submitted</option>
                        <option value="under_review">Under Review</option>
                        <option value="documents_pending">Documents Pending</option>
                        <option value="interview_scheduled">Interview Scheduled</option>
                        <option value="board_pending">Board Pending</option>
                        <option value="approved">Approved</option>
                        <option value="rejected">Rejected</option>
                        <option value="waitlisted">Waitlisted</option>
                    </select>
                </div>
                <div class="col-md-2">
                    <label class="form-label small"><?= lang('applications.membership_type') ?></label>
                    <select name="membership_type_id" class="form-select form-select-sm">
                        <option value=""><?= lang('common.all') ?></option>
                        <?php foreach ($membershipTypes as $t): ?>
                        <option value="<?= $t['id'] ?>"><?= e(current_lang() === 'ar' ? $t['name_ar'] : $t['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-2">
                    <label class="form-label small"><?= lang('applications.priority') ?></label>
                    <select name="priority" class="form-select form-select-sm">
                        <option value=""><?= lang('common.all') ?></option>
                        <option value="normal">Normal</option>
                        <option value="high">High</option>
                        <option value="vip">VIP</option>
                    </select>
                </div>
                <div class="col-md-2">
                    <label class="form-label small"><?= lang('applications.date_from') ?></label>
                    <input type="date" name="date_from" class="form-control form-control-sm datepicker">
                </div>
                <div class="col-md-2">
                    <label class="form-label small"><?= lang('applications.date_to') ?></label>
                    <input type="date" name="date_to" class="form-control form-control-sm datepicker">
                </div>
                <div class="col-md-2">
                    <button type="button" class="btn btn-sm btn-primary w-100" onclick="reloadTable()"><i class="fas fa-search me-1"></i><?= lang('common.search') ?></button>
                </div>
            </form>
        </div>
    </div>

    <!-- DataTable -->
    <div class="card">
        <div class="card-body">
            <table id="applications-table" class="table table-striped table-hover w-100">
                <thead>
                    <tr>
                        <th>#</th>
                        <th><?= lang('applications.app_number') ?></th>
                        <th><?= lang('applications.applicant_name') ?></th>
                        <th><?= lang('applications.phone') ?></th>
                        <th><?= lang('applications.type') ?></th>
                        <th><?= lang('common.status') ?></th>
                        <th><?= lang('applications.priority') ?></th>
                        <th><?= lang('applications.submission_date') ?></th>
                        <th><?= lang('applications.assigned_to') ?></th>
                        <th><?= lang('common.actions') ?></th>
                    </tr>
                </thead>
            </table>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
let appTable;

$(document).ready(function() {
    loadStats();

    appTable = $('#applications-table').DataTable({
        processing: true,
        serverSide: true,
        ajax: {
            url: '/api/applications/datatable',
            type: 'GET',
            data: function(d) {
                const form = document.getElementById('filter-form');
                if (form) {
                    const fd = new FormData(form);
                    for (const [key, value] of fd.entries()) {
                        if (value) d[key] = value;
                    }
                }
                return d;
            }
        },
        columns: [
            { data: 'id', orderable: true },
            { data: 'application_number', orderable: true },
            { data: 'full_name', orderable: true },
            { data: 'phone', orderable: false },
            { data: 'type', orderable: true },
            { data: 'status', orderable: true },
            { data: 'priority', orderable: true },
            { data: 'submission_date', orderable: true },
            { data: 'assigned_to', orderable: true },
            { data: 'actions', orderable: false, searchable: false },
        ],
        order: [[0, 'desc']],
        pageLength: 25,
        responsive: true,
        language: {
            processing: '<div class="spinner-border spinner-border-sm text-primary"></div>',
            emptyTable: '<?= lang('common.no_data') ?>',
        }
    });
});

function reloadTable() {
    appTable.ajax.reload(null, false);
    loadStats();
}

function loadStats() {
    $.get('/api/applications/stats', function(res) {
        if (res.success && res.data) {
            const d = res.data;
            $('#stat-submitted').text(d.submitted || 0);
            $('#stat-under_review').text(d.under_review || 0);
            $('#stat-board_pending').text(d.board_pending || 0);
            $('#stat-approved').text(d.approved || 0);
            $('#stat-rejected').text(d.rejected || 0);
            $('#stat-documents_pending').text(d.documents_pending || 0);
        }
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\applications\views\periods\create.php

```php
<?php
$isEdit = !empty($period);
$p = $period ?? [];
$formAction = $isEdit ? '/application-periods/' . $p['id'] : '/application-periods';
?>
<div class="container-fluid fade-in">
    <div class="row justify-content-center">
        <div class="col-lg-8">
            <div class="card">
                <div class="card-header"><h5 class="mb-0"><?= $isEdit ? lang('applications.edit_period') : lang('applications.create_period') ?></h5></div>
                <div class="card-body">
                    <form method="POST" action="<?= $formAction ?>" data-ajax="true">
                        <?= csrf_field() ?>
                        <div class="row g-3">
                            <div class="col-md-6">
                                <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                                <input type="text" name="period_name_en" class="form-control" value="<?= e($p['period_name_en'] ?? '') ?>" required>
                                <div class="invalid-feedback" data-field="period_name_en"></div>
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                                <input type="text" name="period_name_ar" class="form-control" dir="rtl" value="<?= e($p['period_name_ar'] ?? '') ?>" required>
                                <div class="invalid-feedback" data-field="period_name_ar"></div>
                            </div>
                            <div class="col-md-4">
                                <label class="form-label">Start Date <span class="text-danger">*</span></label>
                                <input type="text" name="start_date" class="form-control datepicker" value="<?= e($p['start_date'] ?? '') ?>" required>
                            </div>
                            <div class="col-md-4">
                                <label class="form-label">End Date <span class="text-danger">*</span></label>
                                <input type="text" name="end_date" class="form-control datepicker" value="<?= e($p['end_date'] ?? '') ?>" required>
                            </div>
                            <div class="col-md-4">
                                <label class="form-label">Max Applications</label>
                                <input type="number" name="max_applications" class="form-control" value="<?= e($p['max_applications'] ?? '') ?>">
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Applicable Membership Types</label>
                                <select name="membership_type_ids[]" class="form-select select2" multiple>
                                    <?php
                                    $selectedTypes = [];
                                    if ($isEdit && $p['membership_type_ids']) {
                                        $selectedTypes = json_decode($p['membership_type_ids'], true) ?: [];
                                    }
                                    ?>
                                    <?php foreach ($membershipTypes as $t): ?>
                                    <option value="<?= $t['id'] ?>" <?= in_array($t['id'], $selectedTypes) ? 'selected' : '' ?>><?= e(current_lang() === 'ar' ? $t['name_ar'] : $t['name_en']) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Active</label>
                                <select name="is_active" class="form-select">
                                    <option value="1" <?= selected(1, $p['is_active'] ?? 1) ?>>Yes</option>
                                    <option value="0" <?= selected(0, $p['is_active'] ?? 1) ?>>No</option>
                                </select>
                            </div>
                        </div>
                        <div class="mt-4 d-flex gap-2">
                            <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                            <a href="/application-periods" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
```

## File: modules\applications\views\periods\index.php

```php
<div class="container-fluid fade-in">
    <div class="d-flex justify-content-between align-items-center mb-3">
        <h4><?= lang('applications.periods') ?></h4>
        <?php if (Gate::can('applications.review')): ?>
        <a href="/application-periods/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
        <?php endif; ?>
    </div>

    <div class="card">
        <div class="card-body">
            <table id="periods-table" class="table table-striped w-100">
                <thead><tr><th>#</th><th>Name</th><th>Start</th><th>End</th><th>Max</th><th>Current</th><th>Status</th><th>Actions</th></tr></thead>
            </table>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(document).ready(function() {
    $('#periods-table').DataTable({
        processing: true, serverSide: true,
        ajax: { url: '/api/application-periods/datatable', type: 'GET' },
        columns: [
            { data: 'id' }, { data: 'name' }, { data: 'start_date' }, { data: 'end_date' },
            { data: 'max' }, { data: 'current' }, { data: 'status' }, { data: 'actions', orderable: false }
        ],
        order: [[0, 'desc']], pageLength: 25
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\applications\views\print.php

```php
<div class="container">
    <div class="text-center mb-4">
        <h2><?= e(Config::get('club_name_en', 'The Club')) ?></h2>
        <h4>Membership Application</h4>
        <p>Application #: <strong><?= e($application['application_number']) ?></strong></p>
    </div>

    <table class="table table-bordered">
        <tr><th width="30%">Full Name (EN)</th><td><?= e($application['full_name_en']) ?></td></tr>
        <tr><th>Full Name (AR)</th><td><?= e($application['full_name_ar'] ?? '-') ?></td></tr>
        <tr><th>National ID</th><td><?= e($application['national_id'] ?? '-') ?></td></tr>
        <tr><th>Date of Birth</th><td><?= $application['date_of_birth'] ? format_date($application['date_of_birth']) : '-' ?></td></tr>
        <tr><th>Gender</th><td><?= ucfirst(e($application['gender'])) ?></td></tr>
        <tr><th>Nationality</th><td><?= e($application['nationality_name_en'] ?? '-') ?></td></tr>
        <tr><th>Phone</th><td><?= e($application['primary_phone']) ?></td></tr>
        <tr><th>Email</th><td><?= e($application['email'] ?? '-') ?></td></tr>
        <tr><th>Address</th><td><?= e($application['address_line1'] ?? '-') ?></td></tr>
        <tr><th>Membership Type</th><td><?= e($application['type_name_en'] ?? '-') ?></td></tr>
        <tr><th>Status</th><td><?= ucfirst(str_replace('_', ' ', e($application['status']))) ?></td></tr>
        <tr><th>Submission Date</th><td><?= $application['submission_date'] ? format_date($application['submission_date']) : '-' ?></td></tr>
    </table>

    <?php if (!empty($sponsors)): ?>
    <h5 class="mt-4">Sponsors</h5>
    <table class="table table-bordered">
        <thead><tr><th>#</th><th>Name</th><th>Membership #</th></tr></thead>
        <tbody>
        <?php foreach ($sponsors as $s): ?>
        <tr><td><?= $s['sponsor_order'] ?></td><td><?= e($s['sponsor_name']) ?></td><td><?= e($s['membership_number']) ?></td></tr>
        <?php endforeach; ?>
        </tbody>
    </table>
    <?php endif; ?>

    <div class="row mt-5">
        <div class="col-6 text-center"><p>_________________________</p><p>Applicant Signature</p></div>
        <div class="col-6 text-center"><p>_________________________</p><p>Authorized Signature</p></div>
    </div>
</div>
```

## File: modules\applications\views\show.php

```php
<?php
$app = $application;
$lang = current_lang();
?>

<?php $__view->push('styles') ?>
<link href="/modules/applications/css/applications.css" rel="stylesheet">
<?php $__view->endPush() ?>

<div class="container-fluid fade-in">
    <!-- Header -->
    <div class="d-flex justify-content-between align-items-center mb-3">
        <div>
            <h4 class="mb-1"><?= e($app['full_name_en']) ?></h4>
            <div class="text-muted"><?= e($app['application_number']) ?> <?= status_badge($app['status']) ?> <?= status_badge($app['priority']) ?></div>
        </div>
        <div class="d-flex gap-2 flex-wrap">
            <?php if ($app['status'] === 'draft'): ?>
            <button class="btn btn-success" onclick="submitApplication(<?= $app['id'] ?>)"><i class="fas fa-paper-plane me-1"></i><?= lang('applications.submit') ?></button>
            <a href="/applications/<?= $app['id'] ?>/edit" class="btn btn-warning"><i class="fas fa-edit me-1"></i><?= lang('common.edit') ?></a>
            <?php endif; ?>

            <?php if (in_array($app['status'], ['submitted', 'under_review', 'documents_pending', 'interview_completed']) && Gate::can('applications.review')): ?>
            <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#statusModal"><i class="fas fa-exchange-alt me-1"></i><?= lang('applications.change_status') ?></button>
            <?php endif; ?>

            <?php if ($app['status'] === 'submitted' && Gate::can('applications.review')): ?>
            <button class="btn btn-info" data-bs-toggle="modal" data-bs-target="#reviewModal"><i class="fas fa-clipboard-check me-1"></i><?= lang('applications.review') ?></button>
            <?php endif; ?>

            <?php if (in_array($app['status'], ['under_review', 'interview_scheduled']) && Gate::can('applications.review')): ?>
            <button class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#interviewModal"><i class="fas fa-calendar me-1"></i><?= lang('applications.schedule_interview') ?></button>
            <?php endif; ?>

            <?php if ($app['status'] === 'board_pending' && Gate::can('applications.approve')): ?>
            <a href="/applications/<?= $app['id'] ?>/board-vote" class="btn btn-dark"><i class="fas fa-vote-yea me-1"></i><?= lang('applications.board_vote') ?></a>
            <?php endif; ?>

            <?php if ($app['status'] === 'approved' && !$app['resulting_member_id'] && Gate::can('applications.approve')): ?>
            <button class="btn btn-success" onclick="createMember(<?= $app['id'] ?>)"><i class="fas fa-user-plus me-1"></i><?= lang('applications.create_member') ?></button>
            <?php endif; ?>

            <a href="/applications/<?= $app['id'] ?>/print" class="btn btn-outline-secondary" target="_blank"><i class="fas fa-print me-1"></i><?= lang('common.print') ?></a>
        </div>
    </div>

    <?php if ($app['resulting_member_id']): ?>
    <div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Member created: <a href="/members/<?= $app['resulting_member_id'] ?>" class="fw-bold"><?= e($app['resulting_member_number'] ?? '#' . $app['resulting_member_id']) ?></a></div>
    <?php endif; ?>

    <!-- Tabs -->
    <ul class="nav nav-tabs mb-4" role="tablist">
        <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-details"><i class="fas fa-info-circle me-1"></i>Details</a></li>
        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-documents"><i class="fas fa-file me-1"></i>Documents <span class="badge bg-primary"><?= count($documents) ?></span></a></li>
        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-sponsors"><i class="fas fa-handshake me-1"></i>Sponsors <span class="badge bg-secondary"><?= count($sponsors) ?></span></a></li>
        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-reviews"><i class="fas fa-clipboard-check me-1"></i>Reviews</a></li>
        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-interviews"><i class="fas fa-calendar me-1"></i>Interviews</a></li>
        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-fees"><i class="fas fa-money-bill me-1"></i>Fees</a></li>
        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-history"><i class="fas fa-history me-1"></i>History</a></li>
    </ul>

    <div class="tab-content">
        <!-- Details Tab -->
        <div class="tab-pane fade show active" id="tab-details">
            <div class="row g-3">
                <div class="col-md-3">
                    <div class="card h-100">
                        <div class="card-body text-center">
                            <?php if ($app['photo_path']): ?>
                            <img src="/<?= e($app['photo_path']) ?>" class="img-fluid rounded mb-2" style="max-height:200px">
                            <?php else: ?>
                            <i class="fas fa-user-circle fa-5x text-muted mb-2"></i>
                            <?php endif; ?>
                            <h5><?= e($app['full_name_en']) ?></h5>
                            <?php if ($app['full_name_ar']): ?>
                            <p class="text-muted"><?= e($app['full_name_ar']) ?></p>
                            <?php endif; ?>
                            <p><?= status_badge($app['status']) ?></p>
                        </div>
                    </div>
                </div>
                <div class="col-md-9">
                    <div class="card">
                        <div class="card-body">
                            <div class="row g-2">
                                <div class="col-md-4"><strong><?= lang('applications.membership_type') ?>:</strong><br><?= e($app['type_name_en']) ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.national_id') ?>:</strong><br><?= e($app['national_id'] ?? '-') ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.dob') ?>:</strong><br><?= $app['date_of_birth'] ? format_date($app['date_of_birth']) : '-' ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.gender') ?>:</strong><br><?= ucfirst(e($app['gender'])) ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.nationality') ?>:</strong><br><?= e($app['nationality_name_en'] ?? '-') ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.marital_status') ?>:</strong><br><?= ucfirst(e($app['marital_status'] ?? '-')) ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.primary_phone') ?>:</strong><br><?= e($app['primary_phone']) ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.email') ?>:</strong><br><?= e($app['email'] ?? '-') ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.source') ?>:</strong><br><?= ucfirst(str_replace('_', ' ', e($app['source']))) ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.occupation') ?>:</strong><br><?= e($app['occupation'] ?? '-') ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.employer') ?>:</strong><br><?= e($app['employer'] ?? '-') ?></div>
                                <div class="col-md-4"><strong><?= lang('applications.assigned_to') ?>:</strong><br><?= e($app['assigned_to_name'] ?? '-') ?></div>
                                <div class="col-md-6"><strong><?= lang('applications.address') ?>:</strong><br><?= e($app['address_line1'] ?? '-') ?> <?= e($app['city_name_en'] ?? '') ?> <?= e($app['country_name_en'] ?? '') ?></div>
                                <div class="col-md-3"><strong><?= lang('applications.submission_date') ?>:</strong><br><?= $app['submission_date'] ? format_date($app['submission_date']) : '-' ?></div>
                                <div class="col-md-3"><strong><?= lang('applications.created') ?>:</strong><br><?= format_datetime($app['created_at']) ?></div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Documents Tab -->
        <div class="tab-pane fade" id="tab-documents">
            <div class="card">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h6 class="mb-0">Documents</h6>
                    <?php if (in_array($app['status'], ['draft', 'submitted', 'documents_pending', 'under_review'])): ?>
                    <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#uploadDocModal"><i class="fas fa-upload me-1"></i>Upload Document</button>
                    <?php endif; ?>
                </div>
                <div class="card-body">
                    <?php if ($requiredDocs): ?>
                    <div class="alert alert-info mb-3"><strong>Required documents:</strong>
                        <ul class="mb-0 mt-1">
                        <?php foreach ($requiredDocs as $rd): ?>
                        <li><?= e($lang === 'ar' ? $rd['document_name_ar'] : $rd['document_name_en']) ?> <?= $rd['is_mandatory'] ? '<span class="text-danger">*</span>' : '' ?></li>
                        <?php endforeach; ?>
                        </ul>
                    </div>
                    <?php endif; ?>

                    <?php if (empty($documents)): ?>
                    <p class="text-muted text-center py-3">No documents uploaded yet.</p>
                    <?php else: ?>
                    <div class="table-responsive">
                        <table class="table table-sm">
                            <thead><tr><th>Type</th><th>Name</th><th>Size</th><th>Uploaded</th><th>Verified</th><th>Actions</th></tr></thead>
                            <tbody>
                            <?php foreach ($documents as $doc): ?>
                            <tr>
                                <td><?= e($doc['document_type']) ?></td>
                                <td><?= e($doc['document_name']) ?></td>
                                <td><?= $doc['file_size_bytes'] ? round($doc['file_size_bytes'] / 1024) . ' KB' : '-' ?></td>
                                <td><?= format_datetime($doc['uploaded_at']) ?></td>
                                <td><?= $doc['is_verified'] ? '<span class="badge bg-success">Verified</span>' : '<span class="badge bg-warning">Pending</span>' ?></td>
                                <td>
                                    <a href="/<?= e($doc['file_path']) ?>" class="btn btn-sm btn-outline-primary" target="_blank"><i class="fas fa-download"></i></a>
                                    <?php if (!$doc['is_verified'] && Gate::can('applications.review')): ?>
                                    <button class="btn btn-sm btn-outline-success" onclick="verifyDoc(<?= $app['id'] ?>, <?= $doc['id'] ?>)"><i class="fas fa-check"></i></button>
                                    <?php endif; ?>
                                    <button class="btn btn-sm btn-outline-danger" onclick="deleteDoc(<?= $app['id'] ?>, <?= $doc['id'] ?>)"><i class="fas fa-trash"></i></button>
                                </td>
                            </tr>
                            <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                    <?php endif; ?>
                </div>
            </div>
        </div>

        <!-- Sponsors Tab -->
        <div class="tab-pane fade" id="tab-sponsors">
            <div class="card"><div class="card-body">
                <?php if (empty($sponsors)): ?>
                <p class="text-muted text-center py-3">No sponsors assigned.</p>
                <?php else: ?>
                <div class="table-responsive">
                    <table class="table table-sm">
                        <thead><tr><th>#</th><th>Member</th><th>Name</th><th>Phone</th><th>Status</th><th>Date</th></tr></thead>
                        <tbody>
                        <?php foreach ($sponsors as $sp): ?>
                        <tr>
                            <td><?= $sp['sponsor_order'] ?></td>
                            <td><a href="/members/<?= $sp['sponsor_member_id'] ?>"><?= e($sp['membership_number']) ?></a></td>
                            <td><?= e($sp['sponsor_name']) ?></td>
                            <td><?= e($sp['sponsor_phone'] ?? '-') ?></td>
                            <td><?= status_badge($sp['status']) ?></td>
                            <td><?= $sp['recommendation_date'] ? format_date($sp['recommendation_date']) : '-' ?></td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                <?php endif; ?>
            </div></div>
        </div>

        <!-- Reviews Tab -->
        <div class="tab-pane fade" id="tab-reviews">
            <div class="card"><div class="card-body">
                <?php if (empty($reviews)): ?>
                <p class="text-muted text-center py-3">No reviews recorded.</p>
                <?php else: ?>
                <?php foreach ($reviews as $rev): ?>
                <div class="border-start border-3 border-<?= $rev['decision'] === 'approve' ? 'success' : ($rev['decision'] === 'reject' ? 'danger' : 'warning') ?> ps-3 mb-3">
                    <div class="d-flex justify-content-between">
                        <strong><?= e($rev['reviewer_name']) ?></strong>
                        <small class="text-muted"><?= format_datetime($rev['reviewed_at']) ?></small>
                    </div>
                    <div>Type: <?= e($rev['review_type']) ?> | Decision: <?= status_badge($rev['decision']) ?></div>
                    <?php if ($rev['comments']): ?><p class="mb-0 mt-1"><?= e($rev['comments']) ?></p><?php endif; ?>
                </div>
                <?php endforeach; ?>
                <?php endif; ?>
            </div></div>
        </div>

        <!-- Interviews Tab -->
        <div class="tab-pane fade" id="tab-interviews">
            <div class="card"><div class="card-body">
                <?php if (empty($interviews)): ?>
                <p class="text-muted text-center py-3">No interviews scheduled.</p>
                <?php else: ?>
                <?php foreach ($interviews as $int): ?>
                <div class="border rounded p-3 mb-3">
                    <div class="d-flex justify-content-between">
                        <strong>Scheduled: <?= format_datetime($int['scheduled_date']) ?></strong>
                        <span><?= status_badge($int['status']) ?></span>
                    </div>
                    <?php if ($int['location']): ?><p class="mb-1">Location: <?= e($int['location']) ?></p><?php endif; ?>
                    <?php if ($int['recommendation']): ?><p class="mb-1">Recommendation: <?= status_badge($int['recommendation']) ?></p><?php endif; ?>
                    <?php if ($int['rating']): ?><p class="mb-1">Rating: <?= $int['rating'] ?>/10</p><?php endif; ?>
                    <?php if ($int['interview_notes']): ?><p class="mb-0 text-muted"><?= e($int['interview_notes']) ?></p><?php endif; ?>
                </div>
                <?php endforeach; ?>
                <?php endif; ?>
            </div></div>
        </div>

        <!-- Fees Tab -->
        <div class="tab-pane fade" id="tab-fees">
            <div class="card"><div class="card-body">
                <?php if (empty($fees)): ?>
                <p class="text-muted text-center py-3">No fees calculated.</p>
                <?php else: ?>
                <table class="table table-sm">
                    <thead><tr><th>Fee Type</th><th>Description</th><th>Amount</th><th>Paid</th></tr></thead>
                    <tbody>
                    <?php $totalFees = 0; ?>
                    <?php foreach ($fees as $fee): ?>
                    <?php $totalFees += (float)$fee['amount']; ?>
                    <tr>
                        <td><?= e($fee['fee_code']) ?></td>
                        <td><?= e($lang === 'ar' ? $fee['description_ar'] : $fee['description_en']) ?></td>
                        <td><?= money($fee['amount']) ?></td>
                        <td><?= $fee['is_paid'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-danger">No</span>' ?></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                    <tfoot><tr><th colspan="2">Total</th><th><?= money($totalFees) ?></th><th></th></tr></tfoot>
                </table>
                <?php endif; ?>
            </div></div>
        </div>

        <!-- History Tab -->
        <div class="tab-pane fade" id="tab-history">
            <div class="card"><div class="card-body">
                <?php if (empty($statusHistory)): ?>
                <p class="text-muted text-center py-3">No status changes recorded.</p>
                <?php else: ?>
                <div class="timeline">
                <?php foreach ($statusHistory as $sh): ?>
                <div class="d-flex mb-3">
                    <div class="me-3">
                        <div class="bg-primary rounded-circle d-flex align-items-center justify-content-center" style="width:32px;height:32px"><i class="fas fa-circle fa-xs text-white"></i></div>
                    </div>
                    <div>
                        <div class="d-flex gap-2">
                            <?php if ($sh['from_status']): ?><?= status_badge($sh['from_status']) ?> â†’ <?php endif; ?><?= status_badge($sh['to_status']) ?>
                        </div>
                        <small class="text-muted"><?= format_datetime($sh['changed_at']) ?> by <?= e($sh['changed_by_name'] ?? 'System') ?></small>
                        <?php if ($sh['reason']): ?><br><small><?= e($sh['reason']) ?></small><?php endif; ?>
                    </div>
                </div>
                <?php endforeach; ?>
                </div>
                <?php endif; ?>
            </div></div>
        </div>
    </div>
</div>

<!-- Status Change Modal -->
<div class="modal fade" id="statusModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title"><?= lang('applications.change_status') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="status-form">
            <?= csrf_field() ?>
            <div class="mb-3">
                <label class="form-label">New Status <span class="text-danger">*</span></label>
                <select name="status" class="form-select" required>
                    <option value="under_review">Under Review</option>
                    <option value="documents_pending">Documents Pending</option>
                    <option value="interview_scheduled">Interview Scheduled</option>
                    <option value="board_pending">Board Pending</option>
                    <option value="approved">Approved</option>
                    <option value="rejected">Rejected</option>
                    <option value="waitlisted">Waitlisted</option>
                    <option value="cancelled">Cancelled</option>
                </select>
            </div>
            <div class="mb-3">
                <label class="form-label">Assign To</label>
                <select name="assigned_to" class="form-select">
                    <option value="">-</option>
                    <?php foreach ($users as $u): ?>
                    <option value="<?= $u['id'] ?>"><?= e($u['full_name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="mb-3">
                <label class="form-label">Reason</label>
                <textarea name="reason" class="form-control" rows="2"></textarea>
            </div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
        <button type="button" class="btn btn-primary" onclick="saveStatus()"><?= lang('common.save') ?></button>
    </div>
</div></div></div>

<!-- Review Modal -->
<div class="modal fade" id="reviewModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title"><?= lang('applications.review') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="review-form">
            <?= csrf_field() ?>
            <div class="mb-3">
                <label class="form-label">Review Type <span class="text-danger">*</span></label>
                <select name="review_type" class="form-select" required>
                    <option value="initial">Initial Review</option>
                    <option value="document">Document Review</option>
                    <option value="background">Background Check</option>
                    <option value="final">Final Review</option>
                </select>
            </div>
            <div class="mb-3">
                <label class="form-label">Decision <span class="text-danger">*</span></label>
                <select name="decision" class="form-select" required>
                    <option value="approve">Approve / Forward</option>
                    <option value="return">Return for Documents</option>
                    <option value="escalate">Escalate to Board</option>
                    <option value="reject">Reject</option>
                </select>
            </div>
            <div class="mb-3">
                <label class="form-label">Comments</label>
                <textarea name="comments" class="form-control" rows="3"></textarea>
            </div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
        <button type="button" class="btn btn-primary" onclick="saveReview()"><?= lang('common.save') ?></button>
    </div>
</div></div></div>

<!-- Interview Modal -->
<div class="modal fade" id="interviewModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title"><?= lang('applications.schedule_interview') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="interview-form">
            <?= csrf_field() ?>
            <input type="hidden" name="action" value="schedule">
            <div class="mb-3">
                <label class="form-label">Date & Time <span class="text-danger">*</span></label>
                <input type="datetime-local" name="scheduled_date" class="form-control" required>
            </div>
            <div class="mb-3">
                <label class="form-label">Location</label>
                <input type="text" name="location" class="form-control">
            </div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
        <button type="button" class="btn btn-primary" onclick="saveInterview()"><?= lang('common.save') ?></button>
    </div>
</div></div></div>

<!-- Upload Document Modal -->
<div class="modal fade" id="uploadDocModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Upload Document</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="upload-doc-form" enctype="multipart/form-data">
            <?= csrf_field() ?>
            <div class="mb-3">
                <label class="form-label">Document Type</label>
                <select name="document_type" class="form-select">
                    <option value="general">General</option>
                    <option value="national_id">National ID</option>
                    <option value="passport">Passport</option>
                    <option value="photo">Photo</option>
                    <option value="birth_certificate">Birth Certificate</option>
                    <option value="address_proof">Address Proof</option>
                    <option value="employment_proof">Employment Proof</option>
                    <option value="other">Other</option>
                </select>
            </div>
            <div class="mb-3">
                <label class="form-label">Document Name</label>
                <input type="text" name="document_name" class="form-control">
            </div>
            <div class="mb-3">
                <label class="form-label">File <span class="text-danger">*</span></label>
                <input type="file" name="document_file" class="form-control" required accept=".pdf,.jpg,.jpeg,.png,.doc,.docx">
            </div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
        <button type="button" class="btn btn-primary" onclick="uploadDocument()">Upload</button>
    </div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
const appId = <?= $app['id'] ?>;

function submitApplication(id) {
    Swal.fire({
        title: '<?= lang('applications.confirm_submit') ?>',
        text: '<?= lang('applications.confirm_submit_text') ?>',
        icon: 'question',
        showCancelButton: true,
        confirmButtonText: '<?= lang('applications.submit') ?>',
        cancelButtonText: '<?= lang('common.cancel') ?>',
    }).then((result) => {
        if (result.isConfirmed) {
            $.post('/api/applications/' + id + '/submit', { _token: '<?= csrf_token() ?>' }, function(res) {
                if (res.success) {
                    FormHelper.showToast('success', res.message);
                    setTimeout(() => location.reload(), 800);
                } else {
                    FormHelper.showToast('error', res.message);
                }
            });
        }
    });
}

function saveStatus() {
    const data = Object.fromEntries(new FormData(document.getElementById('status-form')));
    $.post('/api/applications/' + appId + '/status', data, function(res) {
        if (res.success) {
            bootstrap.Modal.getInstance(document.getElementById('statusModal')).hide();
            FormHelper.showToast('success', res.message);
            setTimeout(() => location.reload(), 800);
        } else {
            FormHelper.showToast('error', res.message);
        }
    });
}

function saveReview() {
    const data = Object.fromEntries(new FormData(document.getElementById('review-form')));
    $.post('/api/applications/' + appId + '/review', data, function(res) {
        if (res.success) {
            bootstrap.Modal.getInstance(document.getElementById('reviewModal')).hide();
            FormHelper.showToast('success', res.message);
            setTimeout(() => location.reload(), 800);
        } else {
            FormHelper.showToast('error', res.message || 'Failed');
        }
    });
}

function saveInterview() {
    const data = Object.fromEntries(new FormData(document.getElementById('interview-form')));
    $.post('/api/applications/' + appId + '/interview', data, function(res) {
        if (res.success) {
            bootstrap.Modal.getInstance(document.getElementById('interviewModal')).hide();
            FormHelper.showToast('success', res.message);
            setTimeout(() => location.reload(), 800);
        } else {
            FormHelper.showToast('error', res.message || 'Failed');
        }
    });
}

function createMember(id) {
    Swal.fire({
        title: '<?= lang('applications.confirm_create_member') ?>',
        text: '<?= lang('applications.confirm_create_member_text') ?>',
        icon: 'question',
        showCancelButton: true,
        confirmButtonText: '<?= lang('applications.create_member') ?>',
    }).then((result) => {
        if (result.isConfirmed) {
            $.post('/api/applications/' + id + '/create-member', { _token: '<?= csrf_token() ?>' }, function(res) {
                if (res.success) {
                    FormHelper.showToast('success', res.message);
                    if (res.redirect) setTimeout(() => window.location.href = res.redirect, 800);
                } else {
                    FormHelper.showToast('error', res.message);
                }
            });
        }
    });
}

function uploadDocument() {
    const form = document.getElementById('upload-doc-form');
    const fd = new FormData(form);
    $.ajax({
        url: '/api/applications/' + appId + '/documents',
        type: 'POST',
        data: fd,
        processData: false,
        contentType: false,
        success: function(res) {
            if (res.success) {
                bootstrap.Modal.getInstance(document.getElementById('uploadDocModal')).hide();
                FormHelper.showToast('success', res.message);
                setTimeout(() => location.reload(), 800);
            } else {
                FormHelper.showToast('error', res.message);
            }
        }
    });
}

function verifyDoc(appId, docId) {
    $.post('/api/applications/' + appId + '/documents/' + docId + '/verify', { _token: '<?= csrf_token() ?>' }, function(res) {
        if (res.success) {
            FormHelper.showToast('success', res.message);
            setTimeout(() => location.reload(), 500);
        }
    });
}

function deleteDoc(appId, docId) {
    Swal.fire({ title: 'Delete document?', icon: 'warning', showCancelButton: true, confirmButtonColor: '#e53e3e' }).then((result) => {
        if (result.isConfirmed) {
            $.post('/api/applications/' + appId + '/documents/' + docId + '/delete', { _token: '<?= csrf_token() ?>' }, function(res) {
                if (res.success) { FormHelper.showToast('success', res.message); setTimeout(() => location.reload(), 500); }
            });
        }
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\applications\views\waiting-list\create-entry.php

```php
<div class="container-fluid fade-in">
    <div class="row justify-content-center">
        <div class="col-lg-8">
            <div class="card">
                <div class="card-header"><h5 class="mb-0"><i class="fas fa-hourglass-half me-2"></i><?= lang('applications.add_to_waitlist') ?></h5></div>
                <div class="card-body">
                    <form method="POST" action="/waiting-list/entries" data-ajax="true">
                        <?= csrf_field() ?>
                        <div class="row g-3">
                            <div class="col-md-6">
                                <label class="form-label">Waiting List <span class="text-danger">*</span></label>
                                <select name="waiting_list_id" class="form-select select2" required>
                                    <option value="">-</option>
                                    <?php foreach ($waitingLists as $wl): ?>
                                    <option value="<?= $wl['id'] ?>"><?= e(current_lang() === 'ar' ? $wl['type_name_ar'] : $wl['type_name_en']) ?> (<?= $wl['current_entries'] ?><?= $wl['max_entries'] ? '/' . $wl['max_entries'] : '' ?>)</option>
                                    <?php endforeach; ?>
                                </select>
                                <div class="invalid-feedback" data-field="waiting_list_id"></div>
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Priority</label>
                                <select name="priority" class="form-select">
                                    <option value="normal">Normal</option>
                                    <option value="high">High</option>
                                    <option value="vip">VIP</option>
                                </select>
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Applicant Name <span class="text-danger">*</span></label>
                                <input type="text" name="applicant_name" class="form-control" required>
                                <div class="invalid-feedback" data-field="applicant_name"></div>
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Phone <span class="text-danger">*</span></label>
                                <input type="text" name="applicant_phone" class="form-control" required>
                                <div class="invalid-feedback" data-field="applicant_phone"></div>
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">Email</label>
                                <input type="email" name="applicant_email" class="form-control">
                            </div>
                            <div class="col-md-6">
                                <label class="form-label">National ID</label>
                                <input type="text" name="applicant_national_id" class="form-control" maxlength="20">
                            </div>
                            <div class="col-12">
                                <label class="form-label">Notes</label>
                                <textarea name="notes" class="form-control" rows="2"></textarea>
                            </div>
                        </div>
                        <div class="mt-4 d-flex gap-2">
                            <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                            <a href="/waiting-list" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
```

## File: modules\applications\views\waiting-list\index.php

```php
<div class="container-fluid fade-in">
    <div class="d-flex justify-content-between align-items-center mb-3">
        <h4><?= lang('applications.waiting_list') ?></h4>
        <?php if (Gate::can('waitlist.manage')): ?>
        <a href="/waiting-list/create-entry" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('applications.add_to_waitlist') ?></a>
        <?php endif; ?>
    </div>

    <!-- Summary Cards -->
    <div class="card mb-4">
        <div class="card-header"><h6 class="mb-0">Waiting Lists by Type</h6></div>
        <div class="card-body">
            <table id="waitlists-table" class="table table-sm w-100">
                <thead><tr><th>#</th><th>Type</th><th>Max</th><th>Current</th><th>Waiting</th><th>Offered</th><th>Status</th></tr></thead>
            </table>
        </div>
    </div>

    <!-- Entries -->
    <div class="card">
        <div class="card-header d-flex justify-content-between align-items-center">
            <h6 class="mb-0">All Entries</h6>
            <div class="d-flex gap-2">
                <select id="filter-list" class="form-select form-select-sm" style="width:auto">
                    <option value="">All Lists</option>
                    <?php foreach ($membershipTypes as $t): ?>
                    <option value=""><?= e($t['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
                <select id="filter-entry-status" class="form-select form-select-sm" style="width:auto">
                    <option value="">All Statuses</option>
                    <option value="waiting">Waiting</option>
                    <option value="offered">Offered</option>
                    <option value="accepted">Accepted</option>
                    <option value="declined">Declined</option>
                    <option value="expired">Expired</option>
                </select>
            </div>
        </div>
        <div class="card-body">
            <table id="entries-table" class="table table-striped w-100">
                <thead><tr><th>Queue #</th><th>Name</th><th>Phone</th><th>National ID</th><th>Type</th><th>Date</th><th>Priority</th><th>Status</th><th>Actions</th></tr></thead>
            </table>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
let entriesTable;

$(document).ready(function() {
    $('#waitlists-table').DataTable({
        processing: true, serverSide: true,
        ajax: { url: '/api/waiting-list/datatable', type: 'GET' },
        columns: [
            { data: 'id' }, { data: 'type' }, { data: 'max' }, { data: 'current' },
            { data: 'waiting' }, { data: 'offered' }, { data: 'status' }
        ],
        paging: false, searching: false, info: false, ordering: false,
    });

    entriesTable = $('#entries-table').DataTable({
        processing: true, serverSide: true,
        ajax: {
            url: '/api/waiting-list/entries/datatable', type: 'GET',
            data: function(d) {
                d.waiting_list_id = $('#filter-list').val();
                d.status = $('#filter-entry-status').val();
            }
        },
        columns: [
            { data: 'queue_number' }, { data: 'name' }, { data: 'phone' }, { data: 'national_id' },
            { data: 'type' }, { data: 'date' }, { data: 'priority' }, { data: 'status' },
            { data: 'actions', orderable: false }
        ],
        order: [[0, 'asc']], pageLength: 25,
    });

    $('#filter-list, #filter-entry-status').on('change', function() { entriesTable.ajax.reload(); });
});

function offerEntry(id) {
    Swal.fire({ title: 'Send offer to this applicant?', icon: 'question', showCancelButton: true }).then(r => {
        if (r.isConfirmed) {
            $.post('/api/waiting-list/entries/' + id + '/offer', { _token: '<?= csrf_token() ?>' }, function(res) {
                if (res.success) { FormHelper.showToast('success', res.message); entriesTable.ajax.reload(); }
                else FormHelper.showToast('error', res.message);
            });
        }
    });
}

function promoteEntry(id) {
    $.post('/api/waiting-list/entries/' + id + '/promote', { _token: '<?= csrf_token() ?>' }, function(res) {
        if (res.success) { FormHelper.showToast('success', res.message); entriesTable.ajax.reload(); }
        else FormHelper.showToast('error', res.message);
    });
}

function removeEntry(id) {
    Swal.fire({ title: 'Remove this entry?', icon: 'warning', showCancelButton: true, confirmButtonColor: '#e53e3e' }).then(r => {
        if (r.isConfirmed) {
            $.post('/api/waiting-list/entries/' + id + '/remove', { _token: '<?= csrf_token() ?>' }, function(res) {
                if (res.success) { FormHelper.showToast('success', res.message); entriesTable.ajax.reload(); }
                else FormHelper.showToast('error', res.message);
            });
        }
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\applications\views\widgets\pending_applications.php

```php
<?php
$db = Database::getInstance();
$pending = $db->fetchOne(
    "SELECT
        SUM(CASE WHEN status = 'submitted' THEN 1 ELSE 0 END) as submitted,
        SUM(CASE WHEN status = 'under_review' THEN 1 ELSE 0 END) as under_review,
        SUM(CASE WHEN status = 'board_pending' THEN 1 ELSE 0 END) as board_pending,
        SUM(CASE WHEN status NOT IN ('approved','rejected','cancelled','expired') THEN 1 ELSE 0 END) as total_pending
     FROM applications WHERE is_deleted = 0"
);
$totalPending = (int)($pending['total_pending'] ?? 0);
?>
<div class="card border-warning h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('applications.pending_applications') ?></h6>
                <h3 class="mb-0"><?= number_format($totalPending) ?></h3>
                <small class="text-muted">
                    Submitted: <?= (int)($pending['submitted'] ?? 0) ?> |
                    Review: <?= (int)($pending['under_review'] ?? 0) ?> |
                    Board: <?= (int)($pending['board_pending'] ?? 0) ?>
                </small>
            </div>
            <div class="align-self-center"><i class="fas fa-file-alt fa-2x text-warning opacity-50"></i></div>
        </div>
    </div>
    <div class="card-footer bg-transparent border-0 pt-0">
        <a href="/applications" class="text-decoration-none small">View all â†’</a>
    </div>
</div>
```

## File: modules\applications\views\widgets\waiting_list_overview.php

```php
<?php
$db = Database::getInstance();
$wlData = $db->fetchAll(
    "SELECT mt.name_en, wl.current_entries, wl.max_entries, wl.is_open
     FROM waiting_lists wl
     JOIN membership_types mt ON wl.membership_type_id = mt.id
     ORDER BY mt.sort_order LIMIT 5"
);
$totalWaiting = 0;
foreach ($wlData as $w) { $totalWaiting += (int)$w['current_entries']; }
?>
<div class="card border-info h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between mb-2">
            <div>
                <h6 class="text-muted mb-1"><?= lang('applications.waiting_list') ?></h6>
                <h3 class="mb-0"><?= number_format($totalWaiting) ?></h3>
            </div>
            <div class="align-self-center"><i class="fas fa-hourglass-half fa-2x text-info opacity-50"></i></div>
        </div>
        <?php if (!empty($wlData)): ?>
        <div class="small">
            <?php foreach ($wlData as $w): ?>
            <div class="d-flex justify-content-between">
                <span><?= e($w['name_en']) ?></span>
                <span class="fw-bold"><?= $w['current_entries'] ?><?= $w['max_entries'] ? '/' . $w['max_entries'] : '' ?></span>
            </div>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>
    </div>
    <div class="card-footer bg-transparent border-0 pt-0">
        <a href="/waiting-list" class="text-decoration-none small">View all â†’</a>
    </div>
</div>
```

## File: modules\board\Controllers\BoardMemberController.php

```php
<?php
class BoardMemberController extends Controller
{
    public function index(Request $request): void
    {
        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar, status FROM board_terms ORDER BY start_date DESC");
        $positions = $this->db()->fetchAll("SELECT id, title_en, title_ar FROM board_positions WHERE is_active = 1 ORDER BY sort_order");

        $this->view('members/index', [
            'title' => lang('board.board_members'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.board_members')]
            ],
            'terms' => $terms,
            'positions' => $positions,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $termFilter = $request->get('term_id', '');
        $statusFilter = $request->get('status', '');

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (bm.full_name_en LIKE ? OR bm.full_name_ar LIKE ?)";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
        }
        if ($termFilter) {
            $where .= " AND bm.term_id = ?";
            $params[] = $termFilter;
        }
        if ($statusFilter) {
            $where .= " AND bm.status = ?";
            $params[] = $statusFilter;
        }

        $countSql = "SELECT COUNT(*) FROM board_members bm WHERE {$where}";
        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_members");
        $filtered = (int) $db->fetchColumn($countSql, $params);

        $rows = $db->fetchAll(
            "SELECT bm.*, bp.title_en as position_title, bp.title_ar as position_title_ar,
                    bt.term_name_en, bt.term_name_ar
             FROM board_members bm
             LEFT JOIN board_positions bp ON bm.position_id = bp.id
             LEFT JOIN board_terms bt ON bm.term_id = bt.id
             WHERE {$where}
             ORDER BY bt.start_date DESC, bp.sort_order ASC
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '';
            if (auth_can('board.manage')) {
                $actions .= '<a href="/board/members/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1"><i class="fas fa-edit"></i></a>';
                if ($row['status'] === 'active') {
                    $actions .= '<button class="btn btn-sm btn-outline-secondary me-1 btn-resign" data-id="' . $row['id'] . '"><i class="fas fa-user-slash"></i></button>';
                }
                $actions .= '<form method="POST" action="/board/members/' . $row['id'] . '/delete" class="d-inline" data-confirm="true"><input type="hidden" name="_token" value="' . csrf_token() . '"><button class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button></form>';
            }

            $data[] = [
                'id' => $row['id'],
                'name' => e(current_lang() === 'ar' ? ($row['full_name_ar'] ?: $row['full_name_en']) : $row['full_name_en']),
                'position' => e(current_lang() === 'ar' ? ($row['position_title_ar'] ?: $row['position_title']) : $row['position_title']),
                'term' => e(current_lang() === 'ar' ? ($row['term_name_ar'] ?: $row['term_name_en']) : $row['term_name_en']),
                'start_date' => format_date($row['start_date']),
                'status' => status_badge($row['status']),
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar FROM board_terms WHERE status IN ('upcoming','active') ORDER BY start_date DESC");
        $positions = $this->db()->fetchAll("SELECT id, title_en, title_ar FROM board_positions WHERE is_active = 1 ORDER BY sort_order");

        $this->view('members/form', [
            'title' => lang('board.add_member'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.board_members'), 'url' => '/board/members'],
                ['label' => lang('common.create')]
            ],
            'boardMember' => null,
            'terms' => $terms,
            'positions' => $positions,
            'formAction' => '/board/members',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'term_id'       => 'required|integer|exists:board_terms,id',
            'position_id'   => 'required|integer|exists:board_positions,id',
            'full_name_en'  => 'required|string|max:300',
            'full_name_ar'  => 'nullable|string|max:300',
            'member_id'     => 'nullable|integer',
            'user_id'       => 'nullable|integer',
            'start_date'    => 'required|date',
            'end_date'      => 'nullable|date',
            'notes'         => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['status'] = 'active';
        $id = $this->db()->insert('board_members', $data);

        $this->audit('create', 'board_members', $id, 'Added board member: ' . $data['full_name_en']);
        $this->json(['success' => true, 'message' => lang('board.member_added'), 'redirect' => '/board/members']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $boardMember = $this->db()->fetchOne("SELECT * FROM board_members WHERE id = ?", [$id]);
        if (!$boardMember) { $this->abort(404); return; }

        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar FROM board_terms ORDER BY start_date DESC");
        $positions = $this->db()->fetchAll("SELECT id, title_en, title_ar FROM board_positions WHERE is_active = 1 ORDER BY sort_order");

        $this->view('members/form', [
            'title' => lang('board.edit_member'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.board_members'), 'url' => '/board/members'],
                ['label' => lang('common.edit')]
            ],
            'boardMember' => $boardMember,
            'terms' => $terms,
            'positions' => $positions,
            'formAction' => '/board/members/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $old = $this->db()->fetchOne("SELECT * FROM board_members WHERE id = ?", [$id]);
        if (!$old) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'term_id'       => 'required|integer|exists:board_terms,id',
            'position_id'   => 'required|integer|exists:board_positions,id',
            'full_name_en'  => 'required|string|max:300',
            'full_name_ar'  => 'nullable|string|max:300',
            'member_id'     => 'nullable|integer',
            'user_id'       => 'nullable|integer',
            'start_date'    => 'required|date',
            'end_date'      => 'nullable|date',
            'status'        => 'required|in:active,resigned,removed,term_ended',
            'notes'         => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $this->db()->update('board_members', $data, 'id = ?', [$id]);

        $this->audit('update', 'board_members', $id, 'Updated board member: ' . $data['full_name_en'], $old, $data);
        $this->json(['success' => true, 'message' => lang('board.member_updated'), 'redirect' => '/board/members']);
    }

    public function resign(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $bm = $this->db()->fetchOne("SELECT * FROM board_members WHERE id = ? AND status = 'active'", [$id]);
        if (!$bm) { $this->json(['success' => false, 'message' => 'Not found or not active'], 404); return; }

        $reason = $request->post('resignation_reason', '');
        $this->db()->update('board_members', [
            'status' => 'resigned',
            'resignation_date' => date('Y-m-d'),
            'resignation_reason' => $reason,
            'end_date' => date('Y-m-d'),
        ], 'id = ?', [$id]);

        $this->audit('update', 'board_members', $id, 'Board member resigned: ' . $bm['full_name_en']);
        $this->json(['success' => true, 'message' => lang('board.member_resigned')]);
    }

    public function delete(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $this->db()->delete('board_committee_members', 'board_member_id = ?', [$id]);
        $this->db()->delete('board_members', 'id = ?', [$id]);
        $this->audit('delete', 'board_members', $id, 'Deleted board member');
        $this->json(['success' => true, 'message' => lang('board.member_deleted'), 'redirect' => '/board/members']);
    }
}
```

## File: modules\board\Controllers\BoardTermController.php

```php
<?php
class BoardTermController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('terms/index', [
            'title' => lang('board.terms'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.terms')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = ($request->get('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

        $columns = ['id', 'term_name_en', 'start_date', 'end_date', 'status', 'created_at'];
        $orderBy = $columns[$orderCol] ?? 'id';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (term_name_en LIKE ? OR term_name_ar LIKE ?)";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_terms");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_terms WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT * FROM board_terms WHERE {$where} ORDER BY {$orderBy} {$orderDir} LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $memberCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_members WHERE term_id = ? AND status = 'active'", [$row['id']]);
            $actions = '';
            if (auth_can('board.view')) {
                $actions .= '<a href="/board/terms/' . $row['id'] . '" class="btn btn-sm btn-outline-primary me-1" title="View"><i class="fas fa-eye"></i></a>';
            }
            if (auth_can('board.manage')) {
                $actions .= '<a href="/board/terms/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1" title="Edit"><i class="fas fa-edit"></i></a>';
                $actions .= '<form method="POST" action="/board/terms/' . $row['id'] . '/delete" class="d-inline" data-confirm="true"><input type="hidden" name="_token" value="' . csrf_token() . '"><button type="submit" class="btn btn-sm btn-outline-danger" title="Delete"><i class="fas fa-trash"></i></button></form>';
            }

            $data[] = [
                'id' => $row['id'],
                'term_name' => e(current_lang() === 'ar' ? ($row['term_name_ar'] ?: $row['term_name_en']) : $row['term_name_en']),
                'start_date' => format_date($row['start_date']),
                'end_date' => format_date($row['end_date']),
                'status' => status_badge($row['status']),
                'members' => $memberCount,
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $this->view('terms/form', [
            'title' => lang('board.create_term'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.terms'), 'url' => '/board/terms'],
                ['label' => lang('common.create')]
            ],
            'term' => null,
            'formAction' => '/board/terms',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'term_name_en' => 'required|string|max:200',
            'term_name_ar' => 'required|string|max:200',
            'start_date'   => 'required|date',
            'end_date'     => 'required|date',
            'status'       => 'required|in:upcoming,active,ended',
            'election_date'=> 'nullable|date',
            'notes'        => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $id = $this->db()->insert('board_terms', $data);

        $this->audit('create', 'board_terms', $id, 'Created board term: ' . $data['term_name_en']);
        $this->json(['success' => true, 'message' => lang('board.term_created'), 'redirect' => '/board/terms']);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $term = $this->db()->fetchOne("SELECT * FROM board_terms WHERE id = ?", [$id]);
        if (!$term) { $this->abort(404, 'Term not found'); return; }

        $members = $this->db()->fetchAll(
            "SELECT bm.*, bp.title_en as position_title, bp.title_ar as position_title_ar
             FROM board_members bm
             LEFT JOIN board_positions bp ON bm.position_id = bp.id
             WHERE bm.term_id = ?
             ORDER BY bp.sort_order ASC",
            [$id]
        );

        $meetings = $this->db()->fetchAll("SELECT * FROM board_meetings WHERE term_id = ? ORDER BY meeting_date DESC LIMIT 10", [$id]);
        $resolutions = $this->db()->fetchAll("SELECT * FROM board_resolutions WHERE term_id = ? ORDER BY resolution_date DESC LIMIT 10", [$id]);

        $this->view('terms/show', [
            'title' => e($term['term_name_en']),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.terms'), 'url' => '/board/terms'],
                ['label' => e($term['term_name_en'])]
            ],
            'term' => $term,
            'members' => $members,
            'meetings' => $meetings,
            'resolutions' => $resolutions,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $term = $this->db()->fetchOne("SELECT * FROM board_terms WHERE id = ?", [$id]);
        if (!$term) { $this->abort(404, 'Term not found'); return; }

        $this->view('terms/form', [
            'title' => lang('board.edit_term'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.terms'), 'url' => '/board/terms'],
                ['label' => lang('common.edit')]
            ],
            'term' => $term,
            'formAction' => '/board/terms/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $term = $this->db()->fetchOne("SELECT * FROM board_terms WHERE id = ?", [$id]);
        if (!$term) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'term_name_en' => 'required|string|max:200',
            'term_name_ar' => 'required|string|max:200',
            'start_date'   => 'required|date',
            'end_date'     => 'required|date',
            'status'       => 'required|in:upcoming,active,ended',
            'election_date'=> 'nullable|date',
            'notes'        => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $this->db()->update('board_terms', $data, 'id = ?', [$id]);

        $this->audit('update', 'board_terms', $id, 'Updated board term: ' . $data['term_name_en'], $term, $data);
        $this->json(['success' => true, 'message' => lang('board.term_updated'), 'redirect' => '/board/terms']);
    }

    public function delete(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $memberCount = (int) $this->db()->fetchColumn("SELECT COUNT(*) FROM board_members WHERE term_id = ?", [$id]);
        if ($memberCount > 0) {
            $this->json(['success' => false, 'message' => 'Cannot delete term with board members assigned'], 400);
            return;
        }

        $this->db()->delete('board_terms', 'id = ?', [$id]);
        $this->audit('delete', 'board_terms', $id, 'Deleted board term');
        $this->json(['success' => true, 'message' => lang('board.term_deleted'), 'redirect' => '/board/terms']);
    }
}
```

## File: modules\board\Controllers\CommitteeController.php

```php
<?php
class CommitteeController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('committees/index', [
            'title' => lang('board.committees'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.committees')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (c.name_en LIKE ? OR c.name_ar LIKE ? OR c.committee_code LIKE ?)";
            $params = array_merge($params, ["%{$search}%", "%{$search}%", "%{$search}%"]);
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_committees");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_committees c WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT c.*, bt.term_name_en, bt.term_name_ar,
                    (SELECT COUNT(*) FROM board_committee_members bcm WHERE bcm.committee_id = c.id AND bcm.is_active = 1) as member_count
             FROM board_committees c
             LEFT JOIN board_terms bt ON c.term_id = bt.id
             WHERE {$where}
             ORDER BY c.created_at DESC
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '';
            if (auth_can('board.manage')) {
                $actions .= '<a href="/board/committees/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1"><i class="fas fa-edit"></i></a>';
            }
            $data[] = [
                'id' => $row['id'],
                'code' => e($row['committee_code']),
                'name' => e(current_lang() === 'ar' ? ($row['name_ar'] ?: $row['name_en']) : $row['name_en']),
                'term' => e(current_lang() === 'ar' ? ($row['term_name_ar'] ?: $row['term_name_en']) : $row['term_name_en']),
                'members' => $row['member_count'],
                'status' => $row['is_active'] ? status_badge('active') : status_badge('expired'),
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar FROM board_terms WHERE status IN ('upcoming','active') ORDER BY start_date DESC");
        $boardMembers = $this->db()->fetchAll("SELECT id, full_name_en, full_name_ar FROM board_members WHERE status = 'active' ORDER BY full_name_en");

        $this->view('committees/form', [
            'title' => lang('board.create_committee'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.committees'), 'url' => '/board/committees'],
                ['label' => lang('common.create')]
            ],
            'committee' => null,
            'terms' => $terms,
            'boardMembers' => $boardMembers,
            'committeeMembers' => [],
            'formAction' => '/board/committees',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'committee_code' => 'required|string|max:20|unique:board_committees,committee_code',
            'name_en'        => 'required|string|max:200',
            'name_ar'        => 'required|string|max:200',
            'term_id'        => 'required|integer|exists:board_terms,id',
            'chairperson_board_member_id' => 'nullable|integer',
            'description_en' => 'nullable|string|max:5000',
            'description_ar' => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_active'] = 1;
        $id = $this->db()->insert('board_committees', $data);

        $this->audit('create', 'board_committees', $id, 'Created committee: ' . $data['name_en']);
        $this->json(['success' => true, 'message' => lang('board.committee_created'), 'redirect' => '/board/committees']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $committee = $this->db()->fetchOne("SELECT * FROM board_committees WHERE id = ?", [$id]);
        if (!$committee) { $this->abort(404); return; }

        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar FROM board_terms ORDER BY start_date DESC");
        $boardMembers = $this->db()->fetchAll("SELECT id, full_name_en, full_name_ar FROM board_members WHERE status = 'active' ORDER BY full_name_en");
        $committeeMembers = $this->db()->fetchAll(
            "SELECT bcm.*, bm.full_name_en, bm.full_name_ar
             FROM board_committee_members bcm
             JOIN board_members bm ON bcm.board_member_id = bm.id
             WHERE bcm.committee_id = ? AND bcm.is_active = 1
             ORDER BY bm.full_name_en",
            [$id]
        );

        $this->view('committees/form', [
            'title' => lang('board.edit_committee'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.committees'), 'url' => '/board/committees'],
                ['label' => lang('common.edit')]
            ],
            'committee' => $committee,
            'terms' => $terms,
            'boardMembers' => $boardMembers,
            'committeeMembers' => $committeeMembers,
            'formAction' => '/board/committees/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $old = $this->db()->fetchOne("SELECT * FROM board_committees WHERE id = ?", [$id]);
        if (!$old) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'committee_code' => 'required|string|max:20|unique:board_committees,committee_code,' . $id,
            'name_en'        => 'required|string|max:200',
            'name_ar'        => 'required|string|max:200',
            'term_id'        => 'required|integer|exists:board_terms,id',
            'chairperson_board_member_id' => 'nullable|integer',
            'description_en' => 'nullable|string|max:5000',
            'description_ar' => 'nullable|string|max:5000',
            'is_active'      => 'nullable|boolean',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_active'] = $request->post('is_active') ? 1 : 0;
        $this->db()->update('board_committees', $data, 'id = ?', [$id]);

        $this->audit('update', 'board_committees', $id, 'Updated committee: ' . $data['name_en'], $old, $data);
        $this->json(['success' => true, 'message' => lang('board.committee_updated'), 'redirect' => '/board/committees']);
    }

    public function addMember(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $boardMemberId = (int) $request->post('board_member_id');
        $role = $request->post('role_in_committee', '');

        $exists = $this->db()->fetchColumn(
            "SELECT COUNT(*) FROM board_committee_members WHERE committee_id = ? AND board_member_id = ? AND is_active = 1",
            [$id, $boardMemberId]
        );
        if ($exists > 0) {
            $this->json(['success' => false, 'message' => 'Member already in this committee'], 400);
            return;
        }

        $this->db()->insert('board_committee_members', [
            'committee_id' => $id,
            'board_member_id' => $boardMemberId,
            'role_in_committee' => $role,
            'joined_date' => date('Y-m-d'),
            'is_active' => 1,
        ]);

        $this->audit('create', 'board_committee_members', null, "Added member {$boardMemberId} to committee {$id}");
        $this->json(['success' => true, 'message' => lang('board.member_added_to_committee')]);
    }

    public function removeMember(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $boardMemberId = (int) $request->post('board_member_id');
        $this->db()->update('board_committee_members', [
            'is_active' => 0,
            'left_date' => date('Y-m-d'),
        ], 'committee_id = ? AND board_member_id = ?', [$id, $boardMemberId]);

        $this->audit('update', 'board_committee_members', null, "Removed member {$boardMemberId} from committee {$id}");
        $this->json(['success' => true, 'message' => lang('board.member_removed_from_committee')]);
    }
}
```

## File: modules\board\Controllers\MeetingController.php

```php
<?php
class MeetingController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('meetings/index', [
            'title' => lang('board.meetings'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.meetings')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (m.meeting_number LIKE ? OR m.title_en LIKE ? OR m.title_ar LIKE ?)";
            $params = array_merge($params, ["%{$search}%", "%{$search}%", "%{$search}%"]);
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_meetings");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_meetings m WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT m.*, bt.term_name_en FROM board_meetings m
             LEFT JOIN board_terms bt ON m.term_id = bt.id
             WHERE {$where} ORDER BY m.meeting_date DESC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<a href="/board/meetings/' . $row['id'] . '" class="btn btn-sm btn-outline-primary me-1"><i class="fas fa-eye"></i></a>';
            if (auth_can('board.manage')) {
                $actions .= '<a href="/board/meetings/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1"><i class="fas fa-edit"></i></a>';
            }

            $data[] = [
                'id' => $row['id'],
                'number' => e($row['meeting_number']),
                'title' => e(current_lang() === 'ar' ? ($row['title_ar'] ?: $row['title_en']) : $row['title_en']),
                'type' => status_badge($row['meeting_type']),
                'date' => format_date($row['meeting_date']),
                'status' => status_badge($row['status']),
                'quorum' => $row['quorum_met'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-danger"></i>',
                'attendees' => $row['attendees_count'],
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar FROM board_terms WHERE status IN ('upcoming','active') ORDER BY start_date DESC");
        $committees = $this->db()->fetchAll("SELECT id, name_en, name_ar FROM board_committees WHERE is_active = 1 ORDER BY name_en");

        $this->view('meetings/form', [
            'title' => lang('board.create_meeting'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.meetings'), 'url' => '/board/meetings'],
                ['label' => lang('common.create')]
            ],
            'meeting' => null,
            'terms' => $terms,
            'committees' => $committees,
            'agendaItems' => [],
            'formAction' => '/board/meetings',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'term_id'          => 'required|integer|exists:board_terms,id',
            'committee_id'     => 'nullable|integer',
            'meeting_type'     => 'required|in:regular,emergency,special,annual_general',
            'title_en'         => 'required|string|max:300',
            'title_ar'         => 'nullable|string|max:300',
            'meeting_date'     => 'required|date',
            'start_time'       => 'nullable|string|max:8',
            'end_time'         => 'nullable|string|max:8',
            'location'         => 'nullable|string|max:300',
            'quorum_required'  => 'required|integer|min:0',
            'notes'            => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $meetingNumber = SequenceGenerator::next('meeting_number');
        $data['meeting_number'] = $meetingNumber ?: 'MTG-' . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT);
        $data['status'] = 'scheduled';
        $data['created_by'] = auth_id();

        $id = $this->db()->insert('board_meetings', $data);

        // Insert agenda items
        $agendaTitles = $request->post('agenda_title', []);
        $agendaDescs = $request->post('agenda_description', []);
        if (is_array($agendaTitles)) {
            foreach ($agendaTitles as $idx => $title) {
                if (empty(trim($title))) continue;
                $this->db()->insert('board_meeting_agenda', [
                    'meeting_id' => $id,
                    'agenda_number' => $idx + 1,
                    'title_en' => $title,
                    'description' => $agendaDescs[$idx] ?? null,
                    'sort_order' => $idx + 1,
                    'status' => 'pending',
                ]);
            }
        }

        $this->audit('create', 'board_meetings', $id, 'Created meeting: ' . $data['meeting_number']);
        $this->json(['success' => true, 'message' => lang('board.meeting_created'), 'redirect' => '/board/meetings/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $meeting = $db->fetchOne(
            "SELECT m.*, bt.term_name_en, bt.term_name_ar, bc.name_en as committee_name
             FROM board_meetings m
             LEFT JOIN board_terms bt ON m.term_id = bt.id
             LEFT JOIN board_committees bc ON m.committee_id = bc.id
             WHERE m.id = ?", [$id]
        );
        if (!$meeting) { $this->abort(404); return; }

        $agendaItems = $db->fetchAll("SELECT * FROM board_meeting_agenda WHERE meeting_id = ? ORDER BY sort_order", [$id]);
        $attendees = $db->fetchAll(
            "SELECT bma.*, bm.full_name_en, bm.full_name_ar, bp.title_en as position_title
             FROM board_meeting_attendees bma
             JOIN board_members bm ON bma.board_member_id = bm.id
             LEFT JOIN board_positions bp ON bm.position_id = bp.id
             WHERE bma.meeting_id = ?
             ORDER BY bp.sort_order", [$id]
        );
        $boardMembers = $db->fetchAll(
            "SELECT bm.id, bm.full_name_en, bm.full_name_ar, bp.title_en as position_title
             FROM board_members bm
             LEFT JOIN board_positions bp ON bm.position_id = bp.id
             WHERE bm.term_id = ? AND bm.status = 'active'
             ORDER BY bp.sort_order", [$meeting['term_id']]
        );
        $resolutions = $db->fetchAll("SELECT * FROM board_resolutions WHERE meeting_id = ? ORDER BY resolution_number", [$id]);

        $this->view('meetings/show', [
            'title' => e($meeting['meeting_number']),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.meetings'), 'url' => '/board/meetings'],
                ['label' => e($meeting['meeting_number'])]
            ],
            'meeting' => $meeting,
            'agendaItems' => $agendaItems,
            'attendees' => $attendees,
            'boardMembers' => $boardMembers,
            'resolutions' => $resolutions,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $meeting = $this->db()->fetchOne("SELECT * FROM board_meetings WHERE id = ?", [$id]);
        if (!$meeting) { $this->abort(404); return; }

        $terms = $this->db()->fetchAll("SELECT id, term_name_en, term_name_ar FROM board_terms ORDER BY start_date DESC");
        $committees = $this->db()->fetchAll("SELECT id, name_en, name_ar FROM board_committees WHERE is_active = 1");
        $agendaItems = $this->db()->fetchAll("SELECT * FROM board_meeting_agenda WHERE meeting_id = ? ORDER BY sort_order", [$id]);

        $this->view('meetings/form', [
            'title' => lang('board.edit_meeting'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.meetings'), 'url' => '/board/meetings'],
                ['label' => lang('common.edit')]
            ],
            'meeting' => $meeting,
            'terms' => $terms,
            'committees' => $committees,
            'agendaItems' => $agendaItems,
            'formAction' => '/board/meetings/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $old = $this->db()->fetchOne("SELECT * FROM board_meetings WHERE id = ?", [$id]);
        if (!$old) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'term_id'          => 'required|integer',
            'committee_id'     => 'nullable|integer',
            'meeting_type'     => 'required|in:regular,emergency,special,annual_general',
            'title_en'         => 'required|string|max:300',
            'title_ar'         => 'nullable|string|max:300',
            'meeting_date'     => 'required|date',
            'start_time'       => 'nullable|string|max:8',
            'end_time'         => 'nullable|string|max:8',
            'location'         => 'nullable|string|max:300',
            'quorum_required'  => 'required|integer|min:0',
            'minutes_text'     => 'nullable|string',
            'notes'            => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $this->db()->update('board_meetings', $data, 'id = ?', [$id]);

        // Re-save agenda
        $this->db()->delete('board_meeting_agenda', 'meeting_id = ?', [$id]);
        $agendaTitles = $request->post('agenda_title', []);
        $agendaDescs = $request->post('agenda_description', []);
        if (is_array($agendaTitles)) {
            foreach ($agendaTitles as $idx => $title) {
                if (empty(trim($title))) continue;
                $this->db()->insert('board_meeting_agenda', [
                    'meeting_id' => $id,
                    'agenda_number' => $idx + 1,
                    'title_en' => $title,
                    'description' => $agendaDescs[$idx] ?? null,
                    'sort_order' => $idx + 1,
                    'status' => 'pending',
                ]);
            }
        }

        $this->audit('update', 'board_meetings', $id, 'Updated meeting: ' . $old['meeting_number'], $old, $data);
        $this->json(['success' => true, 'message' => lang('board.meeting_updated'), 'redirect' => '/board/meetings/' . $id]);
    }

    public function saveAttendance(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $meeting = $this->db()->fetchOne("SELECT * FROM board_meetings WHERE id = ?", [$id]);
        if (!$meeting) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $attendanceData = $request->post('attendance', []);
        $this->db()->delete('board_meeting_attendees', 'meeting_id = ?', [$id]);

        $presentCount = 0;
        foreach ($attendanceData as $bmId => $status) {
            if (empty($status)) continue;
            $this->db()->insert('board_meeting_attendees', [
                'meeting_id' => $id,
                'board_member_id' => (int) $bmId,
                'attendance' => $status,
            ]);
            if (in_array($status, ['present', 'proxy'])) {
                $presentCount++;
            }
        }

        $quorumMet = $presentCount >= $meeting['quorum_required'] ? 1 : 0;
        $this->db()->update('board_meetings', [
            'attendees_count' => $presentCount,
            'quorum_met' => $quorumMet,
        ], 'id = ?', [$id]);

        $this->audit('update', 'board_meetings', $id, "Recorded attendance: {$presentCount} present, quorum " . ($quorumMet ? 'met' : 'NOT met'));
        $this->json(['success' => true, 'message' => lang('board.attendance_saved')]);
    }

    public function approveMinutes(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $this->db()->update('board_meetings', [
            'minutes_approved' => 1,
            'minutes_approved_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('approve', 'board_meetings', $id, 'Approved meeting minutes');
        $this->json(['success' => true, 'message' => lang('board.minutes_approved')]);
    }

    public function changeStatus(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $newStatus = $request->post('status', '');
        $valid = ['scheduled', 'in_progress', 'completed', 'cancelled', 'postponed'];
        if (!in_array($newStatus, $valid)) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 400);
            return;
        }

        $this->db()->update('board_meetings', ['status' => $newStatus], 'id = ?', [$id]);
        $this->audit('update', 'board_meetings', $id, "Changed meeting status to: {$newStatus}");
        $this->json(['success' => true, 'message' => lang('board.status_updated')]);
    }
}
```

## File: modules\board\Controllers\ResolutionController.php

```php
<?php
class ResolutionController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('resolutions/index', [
            'title' => lang('board.resolutions'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.resolutions')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (r.resolution_number LIKE ? OR r.title_en LIKE ? OR r.title_ar LIKE ?)";
            $params = array_merge($params, ["%{$search}%", "%{$search}%", "%{$search}%"]);
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_resolutions");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM board_resolutions r WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT r.*, bm.meeting_number
             FROM board_resolutions r
             LEFT JOIN board_meetings bm ON r.meeting_id = bm.id
             WHERE {$where} ORDER BY r.resolution_date DESC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $votesSummary = "{$row['votes_for']}F / {$row['votes_against']}A / {$row['votes_abstain']}Ab";
            $actions = '<a href="/board/resolutions/' . $row['id'] . '" class="btn btn-sm btn-outline-primary me-1"><i class="fas fa-eye"></i></a>';
            if (auth_can('board.resolutions')) {
                $actions .= '<a href="/board/resolutions/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1"><i class="fas fa-edit"></i></a>';
            }

            $data[] = [
                'id' => $row['id'],
                'number' => e($row['resolution_number']),
                'date' => format_date($row['resolution_date']),
                'title' => e(str_limit(current_lang() === 'ar' ? ($row['title_ar'] ?: $row['title_en']) : $row['title_en'], 60)),
                'meeting' => e($row['meeting_number'] ?? '-'),
                'status' => status_badge($row['status']),
                'votes' => $votesSummary,
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $terms = $this->db()->fetchAll("SELECT id, term_name_en FROM board_terms WHERE status IN ('upcoming','active') ORDER BY start_date DESC");
        $meetings = $this->db()->fetchAll("SELECT id, meeting_number, title_en FROM board_meetings WHERE status IN ('scheduled','in_progress','completed') ORDER BY meeting_date DESC LIMIT 50");

        $this->view('resolutions/form', [
            'title' => lang('board.create_resolution'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.resolutions'), 'url' => '/board/resolutions'],
                ['label' => lang('common.create')]
            ],
            'resolution' => null,
            'terms' => $terms,
            'meetings' => $meetings,
            'formAction' => '/board/resolutions',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'term_id'                => 'required|integer|exists:board_terms,id',
            'meeting_id'             => 'nullable|integer',
            'resolution_date'        => 'required|date',
            'title_en'               => 'required|string|max:500',
            'title_ar'               => 'nullable|string|max:500',
            'resolution_text'        => 'required|string',
            'resolution_text_ar'     => 'nullable|string',
            'category'               => 'nullable|string|max:50',
            'requires_implementation'=> 'nullable|boolean',
            'implementation_deadline'=> 'nullable|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $resNum = SequenceGenerator::next('resolution_number');
        $data['resolution_number'] = $resNum ?: 'RES-' . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT);
        $data['status'] = 'draft';
        $data['requires_implementation'] = $request->post('requires_implementation') ? 1 : 0;
        $data['created_by'] = auth_id();

        $id = $this->db()->insert('board_resolutions', $data);

        $this->audit('create', 'board_resolutions', $id, 'Created resolution: ' . $data['resolution_number']);
        $this->json(['success' => true, 'message' => lang('board.resolution_created'), 'redirect' => '/board/resolutions/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $resolution = $db->fetchOne(
            "SELECT r.*, bm.meeting_number, bt.term_name_en
             FROM board_resolutions r
             LEFT JOIN board_meetings bm ON r.meeting_id = bm.id
             LEFT JOIN board_terms bt ON r.term_id = bt.id
             WHERE r.id = ?", [$id]
        );
        if (!$resolution) { $this->abort(404); return; }

        $votes = $db->fetchAll(
            "SELECT brv.*, bm.full_name_en, bm.full_name_ar, bp.title_en as position_title
             FROM board_resolution_votes brv
             JOIN board_members bm ON brv.board_member_id = bm.id
             LEFT JOIN board_positions bp ON bm.position_id = bp.id
             WHERE brv.resolution_id = ?
             ORDER BY bp.sort_order", [$id]
        );

        $boardMembers = $db->fetchAll(
            "SELECT bm.id, bm.full_name_en, bm.full_name_ar, bp.title_en as position_title
             FROM board_members bm
             LEFT JOIN board_positions bp ON bm.position_id = bp.id
             WHERE bm.term_id = ? AND bm.status = 'active'
             ORDER BY bp.sort_order", [$resolution['term_id']]
        );

        $supersededBy = null;
        if ($resolution['superseded_by_id']) {
            $supersededBy = $db->fetchOne("SELECT id, resolution_number, title_en FROM board_resolutions WHERE id = ?", [$resolution['superseded_by_id']]);
        }

        $this->view('resolutions/show', [
            'title' => e($resolution['resolution_number']),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.resolutions'), 'url' => '/board/resolutions'],
                ['label' => e($resolution['resolution_number'])]
            ],
            'resolution' => $resolution,
            'votes' => $votes,
            'boardMembers' => $boardMembers,
            'supersededBy' => $supersededBy,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $resolution = $this->db()->fetchOne("SELECT * FROM board_resolutions WHERE id = ?", [$id]);
        if (!$resolution) { $this->abort(404); return; }

        $terms = $this->db()->fetchAll("SELECT id, term_name_en FROM board_terms ORDER BY start_date DESC");
        $meetings = $this->db()->fetchAll("SELECT id, meeting_number, title_en FROM board_meetings ORDER BY meeting_date DESC LIMIT 50");

        $this->view('resolutions/form', [
            'title' => lang('board.edit_resolution'),
            'breadcrumbs' => [
                ['label' => lang('board.module'), 'url' => '/board/terms'],
                ['label' => lang('board.resolutions'), 'url' => '/board/resolutions'],
                ['label' => lang('common.edit')]
            ],
            'resolution' => $resolution,
            'terms' => $terms,
            'meetings' => $meetings,
            'formAction' => '/board/resolutions/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $old = $this->db()->fetchOne("SELECT * FROM board_resolutions WHERE id = ?", [$id]);
        if (!$old) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'term_id'                => 'required|integer',
            'meeting_id'             => 'nullable|integer',
            'resolution_date'        => 'required|date',
            'title_en'               => 'required|string|max:500',
            'title_ar'               => 'nullable|string|max:500',
            'resolution_text'        => 'required|string',
            'resolution_text_ar'     => 'nullable|string',
            'category'               => 'nullable|string|max:50',
            'status'                 => 'required|in:draft,approved,rejected,implemented,cancelled,superseded',
            'requires_implementation'=> 'nullable|boolean',
            'implementation_deadline'=> 'nullable|date',
            'implementation_notes'   => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['requires_implementation'] = $request->post('requires_implementation') ? 1 : 0;
        $this->db()->update('board_resolutions', $data, 'id = ?', [$id]);

        $this->audit('update', 'board_resolutions', $id, 'Updated resolution: ' . $old['resolution_number'], $old, $data);
        $this->json(['success' => true, 'message' => lang('board.resolution_updated'), 'redirect' => '/board/resolutions/' . $id]);
    }

    public function recordVote(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $resolution = $this->db()->fetchOne("SELECT * FROM board_resolutions WHERE id = ?", [$id]);
        if (!$resolution) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $votes = $request->post('votes', []);
        $comments = $request->post('vote_comments', []);

        $this->db()->delete('board_resolution_votes', 'resolution_id = ?', [$id]);

        $forCount = 0; $againstCount = 0; $abstainCount = 0;
        foreach ($votes as $bmId => $vote) {
            if (!in_array($vote, ['for', 'against', 'abstain'])) continue;

            $this->db()->insert('board_resolution_votes', [
                'resolution_id' => $id,
                'board_member_id' => (int) $bmId,
                'vote' => $vote,
                'comments' => $comments[$bmId] ?? null,
            ]);

            if ($vote === 'for') $forCount++;
            elseif ($vote === 'against') $againstCount++;
            else $abstainCount++;
        }

        $newStatus = $forCount > $againstCount ? 'approved' : ($againstCount > $forCount ? 'rejected' : $resolution['status']);

        $this->db()->update('board_resolutions', [
            'votes_for' => $forCount,
            'votes_against' => $againstCount,
            'votes_abstain' => $abstainCount,
            'status' => $newStatus,
        ], 'id = ?', [$id]);

        $this->audit('update', 'board_resolutions', $id, "Recorded votes: {$forCount}F/{$againstCount}A/{$abstainCount}Ab â†’ {$newStatus}");
        $this->json(['success' => true, 'message' => lang('board.votes_recorded')]);
    }

    public function markImplemented(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $notes = $request->post('implementation_notes', '');
        $this->db()->update('board_resolutions', [
            'status' => 'implemented',
            'implemented_at' => date('Y-m-d H:i:s'),
            'implemented_by' => auth_id(),
            'implementation_notes' => $notes,
        ], 'id = ?', [$id]);

        $this->audit('update', 'board_resolutions', $id, 'Marked resolution as implemented');
        $this->json(['success' => true, 'message' => lang('board.resolution_implemented')]);
    }
}
```

## File: modules\board\Controllers\SpecialOfferController.php

```php
<?php
class SpecialOfferController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('offers/index', [
            'title' => lang('board.special_offers'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.special_offers')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (offer_code LIKE ? OR name_en LIKE ? OR name_ar LIKE ?)";
            $params = ["%{$search}%", "%{$search}%", "%{$search}%"];
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM special_offers");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM special_offers WHERE {$where}", $params);

        $rows = $db->fetchAll("SELECT * FROM special_offers WHERE {$where} ORDER BY created_at DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = [];
        foreach ($rows as $row) {
            $actions = '<a href="/offers/' . $row['id'] . '" class="btn btn-sm btn-outline-primary me-1"><i class="fas fa-eye"></i></a>';
            if (auth_can('offers.manage')) {
                $actions .= '<a href="/offers/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1"><i class="fas fa-edit"></i></a>';
            }

            $data[] = [
                'id' => $row['id'],
                'code' => e($row['offer_code']),
                'name' => e(current_lang() === 'ar' ? ($row['name_ar'] ?: $row['name_en']) : $row['name_en']),
                'type' => status_badge($row['offer_type']),
                'dates' => format_date($row['start_date']) . ' â†’ ' . format_date($row['end_date']),
                'uses' => $row['current_uses'] . ($row['max_uses'] ? '/' . $row['max_uses'] : ''),
                'status' => status_badge($row['status']),
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $membershipTypes = $this->db()->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('offers/form', [
            'title' => lang('board.create_offer'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.special_offers'), 'url' => '/offers'],
                ['label' => lang('common.create')]
            ],
            'offer' => null,
            'membershipTypes' => $membershipTypes,
            'selectedTypes' => [],
            'formAction' => '/offers',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $validator = new Validator($request->all(), [
            'offer_code'      => 'required|string|max:30|unique:special_offers,offer_code',
            'name_en'         => 'required|string|max:300',
            'name_ar'         => 'required|string|max:300',
            'description_en'  => 'nullable|string|max:5000',
            'description_ar'  => 'nullable|string|max:5000',
            'offer_type'      => 'required|in:discount,fee_waiver,payment_plan,upgrade,bundle,custom',
            'discount_method' => 'nullable|in:percentage,fixed_amount',
            'discount_value'  => 'nullable|decimal|min:0',
            'start_date'      => 'required|date',
            'end_date'        => 'required|date',
            'max_uses'        => 'nullable|integer|min:0',
            'requires_approval'=> 'nullable|boolean',
            'terms_en'        => 'nullable|string',
            'terms_ar'        => 'nullable|string',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['requires_approval'] = $request->post('requires_approval') ? 1 : 0;
        $data['status'] = 'draft';
        $data['created_by'] = auth_id();

        $id = $this->db()->insert('special_offers', $data);

        // Link membership types
        $mtIds = $request->post('membership_type_ids', []);
        if (is_array($mtIds)) {
            foreach ($mtIds as $mtId) {
                $this->db()->insert('special_offer_membership_types', [
                    'offer_id' => $id,
                    'membership_type_id' => (int) $mtId,
                ]);
            }
        }

        $this->audit('create', 'special_offers', $id, 'Created offer: ' . $data['offer_code']);
        $this->json(['success' => true, 'message' => lang('board.offer_created'), 'redirect' => '/offers/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $offer = $this->db()->fetchOne("SELECT * FROM special_offers WHERE id = ?", [$id]);
        if (!$offer) { $this->abort(404); return; }

        $membershipTypes = $this->db()->fetchAll(
            "SELECT mt.* FROM special_offer_membership_types somt
             JOIN membership_types mt ON somt.membership_type_id = mt.id
             WHERE somt.offer_id = ?", [$id]
        );

        $conditions = $this->db()->fetchAll("SELECT * FROM special_offer_conditions WHERE offer_id = ?", [$id]);
        $applications = $this->db()->fetchAll(
            "SELECT mao.*, m.full_name_en, m.membership_number
             FROM member_applied_offers mao
             JOIN members m ON mao.member_id = m.id
             WHERE mao.offer_id = ?
             ORDER BY mao.applied_date DESC LIMIT 50", [$id]
        );

        $this->view('offers/show', [
            'title' => e($offer['offer_code']),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.special_offers'), 'url' => '/offers'],
                ['label' => e($offer['offer_code'])]
            ],
            'offer' => $offer,
            'membershipTypes' => $membershipTypes,
            'conditions' => $conditions,
            'applications' => $applications,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $offer = $this->db()->fetchOne("SELECT * FROM special_offers WHERE id = ?", [$id]);
        if (!$offer) { $this->abort(404); return; }

        $membershipTypes = $this->db()->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");
        $selectedTypes = array_column(
            $this->db()->fetchAll("SELECT membership_type_id FROM special_offer_membership_types WHERE offer_id = ?", [$id]),
            'membership_type_id'
        );

        $this->view('offers/form', [
            'title' => lang('board.edit_offer'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.special_offers'), 'url' => '/offers'],
                ['label' => lang('common.edit')]
            ],
            'offer' => $offer,
            'membershipTypes' => $membershipTypes,
            'selectedTypes' => $selectedTypes,
            'formAction' => '/offers/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $old = $this->db()->fetchOne("SELECT * FROM special_offers WHERE id = ?", [$id]);
        if (!$old) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'offer_code'      => 'required|string|max:30|unique:special_offers,offer_code,' . $id,
            'name_en'         => 'required|string|max:300',
            'name_ar'         => 'required|string|max:300',
            'description_en'  => 'nullable|string|max:5000',
            'description_ar'  => 'nullable|string|max:5000',
            'offer_type'      => 'required|in:discount,fee_waiver,payment_plan,upgrade,bundle,custom',
            'discount_method' => 'nullable|in:percentage,fixed_amount',
            'discount_value'  => 'nullable|decimal|min:0',
            'start_date'      => 'required|date',
            'end_date'        => 'required|date',
            'max_uses'        => 'nullable|integer|min:0',
            'status'          => 'required|in:draft,active,paused,expired,cancelled',
            'requires_approval'=> 'nullable|boolean',
            'terms_en'        => 'nullable|string',
            'terms_ar'        => 'nullable|string',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['requires_approval'] = $request->post('requires_approval') ? 1 : 0;
        $this->db()->update('special_offers', $data, 'id = ?', [$id]);

        // Re-link membership types
        $this->db()->delete('special_offer_membership_types', 'offer_id = ?', [$id]);
        $mtIds = $request->post('membership_type_ids', []);
        if (is_array($mtIds)) {
            foreach ($mtIds as $mtId) {
                $this->db()->insert('special_offer_membership_types', ['offer_id' => $id, 'membership_type_id' => (int) $mtId]);
            }
        }

        $this->audit('update', 'special_offers', $id, 'Updated offer: ' . $data['offer_code'], $old, $data);
        $this->json(['success' => true, 'message' => lang('board.offer_updated'), 'redirect' => '/offers/' . $id]);
    }

    public function applyToMember(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $offer = $this->db()->fetchOne("SELECT * FROM special_offers WHERE id = ? AND status = 'active'", [$id]);
        if (!$offer) { $this->json(['success' => false, 'message' => 'Offer not found or not active'], 400); return; }

        if ($offer['max_uses'] && $offer['current_uses'] >= $offer['max_uses']) {
            $this->json(['success' => false, 'message' => 'Offer has reached maximum uses'], 400);
            return;
        }

        $memberId = (int) $request->post('member_id');
        $discountAmount = (float) $request->post('discount_amount', 0);
        $invoiceId = $request->post('invoice_id') ? (int) $request->post('invoice_id') : null;

        $this->db()->insert('member_applied_offers', [
            'member_id' => $memberId,
            'offer_id' => $id,
            'invoice_id' => $invoiceId,
            'applied_date' => date('Y-m-d'),
            'discount_amount' => $discountAmount,
            'approved_by' => auth_id(),
            'notes' => $request->post('notes', ''),
        ]);

        $this->db()->update('special_offers', [
            'current_uses' => $offer['current_uses'] + 1,
        ], 'id = ?', [$id]);

        $this->audit('execute', 'member_applied_offers', null, "Applied offer {$offer['offer_code']} to member {$memberId}");
        $this->json(['success' => true, 'message' => lang('board.offer_applied')]);
    }
}
```

## File: modules\board\Controllers\TransferController.php

```php
<?php
class TransferController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('transfers/index', [
            'title' => lang('board.transfers'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.transfers')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status', '');

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (mt.transfer_number LIKE ? OR mf.full_name_en LIKE ? OR mt.to_applicant_name LIKE ?)";
            $params = array_merge($params, ["%{$search}%", "%{$search}%", "%{$search}%"]);
        }
        if ($statusFilter) {
            $where .= " AND mt.status = ?";
            $params[] = $statusFilter;
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM membership_transfers");
        $filtered = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM membership_transfers mt LEFT JOIN members mf ON mt.from_member_id = mf.id WHERE {$where}", $params
        );

        $rows = $db->fetchAll(
            "SELECT mt.*, mf.full_name_en as from_name, mf.membership_number as from_number,
                    mto.full_name_en as to_name, mto.membership_number as to_number,
                    tt.name_en as type_name
             FROM membership_transfers mt
             LEFT JOIN members mf ON mt.from_member_id = mf.id
             LEFT JOIN members mto ON mt.to_member_id = mto.id
             LEFT JOIN transfer_types tt ON mt.transfer_type_id = tt.id
             WHERE {$where}
             ORDER BY mt.created_at DESC
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $to = $row['to_name'] ?: e($row['to_applicant_name'] ?: '-');
            $actions = '<a href="/transfers/' . $row['id'] . '" class="btn btn-sm btn-outline-primary me-1"><i class="fas fa-eye"></i></a>';

            $data[] = [
                'id' => $row['id'],
                'number' => e($row['transfer_number']),
                'from' => e($row['from_name'] ?? '') . ' <small class="text-muted">(' . e($row['from_number'] ?? '') . ')</small>',
                'to' => $to,
                'type' => e($row['type_name'] ?? ''),
                'reason' => status_badge($row['reason']),
                'status' => status_badge($row['status']),
                'date' => format_date($row['request_date']),
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $transferTypes = $this->db()->fetchAll("SELECT * FROM transfer_types WHERE is_active = 1 ORDER BY name_en");
        $membershipTypes = $this->db()->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('transfers/create', [
            'title' => lang('board.create_transfer'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.transfers'), 'url' => '/transfers'],
                ['label' => lang('common.create')]
            ],
            'transferTypes' => $transferTypes,
            'membershipTypes' => $membershipTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'transfer_type_id'       => 'required|integer|exists:transfer_types,id',
            'from_member_id'         => 'required|integer|exists:members,id',
            'to_applicant_name'      => 'required|string|max:300',
            'to_applicant_national_id'=> 'nullable|string|max:20',
            'to_applicant_phone'     => 'nullable|string|max:20',
            'relationship_to_original'=> 'nullable|string|max:100',
            'reason'                 => 'required|in:inheritance,divorce,gift,sale,court_order,board_decision,other',
            'reason_details'         => 'nullable|string|max:5000',
            'membership_type_id'     => 'required|integer|exists:membership_types,id',
            'new_membership_type_id' => 'nullable|integer',
            'notes'                  => 'nullable|string|max:5000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $trNum = SequenceGenerator::next('transfer_number');
        $data['transfer_number'] = $trNum ?: 'TRN-' . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT);
        $data['request_date'] = date('Y-m-d');
        $data['status'] = 'requested';
        $data['created_by'] = auth_id();

        // Calculate fees from transfer type
        $type = $this->db()->fetchOne("SELECT * FROM transfer_types WHERE id = ?", [$data['transfer_type_id']]);
        $data['transfer_fee'] = $type['fee_amount'] ?? 0;
        $data['total_fees'] = $data['transfer_fee'];

        $id = $this->db()->insert('membership_transfers', $data);

        // Log status history
        $this->db()->insert('transfer_status_history', [
            'transfer_id' => $id,
            'to_status' => 'requested',
            'changed_by' => auth_id(),
            'notes' => 'Transfer request created',
        ]);

        $this->audit('create', 'membership_transfers', $id, 'Created transfer: ' . $data['transfer_number']);
        $this->json(['success' => true, 'message' => lang('board.transfer_created'), 'redirect' => '/transfers/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $transfer = $db->fetchOne(
            "SELECT mt.*, tt.name_en as type_name, tt.name_ar as type_name_ar, tt.requires_board_approval,
                    mf.full_name_en as from_name, mf.membership_number as from_number,
                    mto.full_name_en as to_name, mto.membership_number as to_number,
                    mtype.name_en as membership_type_name
             FROM membership_transfers mt
             LEFT JOIN transfer_types tt ON mt.transfer_type_id = tt.id
             LEFT JOIN members mf ON mt.from_member_id = mf.id
             LEFT JOIN members mto ON mt.to_member_id = mto.id
             LEFT JOIN membership_types mtype ON mt.membership_type_id = mtype.id
             WHERE mt.id = ?", [$id]
        );
        if (!$transfer) { $this->abort(404); return; }

        $documents = $db->fetchAll("SELECT * FROM transfer_documents WHERE transfer_id = ? ORDER BY uploaded_at DESC", [$id]);
        $heirs = $db->fetchAll("SELECT * FROM transfer_heirs WHERE transfer_id = ? ORDER BY is_primary_heir DESC, heir_name", [$id]);
        $history = $db->fetchAll(
            "SELECT tsh.*, au.full_name_en as changed_by_name
             FROM transfer_status_history tsh
             LEFT JOIN auth_users au ON tsh.changed_by = au.id
             WHERE tsh.transfer_id = ?
             ORDER BY tsh.changed_at DESC", [$id]
        );

        $this->view('transfers/show', [
            'title' => e($transfer['transfer_number']),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.transfers'), 'url' => '/transfers'],
                ['label' => e($transfer['transfer_number'])]
            ],
            'transfer' => $transfer,
            'documents' => $documents,
            'heirs' => $heirs,
            'history' => $history,
        ]);
    }

    public function changeStatus(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $transfer = $this->db()->fetchOne("SELECT * FROM membership_transfers WHERE id = ?", [$id]);
        if (!$transfer) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $newStatus = $request->post('status', '');
        $reason = $request->post('reason', '');
        $validStatuses = ['requested','documents_pending','under_review','board_pending','approved','rejected','completed','cancelled'];

        if (!in_array($newStatus, $validStatuses)) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 400);
            return;
        }

        $this->db()->update('membership_transfers', [
            'status' => $newStatus,
            'status_changed_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->db()->insert('transfer_status_history', [
            'transfer_id' => $id,
            'from_status' => $transfer['status'],
            'to_status' => $newStatus,
            'reason' => $reason,
            'changed_by' => auth_id(),
        ]);

        $this->audit('update', 'membership_transfers', $id, "Transfer status: {$transfer['status']} â†’ {$newStatus}");
        $this->json(['success' => true, 'message' => lang('board.status_updated')]);
    }

    public function uploadDocument(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $file = $request->file('document');
        $upload = new FileUpload($file);
        $upload->setAllowedTypes(['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']);
        $upload->setDestination('uploads/transfers/' . $id);

        if (!$upload->validate()) {
            $this->json(['success' => false, 'message' => implode(', ', $upload->errors())], 422);
            return;
        }

        $path = $upload->save();
        if (!$path) {
            $this->json(['success' => false, 'message' => 'Upload failed'], 500);
            return;
        }

        $this->db()->insert('transfer_documents', [
            'transfer_id' => $id,
            'document_type' => $request->post('document_type', 'general'),
            'document_name' => $request->post('document_name', $upload->originalName()),
            'file_path' => $path,
            'file_size_bytes' => $upload->size(),
            'mime_type' => $upload->mimeType(),
            'uploaded_by' => auth_id(),
        ]);

        $this->audit('create', 'transfer_documents', null, "Uploaded document for transfer {$id}");
        $this->json(['success' => true, 'message' => lang('board.document_uploaded')]);
    }

    public function addHeir(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'heir_name'       => 'required|string|max:300',
            'heir_national_id'=> 'nullable|string|max:20',
            'heir_phone'      => 'nullable|string|max:20',
            'relationship'    => 'required|string|max:100',
            'share_percentage'=> 'required|decimal|min:0|max:100',
            'is_primary_heir' => 'nullable|boolean',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['transfer_id'] = $id;
        $data['is_primary_heir'] = $request->post('is_primary_heir') ? 1 : 0;
        $data['consent_given'] = 0;

        $this->db()->insert('transfer_heirs', $data);
        $this->audit('create', 'transfer_heirs', null, "Added heir for transfer {$id}");
        $this->json(['success' => true, 'message' => lang('board.heir_added')]);
    }

    public function removeHeir(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $heirId = (int) $request->post('heir_id');
        $this->db()->delete('transfer_heirs', 'id = ? AND transfer_id = ?', [$heirId, $id]);
        $this->audit('delete', 'transfer_heirs', $heirId, "Removed heir from transfer {$id}");
        $this->json(['success' => true, 'message' => lang('board.heir_removed')]);
    }

    public function approve(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $transfer = $this->db()->fetchOne("SELECT * FROM membership_transfers WHERE id = ?", [$id]);
        if (!$transfer) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $this->db()->update('membership_transfers', [
            'status' => 'approved',
            'status_changed_at' => date('Y-m-d H:i:s'),
            'approved_by' => auth_id(),
            'approved_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->db()->insert('transfer_status_history', [
            'transfer_id' => $id,
            'from_status' => $transfer['status'],
            'to_status' => 'approved',
            'changed_by' => auth_id(),
            'notes' => $request->post('notes', 'Approved'),
        ]);

        $this->audit('approve', 'membership_transfers', $id, 'Approved transfer: ' . $transfer['transfer_number']);
        $this->json(['success' => true, 'message' => lang('board.transfer_approved')]);
    }

    public function complete(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid token'], 403);
            return;
        }

        $transfer = $this->db()->fetchOne("SELECT * FROM membership_transfers WHERE id = ? AND status = 'approved'", [$id]);
        if (!$transfer) {
            $this->json(['success' => false, 'message' => 'Transfer not found or not approved'], 400);
            return;
        }

        $db = $this->db();
        $db->beginTransaction();

        try {
            // Terminate original member
            $db->execute("CALL sp_terminate_member(?, ?, ?, @result)", [
                $transfer['from_member_id'],
                'Membership transferred: ' . $transfer['transfer_number'],
                auth_id(),
            ]);

            // Update transfer record
            $db->update('membership_transfers', [
                'status' => 'completed',
                'transfer_date' => date('Y-m-d'),
                'status_changed_at' => date('Y-m-d H:i:s'),
                'completed_by' => auth_id(),
                'completed_at' => date('Y-m-d H:i:s'),
            ], 'id = ?', [$id]);

            $db->insert('transfer_status_history', [
                'transfer_id' => $id,
                'from_status' => 'approved',
                'to_status' => 'completed',
                'changed_by' => auth_id(),
                'notes' => 'Transfer completed. Original member terminated.',
            ]);

            $db->commit();
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Transfer completion failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Transfer completion failed: ' . $e->getMessage()], 500);
            return;
        }

        $this->audit('execute', 'membership_transfers', $id, 'Completed transfer: ' . $transfer['transfer_number']);
        $this->json(['success' => true, 'message' => lang('board.transfer_completed'), 'redirect' => '/transfers/' . $id]);
    }
}
```

## File: modules\board\Controllers\TransferTypeController.php

```php
<?php
class TransferTypeController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('transfer-types/index', [
            'title' => lang('board.transfer_types'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.transfer_types')]
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (type_code LIKE ? OR name_en LIKE ? OR name_ar LIKE ?)";
            $params = ["%{$search}%", "%{$search}%", "%{$search}%"];
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM transfer_types");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM transfer_types WHERE {$where}", $params);

        $rows = $db->fetchAll("SELECT * FROM transfer_types WHERE {$where} ORDER BY type_code LIMIT {$length} OFFSET {$start}", $params);

        $data = [];
        foreach ($rows as $row) {
            $actions = '<a href="/transfer-types/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning me-1"><i class="fas fa-edit"></i></a>';
            $data[] = [
                'id' => $row['id'],
                'code' => e($row['type_code']),
                'name' => e(current_lang() === 'ar' ? ($row['name_ar'] ?: $row['name_en']) : $row['name_en']),
                'fee_type' => e($row['fee_type']),
                'fee_amount' => money($row['fee_amount']),
                'board_approval' => $row['requires_board_approval'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-muted"></i>',
                'status' => $row['is_active'] ? status_badge('active') : status_badge('expired'),
                'actions' => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function create(Request $request): void
    {
        $this->view('transfer-types/form', [
            'title' => lang('board.create_transfer_type'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.transfer_types'), 'url' => '/transfer-types'],
                ['label' => lang('common.create')]
            ],
            'type' => null,
            'formAction' => '/transfer-types',
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $validator = new Validator($request->all(), [
            'type_code'              => 'required|string|max:20|unique:transfer_types,type_code',
            'name_en'                => 'required|string|max:200',
            'name_ar'                => 'required|string|max:200',
            'description_en'         => 'nullable|string|max:5000',
            'description_ar'         => 'nullable|string|max:5000',
            'requires_board_approval'=> 'nullable|boolean',
            'fee_type'               => 'required|in:none,fixed,percentage_of_subscription',
            'fee_amount'             => 'nullable|decimal|min:0',
            'fee_percentage'         => 'nullable|decimal|min:0|max:100',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['requires_board_approval'] = $request->post('requires_board_approval') ? 1 : 0;
        $data['is_active'] = 1;
        $id = $this->db()->insert('transfer_types', $data);

        $this->audit('create', 'transfer_types', $id, 'Created transfer type: ' . $data['type_code']);
        $this->json(['success' => true, 'message' => lang('board.type_created'), 'redirect' => '/transfer-types']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $type = $this->db()->fetchOne("SELECT * FROM transfer_types WHERE id = ?", [$id]);
        if (!$type) { $this->abort(404); return; }

        $this->view('transfer-types/form', [
            'title' => lang('board.edit_transfer_type'),
            'breadcrumbs' => [
                ['label' => lang('board.transfers_module'), 'url' => '/transfers'],
                ['label' => lang('board.transfer_types'), 'url' => '/transfer-types'],
                ['label' => lang('common.edit')]
            ],
            'type' => $type,
            'formAction' => '/transfer-types/' . $id,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $old = $this->db()->fetchOne("SELECT * FROM transfer_types WHERE id = ?", [$id]);
        if (!$old) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }

        $validator = new Validator($request->all(), [
            'type_code'              => 'required|string|max:20|unique:transfer_types,type_code,' . $id,
            'name_en'                => 'required|string|max:200',
            'name_ar'                => 'required|string|max:200',
            'description_en'         => 'nullable|string|max:5000',
            'description_ar'         => 'nullable|string|max:5000',
            'requires_board_approval'=> 'nullable|boolean',
            'fee_type'               => 'required|in:none,fixed,percentage_of_subscription',
            'fee_amount'             => 'nullable|decimal|min:0',
            'fee_percentage'         => 'nullable|decimal|min:0|max:100',
            'is_active'              => 'nullable|boolean',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['requires_board_approval'] = $request->post('requires_board_approval') ? 1 : 0;
        $data['is_active'] = $request->post('is_active') ? 1 : 0;
        $this->db()->update('transfer_types', $data, 'id = ?', [$id]);

        $this->audit('update', 'transfer_types', $id, 'Updated transfer type: ' . $data['type_code'], $old, $data);
        $this->json(['success' => true, 'message' => lang('board.type_updated'), 'redirect' => '/transfer-types']);
    }
}
```

## File: modules\board\module.json

```json
{
    "name": "board",
    "label_en": "Board, Governance & Transfers",
    "label_ar": "Ù…Ø¬Ù„Ø³ Ø§Ù„Ø¥Ø¯Ø§Ø±Ø© ÙˆØ§Ù„Ø­ÙˆÙƒÙ…Ø© ÙˆØ§Ù„ØªØ­ÙˆÙŠÙ„Ø§Øª",
    "version": "1.0.0",
    "description": "Board management, meetings, resolutions, membership transfers, and special offers",
    "sort_order": 90,
    "icon": "fas fa-landmark",
    "routes_file": "routes.php",
    "css_files": ["css/board.css"],
    "js_files": ["js/board.js"],
    "menu": [
        {
            "label_en": "Board & Governance",
            "label_ar": "Ù…Ø¬Ù„Ø³ Ø§Ù„Ø¥Ø¯Ø§Ø±Ø© ÙˆØ§Ù„Ø­ÙˆÙƒÙ…Ø©",
            "icon": "fas fa-landmark",
            "permission": "board.view",
            "sort_order": 90,
            "children": [
                {"label_en": "Board Terms", "label_ar": "ÙØªØ±Ø§Øª Ø§Ù„Ù…Ø¬Ù„Ø³", "route": "/board/terms", "permission": "board.view", "sort_order": 1},
                {"label_en": "Board Members", "label_ar": "Ø£Ø¹Ø¶Ø§Ø¡ Ø§Ù„Ù…Ø¬Ù„Ø³", "route": "/board/members", "permission": "board.view", "sort_order": 2},
                {"label_en": "Committees", "label_ar": "Ø§Ù„Ù„Ø¬Ø§Ù†", "route": "/board/committees", "permission": "board.view", "sort_order": 3},
                {"label_en": "Meetings", "label_ar": "Ø§Ù„Ø§Ø¬ØªÙ…Ø§Ø¹Ø§Øª", "route": "/board/meetings", "permission": "board.view", "sort_order": 4},
                {"label_en": "Resolutions", "label_ar": "Ø§Ù„Ù‚Ø±Ø§Ø±Ø§Øª", "route": "/board/resolutions", "permission": "board.resolutions", "sort_order": 5}
            ]
        },
        {
            "label_en": "Transfers & Offers",
            "label_ar": "Ø§Ù„ØªØ­ÙˆÙŠÙ„Ø§Øª ÙˆØ§Ù„Ø¹Ø±ÙˆØ¶",
            "icon": "fas fa-exchange-alt",
            "permission": "transfers.view",
            "sort_order": 91,
            "children": [
                {"label_en": "Membership Transfers", "label_ar": "Ù†Ù‚Ù„ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©", "route": "/transfers", "permission": "transfers.view", "sort_order": 1},
                {"label_en": "Transfer Types", "label_ar": "Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ù†Ù‚Ù„", "route": "/transfer-types", "permission": "transfers.manage", "sort_order": 2},
                {"label_en": "Special Offers", "label_ar": "Ø§Ù„Ø¹Ø±ÙˆØ¶ Ø§Ù„Ø®Ø§ØµØ©", "route": "/offers", "permission": "offers.view", "sort_order": 3}
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "board_summary",
            "label_en": "Board Summary",
            "label_ar": "Ù…Ù„Ø®Øµ Ø§Ù„Ù…Ø¬Ù„Ø³",
            "permission": "board.view",
            "partial": "views/widgets/summary.php",
            "size": "col-md-4",
            "sort_order": 90
        }
    ]
}
```

## File: modules\board\routes.php

```php
<?php
return [
    // Board Terms
    ['GET',  '/board/terms',                    'BoardTermController@index',       'board.view'],
    ['GET',  '/board/terms/datatable',          'BoardTermController@datatable',   'board.view'],
    ['GET',  '/board/terms/create',             'BoardTermController@create',      'board.manage'],
    ['POST', '/board/terms',                    'BoardTermController@store',       'board.manage'],
    ['GET',  '/board/terms/{id}',               'BoardTermController@show',        'board.view'],
    ['GET',  '/board/terms/{id}/edit',          'BoardTermController@edit',        'board.manage'],
    ['POST', '/board/terms/{id}',               'BoardTermController@update',      'board.manage'],
    ['POST', '/board/terms/{id}/delete',        'BoardTermController@delete',      'board.manage'],

    // Board Members
    ['GET',  '/board/members',                  'BoardMemberController@index',     'board.view'],
    ['GET',  '/board/members/datatable',        'BoardMemberController@datatable', 'board.view'],
    ['GET',  '/board/members/create',           'BoardMemberController@create',    'board.manage'],
    ['POST', '/board/members',                  'BoardMemberController@store',     'board.manage'],
    ['GET',  '/board/members/{id}/edit',        'BoardMemberController@edit',      'board.manage'],
    ['POST', '/board/members/{id}',             'BoardMemberController@update',    'board.manage'],
    ['POST', '/board/members/{id}/resign',      'BoardMemberController@resign',    'board.manage'],
    ['POST', '/board/members/{id}/delete',      'BoardMemberController@delete',    'board.manage'],

    // Committees
    ['GET',  '/board/committees',               'CommitteeController@index',       'board.view'],
    ['GET',  '/board/committees/datatable',     'CommitteeController@datatable',   'board.view'],
    ['GET',  '/board/committees/create',        'CommitteeController@create',      'board.manage'],
    ['POST', '/board/committees',               'CommitteeController@store',       'board.manage'],
    ['GET',  '/board/committees/{id}/edit',     'CommitteeController@edit',        'board.manage'],
    ['POST', '/board/committees/{id}',          'CommitteeController@update',      'board.manage'],
    ['POST', '/board/committees/{id}/add-member',    'CommitteeController@addMember',    'board.manage'],
    ['POST', '/board/committees/{id}/remove-member', 'CommitteeController@removeMember', 'board.manage'],

    // Meetings
    ['GET',  '/board/meetings',                 'MeetingController@index',         'board.view'],
    ['GET',  '/board/meetings/datatable',       'MeetingController@datatable',     'board.view'],
    ['GET',  '/board/meetings/create',          'MeetingController@create',        'board.manage'],
    ['POST', '/board/meetings',                 'MeetingController@store',         'board.manage'],
    ['GET',  '/board/meetings/{id}',            'MeetingController@show',          'board.view'],
    ['GET',  '/board/meetings/{id}/edit',       'MeetingController@edit',          'board.manage'],
    ['POST', '/board/meetings/{id}',            'MeetingController@update',        'board.manage'],
    ['POST', '/board/meetings/{id}/attendance', 'MeetingController@saveAttendance','board.manage'],
    ['POST', '/board/meetings/{id}/approve-minutes', 'MeetingController@approveMinutes', 'board.manage'],
    ['POST', '/board/meetings/{id}/status',     'MeetingController@changeStatus',  'board.manage'],

    // Resolutions
    ['GET',  '/board/resolutions',              'ResolutionController@index',      'board.resolutions'],
    ['GET',  '/board/resolutions/datatable',    'ResolutionController@datatable',  'board.resolutions'],
    ['GET',  '/board/resolutions/create',       'ResolutionController@create',     'board.resolutions'],
    ['POST', '/board/resolutions',              'ResolutionController@store',      'board.resolutions'],
    ['GET',  '/board/resolutions/{id}',         'ResolutionController@show',       'board.resolutions'],
    ['GET',  '/board/resolutions/{id}/edit',    'ResolutionController@edit',       'board.resolutions'],
    ['POST', '/board/resolutions/{id}',         'ResolutionController@update',     'board.resolutions'],
    ['POST', '/board/resolutions/{id}/vote',    'ResolutionController@recordVote', 'board.resolutions'],
    ['POST', '/board/resolutions/{id}/implement','ResolutionController@markImplemented','board.resolutions'],

    // Membership Transfers
    ['GET',  '/transfers',                      'TransferController@index',        'transfers.view'],
    ['GET',  '/transfers/datatable',            'TransferController@datatable',    'transfers.view'],
    ['GET',  '/transfers/create',               'TransferController@create',       'transfers.manage'],
    ['POST', '/transfers',                      'TransferController@store',        'transfers.manage'],
    ['GET',  '/transfers/{id}',                 'TransferController@show',         'transfers.view'],
    ['POST', '/transfers/{id}/status',          'TransferController@changeStatus', 'transfers.manage'],
    ['POST', '/transfers/{id}/document',        'TransferController@uploadDocument','transfers.manage'],
    ['POST', '/transfers/{id}/heir',            'TransferController@addHeir',      'transfers.manage'],
    ['POST', '/transfers/{id}/heir-delete',     'TransferController@removeHeir',   'transfers.manage'],
    ['POST', '/transfers/{id}/approve',         'TransferController@approve',      'transfers.approve'],
    ['POST', '/transfers/{id}/complete',        'TransferController@complete',     'transfers.approve'],

    // Transfer Types
    ['GET',  '/transfer-types',                 'TransferTypeController@index',    'transfers.manage'],
    ['GET',  '/transfer-types/datatable',       'TransferTypeController@datatable','transfers.manage'],
    ['GET',  '/transfer-types/create',          'TransferTypeController@create',   'transfers.manage'],
    ['POST', '/transfer-types',                 'TransferTypeController@store',    'transfers.manage'],
    ['GET',  '/transfer-types/{id}/edit',       'TransferTypeController@edit',     'transfers.manage'],
    ['POST', '/transfer-types/{id}',            'TransferTypeController@update',   'transfers.manage'],

    // Special Offers
    ['GET',  '/offers',                         'SpecialOfferController@index',    'offers.view'],
    ['GET',  '/offers/datatable',               'SpecialOfferController@datatable','offers.view'],
    ['GET',  '/offers/create',                  'SpecialOfferController@create',   'offers.manage'],
    ['POST', '/offers',                         'SpecialOfferController@store',    'offers.manage'],
    ['GET',  '/offers/{id}',                    'SpecialOfferController@show',     'offers.view'],
    ['GET',  '/offers/{id}/edit',               'SpecialOfferController@edit',     'offers.manage'],
    ['POST', '/offers/{id}',                    'SpecialOfferController@update',   'offers.manage'],
    ['POST', '/offers/{id}/apply',              'SpecialOfferController@applyToMember', 'offers.manage'],
];
```

## File: modules\board\views\committees\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= e($title) ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-4">
                            <label class="form-label">Code <span class="text-danger">*</span></label>
                            <input type="text" name="committee_code" class="form-control" value="<?= e($committee['committee_code'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="committee_code"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.name') ?> (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="name_en" class="form-control" value="<?= e($committee['name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="name_en"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.name') ?> (AR) <span class="text-danger">*</span></label>
                            <input type="text" name="name_ar" class="form-control" value="<?= e($committee['name_ar'] ?? '') ?>" required dir="rtl">
                            <div class="invalid-feedback" data-field="name_ar"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.term') ?> <span class="text-danger">*</span></label>
                            <select name="term_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($terms as $t): ?>
                                <option value="<?= $t['id'] ?>" <?= selected($t['id'], $committee['term_id'] ?? '') ?>><?= e($t['term_name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="term_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Chairperson</label>
                            <select name="chairperson_board_member_id" class="form-select">
                                <option value="">-- None --</option>
                                <?php foreach ($boardMembers as $bm): ?>
                                <option value="<?= $bm['id'] ?>" <?= selected($bm['id'], $committee['chairperson_board_member_id'] ?? '') ?>><?= e($bm['full_name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Description (EN)</label>
                            <textarea name="description_en" class="form-control" rows="3"><?= e($committee['description_en'] ?? '') ?></textarea>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Description (AR)</label>
                            <textarea name="description_ar" class="form-control" rows="3" dir="rtl"><?= e($committee['description_ar'] ?? '') ?></textarea>
                        </div>
                        <?php if ($committee): ?>
                        <div class="col-12">
                            <div class="form-check">
                                <input type="checkbox" name="is_active" class="form-check-input" value="1" <?= checked(1, $committee['is_active'] ?? 1) ?>>
                                <label class="form-check-label">Active</label>
                            </div>
                        </div>
                        <?php endif; ?>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/board/committees" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
        <?php if ($committee && !empty($committeeMembers)): ?>
        <div class="card mt-3">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h6 class="mb-0">Committee Members</h6>
            </div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Name</th><th>Role</th><th>Joined</th><th>Actions</th></tr></thead>
                    <tbody>
                    <?php foreach ($committeeMembers as $cm): ?>
                    <tr>
                        <td><?= e($cm['full_name_en']) ?></td>
                        <td><?= e($cm['role_in_committee'] ?? '-') ?></td>
                        <td><?= format_date($cm['joined_date']) ?></td>
                        <td>
                            <form method="POST" action="/board/committees/<?= $committee['id'] ?>/remove-member" class="d-inline" data-ajax="true">
                                <?= csrf_field() ?>
                                <input type="hidden" name="board_member_id" value="<?= $cm['board_member_id'] ?>">
                                <button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('Remove?')"><i class="fas fa-times"></i></button>
                            </form>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>
        <?php if ($committee): ?>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0">Add Committee Member</h6></div>
            <div class="card-body">
                <form method="POST" action="/board/committees/<?= $committee['id'] ?>/add-member" data-ajax="true" class="row g-2">
                    <?= csrf_field() ?>
                    <div class="col-md-5">
                        <select name="board_member_id" class="form-select" required>
                            <option value="">-- Select Board Member --</option>
                            <?php foreach ($boardMembers as $bm): ?>
                            <option value="<?= $bm['id'] ?>"><?= e($bm['full_name_en']) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="col-md-4">
                        <input type="text" name="role_in_committee" class="form-control" placeholder="Role (optional)">
                    </div>
                    <div class="col-md-3">
                        <button type="submit" class="btn btn-primary w-100"><i class="fas fa-plus me-1"></i>Add</button>
                    </div>
                </form>
            </div>
        </div>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\board\views\committees\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-users-cog me-2"></i><?= lang('board.committees') ?></h4>
    <?php if (auth_can('board.manage')): ?>
    <a href="/board/committees/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card">
    <div class="card-body">
        <table id="committeesTable" class="table table-striped table-hover w-100"
               data-url="/board/committees/datatable"
               data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"code","title":"Code"},{"data":"name","title":"<?= lang('board.name') ?>"},{"data":"term","title":"<?= lang('board.term') ?>"},{"data":"members","title":"Members"},{"data":"status","title":"<?= lang('common.status') ?>"},{"data":"actions","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}]'>
            <thead><tr><th>#</th><th>Code</th><th><?= lang('board.name') ?></th><th><?= lang('board.term') ?></th><th>Members</th><th><?= lang('common.status') ?></th><th><?= lang('common.actions') ?></th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function(){ DataTableHelper.init('committeesTable'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\meetings\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= e($title) ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.term') ?> <span class="text-danger">*</span></label>
                            <select name="term_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($terms as $t): ?>
                                <option value="<?= $t['id'] ?>" <?= selected($t['id'], $meeting['term_id'] ?? '') ?>><?= e($t['term_name_en']) ?></option>
                                <?php endforeach; ?>
                            </select><div class="invalid-feedback" data-field="term_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Committee (optional)</label>
                            <select name="committee_id" class="form-select">
                                <option value="">-- Full Board --</option>
                                <?php foreach ($committees as $c): ?>
                                <option value="<?= $c['id'] ?>" <?= selected($c['id'], $meeting['committee_id'] ?? '') ?>><?= e($c['name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Meeting Type <span class="text-danger">*</span></label>
                            <select name="meeting_type" class="form-select" required>
                                <option value="regular" <?= selected('regular', $meeting['meeting_type'] ?? 'regular') ?>>Regular</option>
                                <option value="emergency" <?= selected('emergency', $meeting['meeting_type'] ?? '') ?>>Emergency</option>
                                <option value="special" <?= selected('special', $meeting['meeting_type'] ?? '') ?>>Special</option>
                                <option value="annual_general" <?= selected('annual_general', $meeting['meeting_type'] ?? '') ?>>Annual General</option>
                            </select><div class="invalid-feedback" data-field="meeting_type"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Title (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="title_en" class="form-control" value="<?= e($meeting['title_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="title_en"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Title (AR)</label>
                            <input type="text" name="title_ar" class="form-control" value="<?= e($meeting['title_ar'] ?? '') ?>" dir="rtl">
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Date <span class="text-danger">*</span></label>
                            <input type="date" name="meeting_date" class="form-control" value="<?= e($meeting['meeting_date'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="meeting_date"></div>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Start Time</label>
                            <input type="time" name="start_time" class="form-control" value="<?= e($meeting['start_time'] ?? '') ?>">
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">End Time</label>
                            <input type="time" name="end_time" class="form-control" value="<?= e($meeting['end_time'] ?? '') ?>">
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Quorum Required <span class="text-danger">*</span></label>
                            <input type="number" name="quorum_required" class="form-control" value="<?= e($meeting['quorum_required'] ?? 0) ?>" min="0" required>
                            <div class="invalid-feedback" data-field="quorum_required"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Location</label>
                            <input type="text" name="location" class="form-control" value="<?= e($meeting['location'] ?? '') ?>">
                        </div>
                        <?php if ($meeting): ?>
                        <div class="col-12">
                            <label class="form-label">Minutes</label>
                            <textarea name="minutes_text" class="form-control" rows="5"><?= e($meeting['minutes_text'] ?? '') ?></textarea>
                        </div>
                        <?php endif; ?>
                        <div class="col-12">
                            <label class="form-label">Notes</label>
                            <textarea name="notes" class="form-control" rows="2"><?= e($meeting['notes'] ?? '') ?></textarea>
                        </div>
                    </div>

                    <hr>
                    <h6>Agenda Items</h6>
                    <div id="agendaContainer">
                        <?php
                        $items = $agendaItems ?: [['title_en' => '', 'description' => '']];
                        foreach ($items as $idx => $ai):
                        ?>
                        <div class="row g-2 mb-2 agenda-row">
                            <div class="col-md-5">
                                <input type="text" name="agenda_title[]" class="form-control" placeholder="Agenda title" value="<?= e($ai['title_en'] ?? '') ?>">
                            </div>
                            <div class="col-md-5">
                                <input type="text" name="agenda_description[]" class="form-control" placeholder="Description (optional)" value="<?= e($ai['description'] ?? '') ?>">
                            </div>
                            <div class="col-md-2">
                                <button type="button" class="btn btn-outline-danger btn-remove-agenda w-100"><i class="fas fa-times"></i></button>
                            </div>
                        </div>
                        <?php endforeach; ?>
                    </div>
                    <button type="button" id="addAgendaBtn" class="btn btn-sm btn-outline-primary mt-2"><i class="fas fa-plus me-1"></i>Add Agenda Item</button>

                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/board/meetings" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.getElementById('addAgendaBtn').addEventListener('click', function() {
    var row = document.createElement('div');
    row.className = 'row g-2 mb-2 agenda-row';
    row.innerHTML = '<div class="col-md-5"><input type="text" name="agenda_title[]" class="form-control" placeholder="Agenda title"></div><div class="col-md-5"><input type="text" name="agenda_description[]" class="form-control" placeholder="Description"></div><div class="col-md-2"><button type="button" class="btn btn-outline-danger btn-remove-agenda w-100"><i class="fas fa-times"></i></button></div>';
    document.getElementById('agendaContainer').appendChild(row);
});
document.addEventListener('click', function(e) {
    if (e.target.closest('.btn-remove-agenda')) {
        var rows = document.querySelectorAll('.agenda-row');
        if (rows.length > 1) e.target.closest('.agenda-row').remove();
    }
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\meetings\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-handshake me-2"></i><?= lang('board.meetings') ?></h4>
    <?php if (auth_can('board.manage')): ?>
    <a href="/board/meetings/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card">
    <div class="card-body">
        <table id="meetingsTable" class="table table-striped table-hover w-100"
               data-url="/board/meetings/datatable"
               data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"number","title":"#Meeting"},{"data":"title","title":"Title"},{"data":"type","title":"Type"},{"data":"date","title":"Date"},{"data":"status","title":"Status"},{"data":"quorum","title":"Quorum"},{"data":"attendees","title":"Attendees"},{"data":"actions","title":"Actions","orderable":false,"searchable":false}]'>
            <thead><tr><th>#</th><th>#Meeting</th><th>Title</th><th>Type</th><th>Date</th><th>Status</th><th>Quorum</th><th>Attendees</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function(){ DataTableHelper.init('meetingsTable'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\meetings\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= e($meeting['meeting_number']) ?> â€” <?= e($meeting['title_en']) ?></h4>
        <span class="text-muted"><?= format_date($meeting['meeting_date']) ?> | <?= e($meeting['location'] ?? '') ?></span>
    </div>
    <div class="d-flex gap-2">
        <?php if (auth_can('board.manage')): ?>
        <a href="/board/meetings/<?= $meeting['id'] ?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a>
        <?php endif; ?>
        <a href="/board/meetings" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-3">
        <div class="card">
            <div class="card-body text-center">
                <div class="mb-2"><?= status_badge($meeting['status']) ?></div>
                <div class="mb-1"><strong>Type:</strong> <?= ucfirst(str_replace('_', ' ', $meeting['meeting_type'])) ?></div>
                <div class="mb-1"><strong>Quorum:</strong> <?= $meeting['quorum_met'] ? '<span class="text-success">Met</span>' : '<span class="text-danger">Not Met</span>' ?> (<?= $meeting['attendees_count'] ?>/<?= $meeting['quorum_required'] ?>)</div>
                <?php if ($meeting['start_time']): ?>
                <div><strong>Time:</strong> <?= e($meeting['start_time']) ?> - <?= e($meeting['end_time'] ?? '?') ?></div>
                <?php endif; ?>
                <?php if (auth_can('board.manage') && $meeting['status'] !== 'completed'): ?>
                <hr>
                <form method="POST" action="/board/meetings/<?= $meeting['id'] ?>/status" data-ajax="true">
                    <?= csrf_field() ?>
                    <select name="status" class="form-select form-select-sm mb-2">
                        <option value="scheduled">Scheduled</option>
                        <option value="in_progress">In Progress</option>
                        <option value="completed">Completed</option>
                        <option value="cancelled">Cancelled</option>
                        <option value="postponed">Postponed</option>
                    </select>
                    <button type="submit" class="btn btn-sm btn-primary w-100">Update Status</button>
                </form>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-9">
        <ul class="nav nav-tabs mb-3" role="tablist">
            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-agenda">Agenda</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-attendance">Attendance</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-minutes">Minutes</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-resolutions">Resolutions</a></li>
        </ul>
        <div class="tab-content">
            <div class="tab-pane fade show active" id="tab-agenda">
                <div class="card">
                    <div class="card-body p-0">
                        <table class="table table-sm mb-0">
                            <thead><tr><th width="50">#</th><th>Title</th><th>Status</th></tr></thead>
                            <tbody>
                            <?php foreach ($agendaItems as $ai): ?>
                            <tr>
                                <td><?= $ai['agenda_number'] ?></td>
                                <td><?= e($ai['title_en']) ?><?php if ($ai['description']): ?><br><small class="text-muted"><?= e($ai['description']) ?></small><?php endif; ?></td>
                                <td><?= status_badge($ai['status']) ?></td>
                            </tr>
                            <?php endforeach; ?>
                            <?php if (empty($agendaItems)): ?><tr><td colspan="3" class="text-center text-muted py-3">No agenda items</td></tr><?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
            <div class="tab-pane fade" id="tab-attendance">
                <div class="card">
                    <div class="card-body">
                        <?php if (auth_can('board.manage')): ?>
                        <form method="POST" action="/board/meetings/<?= $meeting['id'] ?>/attendance" data-ajax="true">
                            <?= csrf_field() ?>
                            <table class="table table-sm">
                                <thead><tr><th>Board Member</th><th>Position</th><th>Attendance</th></tr></thead>
                                <tbody>
                                <?php
                                $existingAtt = [];
                                foreach ($attendees as $a) $existingAtt[$a['board_member_id']] = $a['attendance'];
                                foreach ($boardMembers as $bm):
                                    $current = $existingAtt[$bm['id']] ?? '';
                                ?>
                                <tr>
                                    <td><?= e($bm['full_name_en']) ?></td>
                                    <td><?= e($bm['position_title'] ?? '') ?></td>
                                    <td>
                                        <select name="attendance[<?= $bm['id'] ?>]" class="form-select form-select-sm">
                                            <option value="">-- Not recorded --</option>
                                            <option value="present" <?= selected('present', $current) ?>>Present</option>
                                            <option value="absent" <?= selected('absent', $current) ?>>Absent</option>
                                            <option value="excused" <?= selected('excused', $current) ?>>Excused</option>
                                            <option value="proxy" <?= selected('proxy', $current) ?>>Proxy</option>
                                        </select>
                                    </td>
                                </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                            <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save Attendance</button>
                        </form>
                        <?php else: ?>
                        <table class="table table-sm">
                            <thead><tr><th>Name</th><th>Attendance</th></tr></thead>
                            <tbody>
                            <?php foreach ($attendees as $a): ?>
                            <tr><td><?= e($a['full_name_en']) ?></td><td><?= status_badge($a['attendance']) ?></td></tr>
                            <?php endforeach; ?>
                            </tbody>
                        </table>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
            <div class="tab-pane fade" id="tab-minutes">
                <div class="card">
                    <div class="card-body">
                        <?php if ($meeting['minutes_text']): ?>
                        <div class="border rounded p-3 mb-3" style="white-space:pre-wrap"><?= e($meeting['minutes_text']) ?></div>
                        <?php if (!$meeting['minutes_approved'] && auth_can('board.manage')): ?>
                        <form method="POST" action="/board/meetings/<?= $meeting['id'] ?>/approve-minutes" data-ajax="true">
                            <?= csrf_field() ?>
                            <button type="submit" class="btn btn-success"><i class="fas fa-check me-1"></i>Approve Minutes</button>
                        </form>
                        <?php elseif ($meeting['minutes_approved']): ?>
                        <span class="badge bg-success"><i class="fas fa-check me-1"></i>Minutes Approved <?= format_datetime($meeting['minutes_approved_at']) ?></span>
                        <?php endif; ?>
                        <?php else: ?>
                        <p class="text-muted">No minutes recorded yet. <a href="/board/meetings/<?= $meeting['id'] ?>/edit">Edit meeting</a> to add minutes.</p>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
            <div class="tab-pane fade" id="tab-resolutions">
                <div class="card">
                    <div class="card-body p-0">
                        <table class="table table-sm mb-0">
                            <thead><tr><th>#</th><th>Title</th><th>Status</th><th>Votes</th></tr></thead>
                            <tbody>
                            <?php foreach ($resolutions as $r): ?>
                            <tr>
                                <td><a href="/board/resolutions/<?= $r['id'] ?>"><?= e($r['resolution_number']) ?></a></td>
                                <td><?= e($r['title_en']) ?></td>
                                <td><?= status_badge($r['status']) ?></td>
                                <td><?= $r['votes_for'] ?>F / <?= $r['votes_against'] ?>A / <?= $r['votes_abstain'] ?>Ab</td>
                            </tr>
                            <?php endforeach; ?>
                            <?php if (empty($resolutions)): ?><tr><td colspan="4" class="text-center text-muted py-3">No resolutions</td></tr><?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                    <?php if (auth_can('board.resolutions')): ?>
                    <div class="card-footer">
                        <a href="/board/resolutions/create?meeting_id=<?= $meeting['id'] ?>&term_id=<?= $meeting['term_id'] ?>" class="btn btn-sm btn-primary"><i class="fas fa-plus me-1"></i>Create Resolution from Meeting</a>
                    </div>
                    <?php endif; ?>
                </div>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\members\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= e($title) ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.term') ?> <span class="text-danger">*</span></label>
                            <select name="term_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($terms as $t): ?>
                                <option value="<?= $t['id'] ?>" <?= selected($t['id'], $boardMember['term_id'] ?? '') ?>><?= e($t['term_name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="term_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.position') ?> <span class="text-danger">*</span></label>
                            <select name="position_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($positions as $p): ?>
                                <option value="<?= $p['id'] ?>" <?= selected($p['id'], $boardMember['position_id'] ?? '') ?>><?= e($p['title_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="position_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.name') ?> (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="full_name_en" class="form-control" value="<?= e($boardMember['full_name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="full_name_en"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.name') ?> (AR)</label>
                            <input type="text" name="full_name_ar" class="form-control" value="<?= e($boardMember['full_name_ar'] ?? '') ?>" dir="rtl">
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.start_date') ?> <span class="text-danger">*</span></label>
                            <input type="date" name="start_date" class="form-control" value="<?= e($boardMember['start_date'] ?? date('Y-m-d')) ?>" required>
                            <div class="invalid-feedback" data-field="start_date"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.end_date') ?></label>
                            <input type="date" name="end_date" class="form-control" value="<?= e($boardMember['end_date'] ?? '') ?>">
                        </div>
                        <?php if ($boardMember): ?>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('common.status') ?></label>
                            <select name="status" class="form-select">
                                <option value="active" <?= selected('active', $boardMember['status']) ?>>Active</option>
                                <option value="resigned" <?= selected('resigned', $boardMember['status']) ?>>Resigned</option>
                                <option value="removed" <?= selected('removed', $boardMember['status']) ?>>Removed</option>
                                <option value="term_ended" <?= selected('term_ended', $boardMember['status']) ?>>Term Ended</option>
                            </select>
                        </div>
                        <?php endif; ?>
                        <div class="col-md-6">
                            <label class="form-label">Club Member ID</label>
                            <input type="number" name="member_id" class="form-control" value="<?= e($boardMember['member_id'] ?? '') ?>" placeholder="Link to club member (optional)">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">System User ID</label>
                            <input type="number" name="user_id" class="form-control" value="<?= e($boardMember['user_id'] ?? '') ?>" placeholder="Link to system user (optional)">
                        </div>
                        <div class="col-12">
                            <label class="form-label"><?= lang('board.notes') ?></label>
                            <textarea name="notes" class="form-control" rows="3"><?= e($boardMember['notes'] ?? '') ?></textarea>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/board/members" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\members\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-user-tie me-2"></i><?= lang('board.board_members') ?></h4>
    <?php if (auth_can('board.manage')): ?>
    <a href="/board/members/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('board.add_member') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3">
    <div class="card-body">
        <form class="row g-2" data-filter-table="membersTable" id="filterForm">
            <div class="col-md-4">
                <select name="term_id" class="form-select" onchange="DataTableHelper.reload('membersTable')">
                    <option value=""><?= lang('board.all_terms') ?></option>
                    <?php foreach ($terms as $t): ?>
                    <option value="<?= $t['id'] ?>"><?= e($t['term_name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-3">
                <select name="status" class="form-select" onchange="DataTableHelper.reload('membersTable')">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="active">Active</option>
                    <option value="resigned">Resigned</option>
                    <option value="removed">Removed</option>
                    <option value="term_ended">Term Ended</option>
                </select>
            </div>
        </form>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="membersTable" class="table table-striped table-hover w-100"
               data-url="/board/members/datatable"
               data-columns='[
                   {"data":"id","title":"#","width":"50px"},
                   {"data":"name","title":"<?= lang('board.name') ?>"},
                   {"data":"position","title":"<?= lang('board.position') ?>"},
                   {"data":"term","title":"<?= lang('board.term') ?>"},
                   {"data":"start_date","title":"<?= lang('board.start_date') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"},
                   {"data":"actions","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}
               ]'>
            <thead><tr><th>#</th><th><?= lang('board.name') ?></th><th><?= lang('board.position') ?></th><th><?= lang('board.term') ?></th><th><?= lang('board.start_date') ?></th><th><?= lang('common.status') ?></th><th><?= lang('common.actions') ?></th></tr></thead>
        </table>
    </div>
</div>

<!-- Resign Modal -->
<div class="modal fade" id="resignModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form id="resignForm" method="POST" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-header"><h5 class="modal-title"><?= lang('board.resign_member') ?></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
                <div class="modal-body">
                    <label class="form-label"><?= lang('board.resignation_reason') ?></label>
                    <textarea name="resignation_reason" class="form-control" rows="3" required></textarea>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                    <button type="submit" class="btn btn-danger"><?= lang('board.confirm_resign') ?></button>
                </div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('membersTable');

    document.addEventListener('click', function(e) {
        var btn = e.target.closest('.btn-resign');
        if (btn) {
            var id = btn.dataset.id;
            document.getElementById('resignForm').action = '/board/members/' + id + '/resign';
            new bootstrap.Modal(document.getElementById('resignModal')).show();
        }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\offers\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= e($title) ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-3">
                            <label class="form-label">Offer Code <span class="text-danger">*</span></label>
                            <input type="text" name="offer_code" class="form-control" value="<?= e($offer['offer_code'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="offer_code"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="name_en" class="form-control" value="<?= e($offer['name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="name_en"></div>
                        </div>
                        <div class="col-md-5">
                            <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                            <input type="text" name="name_ar" class="form-control" value="<?= e($offer['name_ar'] ?? '') ?>" required dir="rtl">
                            <div class="invalid-feedback" data-field="name_ar"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Offer Type <span class="text-danger">*</span></label>
                            <select name="offer_type" class="form-select" required>
                                <?php foreach (['discount','fee_waiver','payment_plan','upgrade','bundle','custom'] as $t): ?>
                                <option value="<?= $t ?>" <?= selected($t, $offer['offer_type'] ?? 'discount') ?>><?= ucfirst(str_replace('_', ' ', $t)) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Discount Method</label>
                            <select name="discount_method" class="form-select">
                                <option value="">-- N/A --</option>
                                <option value="percentage" <?= selected('percentage', $offer['discount_method'] ?? '') ?>>Percentage</option>
                                <option value="fixed_amount" <?= selected('fixed_amount', $offer['discount_method'] ?? '') ?>>Fixed Amount</option>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Discount Value</label>
                            <input type="number" name="discount_value" class="form-control" value="<?= e($offer['discount_value'] ?? '') ?>" step="0.01" min="0">
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Start Date <span class="text-danger">*</span></label>
                            <input type="date" name="start_date" class="form-control" value="<?= e($offer['start_date'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="start_date"></div>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">End Date <span class="text-danger">*</span></label>
                            <input type="date" name="end_date" class="form-control" value="<?= e($offer['end_date'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="end_date"></div>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Max Uses</label>
                            <input type="number" name="max_uses" class="form-control" value="<?= e($offer['max_uses'] ?? '') ?>" min="0" placeholder="Unlimited">
                        </div>
                        <?php if ($offer): ?>
                        <div class="col-md-3">
                            <label class="form-label">Status</label>
                            <select name="status" class="form-select">
                                <?php foreach (['draft','active','paused','expired','cancelled'] as $s): ?>
                                <option value="<?= $s ?>" <?= selected($s, $offer['status'] ?? 'draft') ?>><?= ucfirst($s) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <?php endif; ?>
                        <div class="col-12">
                            <label class="form-label">Target Membership Types</label>
                            <div class="row g-2">
                                <?php foreach ($membershipTypes as $mt): ?>
                                <div class="col-auto">
                                    <div class="form-check">
                                        <input type="checkbox" name="membership_type_ids[]" value="<?= $mt['id'] ?>" class="form-check-input" <?= in_array($mt['id'], $selectedTypes) ? 'checked' : '' ?>>
                                        <label class="form-check-label"><?= e($mt['name_en']) ?></label>
                                    </div>
                                </div>
                                <?php endforeach; ?>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Description (EN)</label>
                            <textarea name="description_en" class="form-control" rows="3"><?= e($offer['description_en'] ?? '') ?></textarea>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Description (AR)</label>
                            <textarea name="description_ar" class="form-control" rows="3" dir="rtl"><?= e($offer['description_ar'] ?? '') ?></textarea>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Terms (EN)</label>
                            <textarea name="terms_en" class="form-control" rows="3"><?= e($offer['terms_en'] ?? '') ?></textarea>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Terms (AR)</label>
                            <textarea name="terms_ar" class="form-control" rows="3" dir="rtl"><?= e($offer['terms_ar'] ?? '') ?></textarea>
                        </div>
                        <div class="col-12">
                            <div class="form-check"><input type="checkbox" name="requires_approval" class="form-check-input" value="1" <?= checked(1, $offer['requires_approval'] ?? 0) ?>><label class="form-check-label">Requires Approval to Apply</label></div>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/offers" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\offers\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tags me-2"></i><?= lang('board.special_offers') ?></h4>
    <?php if (auth_can('offers.manage')): ?>
    <a href="/offers/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card">
    <div class="card-body">
        <table id="offersTable" class="table table-striped table-hover w-100"
               data-url="/offers/datatable"
               data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"code","title":"Code"},{"data":"name","title":"Name"},{"data":"type","title":"Type"},{"data":"dates","title":"Dates"},{"data":"uses","title":"Uses"},{"data":"status","title":"Status"},{"data":"actions","title":"Actions","orderable":false,"searchable":false}]'>
            <thead><tr><th>#</th><th>Code</th><th>Name</th><th>Type</th><th>Dates</th><th>Uses</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function(){ DataTableHelper.init('offersTable'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\offers\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= e($offer['offer_code']) ?> â€” <?= e($offer['name_en']) ?> <?= status_badge($offer['status']) ?></h4>
        <p class="text-muted mb-0"><?= format_date($offer['start_date']) ?> â†’ <?= format_date($offer['end_date']) ?> | Uses: <?= $offer['current_uses'] ?><?= $offer['max_uses'] ? '/' . $offer['max_uses'] : '' ?></p>
    </div>
    <div class="d-flex gap-2">
        <?php if (auth_can('offers.manage')): ?>
        <a href="/offers/<?= $offer['id'] ?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a>
        <?php endif; ?>
        <a href="/offers" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Offer Details</h6></div>
            <div class="card-body">
                <table class="table table-sm table-borderless mb-0">
                    <tr><td class="text-muted">Type</td><td><?= ucfirst(str_replace('_', ' ', $offer['offer_type'])) ?></td></tr>
                    <?php if ($offer['discount_method']): ?>
                    <tr><td class="text-muted">Discount</td><td><?= $offer['discount_method'] === 'percentage' ? format_number($offer['discount_value']) . '%' : money($offer['discount_value']) ?></td></tr>
                    <?php endif; ?>
                    <tr><td class="text-muted">Approval</td><td><?= $offer['requires_approval'] ? 'Required' : 'Not required' ?></td></tr>
                </table>
                <?php if (!empty($membershipTypes)): ?>
                <hr>
                <h6 class="small text-muted">Target Types</h6>
                <?php foreach ($membershipTypes as $mt): ?>
                <span class="badge bg-info me-1"><?= e($mt['name_en']) ?></span>
                <?php endforeach; ?>
                <?php endif; ?>

                <?php if (auth_can('offers.manage') && $offer['status'] === 'active'): ?>
                <hr>
                <h6 class="small text-muted">Apply to Member</h6>
                <form method="POST" action="/offers/<?= $offer['id'] ?>/apply" data-ajax="true">
                    <?= csrf_field() ?>
                    <input type="number" name="member_id" class="form-control form-control-sm mb-2" placeholder="Member ID" required>
                    <input type="number" name="discount_amount" class="form-control form-control-sm mb-2" placeholder="Discount Amount" step="0.01" required>
                    <input type="number" name="invoice_id" class="form-control form-control-sm mb-2" placeholder="Invoice ID (optional)">
                    <input type="text" name="notes" class="form-control form-control-sm mb-2" placeholder="Notes">
                    <button type="submit" class="btn btn-success btn-sm w-100"><i class="fas fa-check me-1"></i>Apply Offer</button>
                </form>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-8">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Applied To (<?= count($applications) ?>)</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Member</th><th>Date</th><th>Discount</th><th>Invoice</th></tr></thead>
                    <tbody>
                    <?php foreach ($applications as $app): ?>
                    <tr>
                        <td><?= e($app['full_name_en']) ?> <small class="text-muted">(<?= e($app['membership_number']) ?>)</small></td>
                        <td><?= format_date($app['applied_date']) ?></td>
                        <td><?= money($app['discount_amount']) ?></td>
                        <td><?= $app['invoice_id'] ?: '-' ?></td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($applications)): ?><tr><td colspan="4" class="text-center text-muted py-3">No applications yet</td></tr><?php endif; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\resolutions\form.php

```php
<?php
$prefillMeetingId = $_GET['meeting_id'] ?? ($resolution['meeting_id'] ?? '');
$prefillTermId = $_GET['term_id'] ?? ($resolution['term_id'] ?? '');
?>
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= e($title) ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-4">
                            <label class="form-label">Term <span class="text-danger">*</span></label>
                            <select name="term_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($terms as $t): ?>
                                <option value="<?= $t['id'] ?>" <?= selected($t['id'], $prefillTermId) ?>><?= e($t['term_name_en']) ?></option>
                                <?php endforeach; ?>
                            </select><div class="invalid-feedback" data-field="term_id"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Meeting (optional)</label>
                            <select name="meeting_id" class="form-select">
                                <option value="">-- Standalone --</option>
                                <?php foreach ($meetings as $m): ?>
                                <option value="<?= $m['id'] ?>" <?= selected($m['id'], $prefillMeetingId) ?>><?= e($m['meeting_number'] . ' - ' . $m['title_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Resolution Date <span class="text-danger">*</span></label>
                            <input type="date" name="resolution_date" class="form-control" value="<?= e($resolution['resolution_date'] ?? date('Y-m-d')) ?>" required>
                            <div class="invalid-feedback" data-field="resolution_date"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Title (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="title_en" class="form-control" value="<?= e($resolution['title_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="title_en"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Title (AR)</label>
                            <input type="text" name="title_ar" class="form-control" value="<?= e($resolution['title_ar'] ?? '') ?>" dir="rtl">
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Category</label>
                            <input type="text" name="category" class="form-control" value="<?= e($resolution['category'] ?? '') ?>" placeholder="e.g. finance, membership">
                        </div>
                        <?php if ($resolution): ?>
                        <div class="col-md-4">
                            <label class="form-label">Status</label>
                            <select name="status" class="form-select">
                                <?php foreach (['draft','approved','rejected','implemented','cancelled','superseded'] as $s): ?>
                                <option value="<?= $s ?>" <?= selected($s, $resolution['status']) ?>><?= ucfirst($s) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <?php endif; ?>
                        <div class="col-md-4">
                            <label class="form-label">Implementation Deadline</label>
                            <input type="date" name="implementation_deadline" class="form-control" value="<?= e($resolution['implementation_deadline'] ?? '') ?>">
                        </div>
                        <div class="col-12">
                            <div class="form-check">
                                <input type="checkbox" name="requires_implementation" class="form-check-input" value="1" <?= checked(1, $resolution['requires_implementation'] ?? 0) ?>>
                                <label class="form-check-label">Requires Implementation</label>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Resolution Text (EN) <span class="text-danger">*</span></label>
                            <textarea name="resolution_text" class="form-control" rows="6" required><?= e($resolution['resolution_text'] ?? '') ?></textarea>
                            <div class="invalid-feedback" data-field="resolution_text"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Resolution Text (AR)</label>
                            <textarea name="resolution_text_ar" class="form-control" rows="6" dir="rtl"><?= e($resolution['resolution_text_ar'] ?? '') ?></textarea>
                        </div>
                        <?php if ($resolution): ?>
                        <div class="col-12">
                            <label class="form-label">Implementation Notes</label>
                            <textarea name="implementation_notes" class="form-control" rows="3"><?= e($resolution['implementation_notes'] ?? '') ?></textarea>
                        </div>
                        <?php endif; ?>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/board/resolutions" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\resolutions\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-gavel me-2"></i><?= lang('board.resolutions') ?></h4>
    <?php if (auth_can('board.resolutions')): ?>
    <a href="/board/resolutions/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card">
    <div class="card-body">
        <table id="resolutionsTable" class="table table-striped table-hover w-100"
               data-url="/board/resolutions/datatable"
               data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"number","title":"Resolution #"},{"data":"date","title":"Date"},{"data":"title","title":"Title"},{"data":"meeting","title":"Meeting"},{"data":"status","title":"Status"},{"data":"votes","title":"Votes"},{"data":"actions","title":"Actions","orderable":false,"searchable":false}]'>
            <thead><tr><th>#</th><th>Resolution #</th><th>Date</th><th>Title</th><th>Meeting</th><th>Status</th><th>Votes</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function(){ DataTableHelper.init('resolutionsTable'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\resolutions\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= e($resolution['resolution_number']) ?></h4>
        <p class="text-muted mb-0"><?= e($resolution['title_en']) ?></p>
    </div>
    <div class="d-flex gap-2">
        <?php if (auth_can('board.resolutions')): ?>
        <a href="/board/resolutions/<?= $resolution['id'] ?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a>
        <?php endif; ?>
        <a href="/board/resolutions" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-body">
                <table class="table table-sm table-borderless mb-0">
                    <tr><td class="text-muted">Status</td><td><?= status_badge($resolution['status']) ?></td></tr>
                    <tr><td class="text-muted">Date</td><td><?= format_date($resolution['resolution_date']) ?></td></tr>
                    <tr><td class="text-muted">Term</td><td><?= e($resolution['term_name_en'] ?? '-') ?></td></tr>
                    <tr><td class="text-muted">Meeting</td><td><?php if ($resolution['meeting_number']): ?><a href="/board/meetings/<?= $resolution['meeting_id'] ?>"><?= e($resolution['meeting_number']) ?></a><?php else: ?>Standalone<?php endif; ?></td></tr>
                    <tr><td class="text-muted">Category</td><td><?= e($resolution['category'] ?? '-') ?></td></tr>
                    <tr><td class="text-muted">Votes</td><td><span class="text-success"><?= $resolution['votes_for'] ?>F</span> / <span class="text-danger"><?= $resolution['votes_against'] ?>A</span> / <span class="text-muted"><?= $resolution['votes_abstain'] ?>Ab</span></td></tr>
                    <?php if ($resolution['requires_implementation']): ?>
                    <tr><td class="text-muted">Impl. Deadline</td><td><?= format_date($resolution['implementation_deadline']) ?: '-' ?></td></tr>
                    <?php if ($resolution['implemented_at']): ?>
                    <tr><td class="text-muted">Implemented</td><td><?= format_datetime($resolution['implemented_at']) ?></td></tr>
                    <?php endif; ?>
                    <?php endif; ?>
                </table>
                <?php if ($resolution['status'] === 'approved' && $resolution['requires_implementation'] && !$resolution['implemented_at'] && auth_can('board.resolutions')): ?>
                <hr>
                <form method="POST" action="/board/resolutions/<?= $resolution['id'] ?>/implement" data-ajax="true">
                    <?= csrf_field() ?>
                    <textarea name="implementation_notes" class="form-control form-control-sm mb-2" rows="2" placeholder="Implementation notes..."></textarea>
                    <button type="submit" class="btn btn-success btn-sm w-100"><i class="fas fa-check me-1"></i>Mark Implemented</button>
                </form>
                <?php endif; ?>
                <?php if ($supersededBy): ?>
                <div class="alert alert-warning mt-2 mb-0 small">Superseded by <a href="/board/resolutions/<?= $supersededBy['id'] ?>"><?= e($supersededBy['resolution_number']) ?></a></div>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-8">
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Resolution Text</h6></div>
            <div class="card-body" style="white-space:pre-wrap"><?= e($resolution['resolution_text']) ?></div>
        </div>

        <div class="card">
            <div class="card-header"><h6 class="mb-0">Voting</h6></div>
            <div class="card-body">
                <?php if (auth_can('board.resolutions') && $resolution['status'] === 'draft'): ?>
                <form method="POST" action="/board/resolutions/<?= $resolution['id'] ?>/vote" data-ajax="true">
                    <?= csrf_field() ?>
                    <table class="table table-sm">
                        <thead><tr><th>Board Member</th><th>Position</th><th>Vote</th><th>Comments</th></tr></thead>
                        <tbody>
                        <?php
                        $existingVotes = [];
                        $existingComments = [];
                        foreach ($votes as $v) { $existingVotes[$v['board_member_id']] = $v['vote']; $existingComments[$v['board_member_id']] = $v['comments']; }
                        foreach ($boardMembers as $bm):
                            $curVote = $existingVotes[$bm['id']] ?? '';
                            $curComment = $existingComments[$bm['id']] ?? '';
                        ?>
                        <tr>
                            <td><?= e($bm['full_name_en']) ?></td>
                            <td><small class="text-muted"><?= e($bm['position_title'] ?? '') ?></small></td>
                            <td>
                                <select name="votes[<?= $bm['id'] ?>]" class="form-select form-select-sm">
                                    <option value="">--</option>
                                    <option value="for" <?= selected('for', $curVote) ?>>For</option>
                                    <option value="against" <?= selected('against', $curVote) ?>>Against</option>
                                    <option value="abstain" <?= selected('abstain', $curVote) ?>>Abstain</option>
                                </select>
                            </td>
                            <td><input type="text" name="vote_comments[<?= $bm['id'] ?>]" class="form-control form-control-sm" value="<?= e($curComment) ?>" placeholder="Optional"></td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                    <button type="submit" class="btn btn-primary"><i class="fas fa-vote-yea me-1"></i>Save Votes</button>
                </form>
                <?php else: ?>
                <table class="table table-sm">
                    <thead><tr><th>Board Member</th><th>Vote</th><th>Comments</th></tr></thead>
                    <tbody>
                    <?php foreach ($votes as $v): ?>
                    <tr>
                        <td><?= e($v['full_name_en']) ?></td>
                        <td><?= status_badge($v['vote'] === 'for' ? 'approved' : ($v['vote'] === 'against' ? 'rejected' : 'pending')) ?> <?= ucfirst($v['vote']) ?></td>
                        <td><?= e($v['comments'] ?? '') ?></td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($votes)): ?><tr><td colspan="3" class="text-center text-muted py-3">No votes recorded</td></tr><?php endif; ?>
                    </tbody>
                </table>
                <?php endif; ?>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\terms\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header">
                <h5 class="mb-0"><i class="fas fa-calendar-alt me-2"></i><?= e($title) ?></h5>
            </div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.term_name') ?> (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="term_name_en" class="form-control" value="<?= e($term['term_name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="term_name_en"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('board.term_name') ?> (AR) <span class="text-danger">*</span></label>
                            <input type="text" name="term_name_ar" class="form-control" value="<?= e($term['term_name_ar'] ?? '') ?>" required dir="rtl">
                            <div class="invalid-feedback" data-field="term_name_ar"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.start_date') ?> <span class="text-danger">*</span></label>
                            <input type="date" name="start_date" class="form-control" value="<?= e($term['start_date'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="start_date"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.end_date') ?> <span class="text-danger">*</span></label>
                            <input type="date" name="end_date" class="form-control" value="<?= e($term['end_date'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="end_date"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('common.status') ?> <span class="text-danger">*</span></label>
                            <select name="status" class="form-select" required>
                                <option value="upcoming" <?= selected('upcoming', $term['status'] ?? 'upcoming') ?>>Upcoming</option>
                                <option value="active" <?= selected('active', $term['status'] ?? '') ?>>Active</option>
                                <option value="ended" <?= selected('ended', $term['status'] ?? '') ?>>Ended</option>
                            </select>
                            <div class="invalid-feedback" data-field="status"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('board.election_date') ?></label>
                            <input type="date" name="election_date" class="form-control" value="<?= e($term['election_date'] ?? '') ?>">
                        </div>
                        <div class="col-12">
                            <label class="form-label"><?= lang('board.notes') ?></label>
                            <textarea name="notes" class="form-control" rows="3"><?= e($term['notes'] ?? '') ?></textarea>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/board/terms" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\terms\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-calendar-alt me-2"></i><?= lang('board.terms') ?></h4>
    <?php if (auth_can('board.manage')): ?>
    <a href="/board/terms/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="termsTable" class="table table-striped table-hover w-100"
               data-url="/board/terms/datatable"
               data-columns='[
                   {"data":"id","title":"#","width":"50px"},
                   {"data":"term_name","title":"<?= lang('board.term_name') ?>"},
                   {"data":"start_date","title":"<?= lang('board.start_date') ?>"},
                   {"data":"end_date","title":"<?= lang('board.end_date') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"},
                   {"data":"members","title":"<?= lang('board.members_count') ?>"},
                   {"data":"actions","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}
               ]'>
            <thead>
                <tr>
                    <th>#</th>
                    <th><?= lang('board.term_name') ?></th>
                    <th><?= lang('board.start_date') ?></th>
                    <th><?= lang('board.end_date') ?></th>
                    <th><?= lang('common.status') ?></th>
                    <th><?= lang('board.members_count') ?></th>
                    <th><?= lang('common.actions') ?></th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('termsTable');
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\terms\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><?= e(current_lang() === 'ar' ? ($term['term_name_ar'] ?: $term['term_name_en']) : $term['term_name_en']) ?></h4>
    <div class="d-flex gap-2">
        <?php if (auth_can('board.manage')): ?>
        <a href="/board/terms/<?= $term['id'] ?>/edit" class="btn btn-warning"><i class="fas fa-edit me-1"></i><?= lang('common.edit') ?></a>
        <?php endif; ?>
        <a href="/board/terms" class="btn btn-secondary"><i class="fas fa-arrow-left me-1"></i><?= lang('common.back') ?></a>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card h-100">
            <div class="card-header"><h6 class="mb-0"><?= lang('common.details') ?></h6></div>
            <div class="card-body">
                <table class="table table-sm table-borderless">
                    <tr><td class="text-muted"><?= lang('common.status') ?></td><td><?= status_badge($term['status']) ?></td></tr>
                    <tr><td class="text-muted"><?= lang('board.start_date') ?></td><td><?= format_date($term['start_date']) ?></td></tr>
                    <tr><td class="text-muted"><?= lang('board.end_date') ?></td><td><?= format_date($term['end_date']) ?></td></tr>
                    <tr><td class="text-muted"><?= lang('board.election_date') ?></td><td><?= format_date($term['election_date']) ?: '-' ?></td></tr>
                </table>
                <?php if ($term['notes']): ?>
                <p class="text-muted small"><?= nl2br(e($term['notes'])) ?></p>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-8">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('board.board_members') ?> (<?= count($members) ?>)</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th><?= lang('board.name') ?></th><th><?= lang('board.position') ?></th><th><?= lang('common.status') ?></th></tr></thead>
                    <tbody>
                    <?php foreach ($members as $m): ?>
                    <tr>
                        <td><?= e($m['full_name_en']) ?></td>
                        <td><?= e($m['position_title'] ?? '-') ?></td>
                        <td><?= status_badge($m['status']) ?></td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($members)): ?>
                    <tr><td colspan="3" class="text-center text-muted py-3">No board members assigned</td></tr>
                    <?php endif; ?>
                    </tbody>
                </table>
            </div>
        </div>

        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0"><?= lang('board.recent_meetings') ?></h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>#</th><th><?= lang('board.title') ?></th><th><?= lang('board.date') ?></th><th><?= lang('common.status') ?></th></tr></thead>
                    <tbody>
                    <?php foreach ($meetings as $mt): ?>
                    <tr>
                        <td><a href="/board/meetings/<?= $mt['id'] ?>"><?= e($mt['meeting_number']) ?></a></td>
                        <td><?= e($mt['title_en']) ?></td>
                        <td><?= format_date($mt['meeting_date']) ?></td>
                        <td><?= status_badge($mt['status']) ?></td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($meetings)): ?>
                    <tr><td colspan="4" class="text-center text-muted py-3">No meetings yet</td></tr>
                    <?php endif; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\transfers\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-exchange-alt me-2"></i><?= lang('board.create_transfer') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="/transfers" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Transfer Type <span class="text-danger">*</span></label>
                            <select name="transfer_type_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($transferTypes as $tt): ?>
                                <option value="<?= $tt['id'] ?>"><?= e($tt['name_en']) ?> (<?= e($tt['type_code']) ?>)</option>
                                <?php endforeach; ?>
                            </select><div class="invalid-feedback" data-field="transfer_type_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Reason <span class="text-danger">*</span></label>
                            <select name="reason" class="form-select" required>
                                <?php foreach (['inheritance','divorce','gift','sale','court_order','board_decision','other'] as $r): ?>
                                <option value="<?= $r ?>"><?= ucfirst(str_replace('_', ' ', $r)) ?></option>
                                <?php endforeach; ?>
                            </select><div class="invalid-feedback" data-field="reason"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">From Member ID <span class="text-danger">*</span></label>
                            <input type="number" name="from_member_id" class="form-control" required placeholder="Enter member ID">
                            <div class="invalid-feedback" data-field="from_member_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Membership Type <span class="text-danger">*</span></label>
                            <select name="membership_type_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($membershipTypes as $mt): ?>
                                <option value="<?= $mt['id'] ?>"><?= e($mt['name_en']) ?></option>
                                <?php endforeach; ?>
                            </select><div class="invalid-feedback" data-field="membership_type_id"></div>
                        </div>
                        <hr>
                        <h6>Recipient Information</h6>
                        <div class="col-md-6">
                            <label class="form-label">Recipient Name <span class="text-danger">*</span></label>
                            <input type="text" name="to_applicant_name" class="form-control" required>
                            <div class="invalid-feedback" data-field="to_applicant_name"></div>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">National ID</label>
                            <input type="text" name="to_applicant_national_id" class="form-control">
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Phone</label>
                            <input type="text" name="to_applicant_phone" class="form-control">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Relationship to Original Member</label>
                            <input type="text" name="relationship_to_original" class="form-control" placeholder="e.g. Son, Spouse">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">New Membership Type (if different)</label>
                            <select name="new_membership_type_id" class="form-select">
                                <option value="">-- Same as original --</option>
                                <?php foreach ($membershipTypes as $mt): ?>
                                <option value="<?= $mt['id'] ?>"><?= e($mt['name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-12">
                            <label class="form-label">Reason Details</label>
                            <textarea name="reason_details" class="form-control" rows="3"></textarea>
                        </div>
                        <div class="col-12">
                            <label class="form-label">Notes</label>
                            <textarea name="notes" class="form-control" rows="2"></textarea>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/transfers" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\transfers\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-exchange-alt me-2"></i><?= lang('board.transfers') ?></h4>
    <?php if (auth_can('transfers.manage')): ?>
    <a href="/transfers/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card mb-3">
    <div class="card-body">
        <form class="row g-2" data-filter-table="transfersTable">
            <div class="col-md-3">
                <select name="status" class="form-select" onchange="DataTableHelper.reload('transfersTable')">
                    <option value="">All Statuses</option>
                    <?php foreach (['requested','documents_pending','under_review','board_pending','approved','rejected','completed','cancelled'] as $s): ?>
                    <option value="<?= $s ?>"><?= ucfirst(str_replace('_', ' ', $s)) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
        </form>
    </div>
</div>
<div class="card">
    <div class="card-body">
        <table id="transfersTable" class="table table-striped table-hover w-100"
               data-url="/transfers/datatable"
               data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"number","title":"Transfer #"},{"data":"from","title":"From"},{"data":"to","title":"To"},{"data":"type","title":"Type"},{"data":"reason","title":"Reason"},{"data":"status","title":"Status"},{"data":"date","title":"Date"},{"data":"actions","title":"Actions","orderable":false,"searchable":false}]'>
            <thead><tr><th>#</th><th>Transfer #</th><th>From</th><th>To</th><th>Type</th><th>Reason</th><th>Status</th><th>Date</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function(){ DataTableHelper.init('transfersTable'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\transfers\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= e($transfer['transfer_number']) ?> <?= status_badge($transfer['status']) ?></h4>
        <p class="text-muted mb-0"><?= e($transfer['type_name'] ?? '') ?> â€” <?= ucfirst(str_replace('_', ' ', $transfer['reason'])) ?></p>
    </div>
    <a href="/transfers" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back</a>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Transfer Details</h6></div>
            <div class="card-body">
                <table class="table table-sm table-borderless mb-0">
                    <tr><td class="text-muted">From</td><td><strong><?= e($transfer['from_name'] ?? '') ?></strong><br><small><?= e($transfer['from_number'] ?? '') ?></small></td></tr>
                    <tr><td class="text-muted">To</td><td><strong><?= e($transfer['to_name'] ?? $transfer['to_applicant_name'] ?? '') ?></strong><?php if ($transfer['to_applicant_national_id']): ?><br><small>NID: <?= e($transfer['to_applicant_national_id']) ?></small><?php endif; ?></td></tr>
                    <tr><td class="text-muted">Type</td><td><?= e($transfer['membership_type_name'] ?? '') ?></td></tr>
                    <tr><td class="text-muted">Fees</td><td><?= money($transfer['total_fees']) ?></td></tr>
                    <tr><td class="text-muted">Requested</td><td><?= format_date($transfer['request_date']) ?></td></tr>
                    <?php if ($transfer['transfer_date']): ?>
                    <tr><td class="text-muted">Completed</td><td><?= format_date($transfer['transfer_date']) ?></td></tr>
                    <?php endif; ?>
                </table>

                <?php if (auth_can('transfers.manage') && !in_array($transfer['status'], ['completed','cancelled'])): ?>
                <hr>
                <form method="POST" action="/transfers/<?= $transfer['id'] ?>/status" data-ajax="true" class="mb-2">
                    <?= csrf_field() ?>
                    <label class="form-label small">Change Status</label>
                    <select name="status" class="form-select form-select-sm mb-2">
                        <?php foreach (['requested','documents_pending','under_review','board_pending','cancelled'] as $s): ?>
                        <option value="<?= $s ?>" <?= selected($s, $transfer['status']) ?>><?= ucfirst(str_replace('_', ' ', $s)) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <input type="text" name="reason" class="form-control form-control-sm mb-2" placeholder="Reason (optional)">
                    <button type="submit" class="btn btn-sm btn-primary w-100">Update Status</button>
                </form>
                <?php endif; ?>

                <?php if (auth_can('transfers.approve') && $transfer['status'] === 'board_pending'): ?>
                <form method="POST" action="/transfers/<?= $transfer['id'] ?>/approve" data-ajax="true" class="mb-2">
                    <?= csrf_field() ?>
                    <button type="submit" class="btn btn-success w-100"><i class="fas fa-check me-1"></i>Approve Transfer</button>
                </form>
                <?php endif; ?>

                <?php if (auth_can('transfers.approve') && $transfer['status'] === 'approved'): ?>
                <form method="POST" action="/transfers/<?= $transfer['id'] ?>/complete" data-ajax="true">
                    <?= csrf_field() ?>
                    <button type="submit" class="btn btn-success w-100" onclick="return confirm('Complete transfer? This will terminate the original member.')"><i class="fas fa-flag-checkered me-1"></i>Complete Transfer</button>
                </form>
                <?php endif; ?>
            </div>
        </div>
    </div>

    <div class="col-md-8">
        <ul class="nav nav-tabs mb-3">
            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-docs">Documents</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-heirs">Heirs</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-history">History</a></li>
        </ul>
        <div class="tab-content">
            <div class="tab-pane fade show active" id="tab-docs">
                <?php if (auth_can('transfers.manage') && !in_array($transfer['status'], ['completed','cancelled'])): ?>
                <div class="card mb-3">
                    <div class="card-body">
                        <form method="POST" action="/transfers/<?= $transfer['id'] ?>/document" data-ajax="true" enctype="multipart/form-data">
                            <?= csrf_field() ?>
                            <div class="row g-2">
                                <div class="col-md-3"><input type="text" name="document_type" class="form-control" placeholder="Type (e.g. death_cert)" required></div>
                                <div class="col-md-4"><input type="text" name="document_name" class="form-control" placeholder="Document name" required></div>
                                <div class="col-md-3"><input type="file" name="document" class="form-control" required></div>
                                <div class="col-md-2"><button type="submit" class="btn btn-primary w-100"><i class="fas fa-upload"></i></button></div>
                            </div>
                        </form>
                    </div>
                </div>
                <?php endif; ?>
                <div class="card">
                    <div class="card-body p-0">
                        <table class="table table-sm mb-0">
                            <thead><tr><th>Type</th><th>Name</th><th>Size</th><th>Uploaded</th><th>Verified</th></tr></thead>
                            <tbody>
                            <?php foreach ($documents as $doc): ?>
                            <tr>
                                <td><span class="badge bg-secondary"><?= e($doc['document_type']) ?></span></td>
                                <td><?= e($doc['document_name']) ?></td>
                                <td><?= $doc['file_size_bytes'] ? round($doc['file_size_bytes'] / 1024, 1) . ' KB' : '-' ?></td>
                                <td><?= format_datetime($doc['uploaded_at']) ?></td>
                                <td><?= $doc['is_verified'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-clock text-warning"></i>' ?></td>
                            </tr>
                            <?php endforeach; ?>
                            <?php if (empty($documents)): ?><tr><td colspan="5" class="text-center text-muted py-3">No documents uploaded</td></tr><?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <div class="tab-pane fade" id="tab-heirs">
                <?php if (auth_can('transfers.manage') && $transfer['reason'] === 'inheritance'): ?>
                <div class="card mb-3">
                    <div class="card-body">
                        <form method="POST" action="/transfers/<?= $transfer['id'] ?>/heir" data-ajax="true">
                            <?= csrf_field() ?>
                            <div class="row g-2">
                                <div class="col-md-3"><input type="text" name="heir_name" class="form-control" placeholder="Heir name" required></div>
                                <div class="col-md-2"><input type="text" name="heir_national_id" class="form-control" placeholder="National ID"></div>
                                <div class="col-md-2"><input type="text" name="heir_phone" class="form-control" placeholder="Phone"></div>
                                <div class="col-md-2"><input type="text" name="relationship" class="form-control" placeholder="Relationship" required></div>
                                <div class="col-md-1"><input type="number" name="share_percentage" class="form-control" placeholder="%" value="100" step="0.01" required></div>
                                <div class="col-md-2"><button type="submit" class="btn btn-primary w-100"><i class="fas fa-plus me-1"></i>Add</button></div>
                            </div>
                        </form>
                    </div>
                </div>
                <?php endif; ?>
                <div class="card">
                    <div class="card-body p-0">
                        <table class="table table-sm mb-0">
                            <thead><tr><th>Name</th><th>Relationship</th><th>Share %</th><th>Phone</th><th>Primary</th><th>Consent</th><?php if (auth_can('transfers.manage')): ?><th></th><?php endif; ?></tr></thead>
                            <tbody>
                            <?php foreach ($heirs as $h): ?>
                            <tr>
                                <td><?= e($h['heir_name']) ?></td>
                                <td><?= e($h['relationship']) ?></td>
                                <td><?= format_number($h['share_percentage']) ?>%</td>
                                <td><?= e($h['heir_phone'] ?? '-') ?></td>
                                <td><?= $h['is_primary_heir'] ? '<i class="fas fa-star text-warning"></i>' : '' ?></td>
                                <td><?= $h['consent_given'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-danger"></i>' ?></td>
                                <?php if (auth_can('transfers.manage')): ?>
                                <td>
                                    <form method="POST" action="/transfers/<?= $transfer['id'] ?>/heir-delete" data-ajax="true" class="d-inline">
                                        <?= csrf_field() ?>
                                        <input type="hidden" name="heir_id" value="<?= $h['id'] ?>">
                                        <button type="submit" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button>
                                    </form>
                                </td>
                                <?php endif; ?>
                            </tr>
                            <?php endforeach; ?>
                            <?php if (empty($heirs)): ?><tr><td colspan="7" class="text-center text-muted py-3">No heirs recorded</td></tr><?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <div class="tab-pane fade" id="tab-history">
                <div class="card">
                    <div class="card-body p-0">
                        <table class="table table-sm mb-0">
                            <thead><tr><th>Date</th><th>From</th><th>To</th><th>By</th><th>Notes</th></tr></thead>
                            <tbody>
                            <?php foreach ($history as $h): ?>
                            <tr>
                                <td><?= format_datetime($h['changed_at']) ?></td>
                                <td><?= $h['from_status'] ? status_badge($h['from_status']) : '-' ?></td>
                                <td><?= status_badge($h['to_status']) ?></td>
                                <td><?= e($h['changed_by_name'] ?? '-') ?></td>
                                <td><?= e($h['notes'] ?? '') ?></td>
                            </tr>
                            <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\transfer-types\form.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= e($title) ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= e($formAction) ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-4">
                            <label class="form-label">Code <span class="text-danger">*</span></label>
                            <input type="text" name="type_code" class="form-control" value="<?= e($type['type_code'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="type_code"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="name_en" class="form-control" value="<?= e($type['name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="name_en"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                            <input type="text" name="name_ar" class="form-control" value="<?= e($type['name_ar'] ?? '') ?>" required dir="rtl">
                            <div class="invalid-feedback" data-field="name_ar"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Fee Type <span class="text-danger">*</span></label>
                            <select name="fee_type" class="form-select" required>
                                <option value="none" <?= selected('none', $type['fee_type'] ?? 'none') ?>>None</option>
                                <option value="fixed" <?= selected('fixed', $type['fee_type'] ?? '') ?>>Fixed</option>
                                <option value="percentage_of_subscription" <?= selected('percentage_of_subscription', $type['fee_type'] ?? '') ?>>% of Subscription</option>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Fee Amount</label>
                            <input type="number" name="fee_amount" class="form-control" value="<?= e($type['fee_amount'] ?? '0') ?>" step="0.01" min="0">
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Fee %</label>
                            <input type="number" name="fee_percentage" class="form-control" value="<?= e($type['fee_percentage'] ?? '') ?>" step="0.01" min="0" max="100">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Description (EN)</label>
                            <textarea name="description_en" class="form-control" rows="3"><?= e($type['description_en'] ?? '') ?></textarea>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Description (AR)</label>
                            <textarea name="description_ar" class="form-control" rows="3" dir="rtl"><?= e($type['description_ar'] ?? '') ?></textarea>
                        </div>
                        <div class="col-12">
                            <div class="form-check"><input type="checkbox" name="requires_board_approval" class="form-check-input" value="1" <?= checked(1, $type['requires_board_approval'] ?? 1) ?>><label class="form-check-label">Requires Board Approval</label></div>
                        </div>
                        <?php if ($type): ?>
                        <div class="col-12">
                            <div class="form-check"><input type="checkbox" name="is_active" class="form-check-input" value="1" <?= checked(1, $type['is_active'] ?? 1) ?>><label class="form-check-label">Active</label></div>
                        </div>
                        <?php endif; ?>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/transfer-types" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\board\views\transfer-types\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-list me-2"></i><?= lang('board.transfer_types') ?></h4>
    <a href="/transfer-types/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card">
    <div class="card-body">
        <table id="transferTypesTable" class="table table-striped table-hover w-100"
               data-url="/transfer-types/datatable"
               data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"code","title":"Code"},{"data":"name","title":"Name"},{"data":"fee_type","title":"Fee Type"},{"data":"fee_amount","title":"Fee"},{"data":"board_approval","title":"Board"},{"data":"status","title":"Status"},{"data":"actions","title":"Actions","orderable":false,"searchable":false}]'>
            <thead><tr><th>#</th><th>Code</th><th>Name</th><th>Fee Type</th><th>Fee</th><th>Board</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function(){ DataTableHelper.init('transferTypesTable'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\board\views\widgets\summary.php

```php
<?php
try {
    $db = Database::getInstance();
    $pendingTransfers = (int) $db->fetchColumn("SELECT COUNT(*) FROM membership_transfers WHERE status NOT IN ('completed','cancelled','rejected')");
    $upcomingMeeting = $db->fetchOne("SELECT meeting_number, meeting_date, title_en FROM board_meetings WHERE status = 'scheduled' AND meeting_date >= CURDATE() ORDER BY meeting_date ASC LIMIT 1");
    $activeOffers = (int) $db->fetchColumn("SELECT COUNT(*) FROM special_offers WHERE status = 'active' AND end_date >= CURDATE()");
} catch (\Exception $e) {
    $pendingTransfers = 0;
    $upcomingMeeting = null;
    $activeOffers = 0;
}
?>
<div class="card border-info h-100">
    <div class="card-header bg-transparent"><h6 class="mb-0"><i class="fas fa-landmark me-2 text-info"></i><?= lang('board.board_summary') ?></h6></div>
    <div class="card-body">
        <div class="mb-3">
            

---
```

## File: modules\cards\Controllers\AccessApiController.php

```php
<?php
class AccessApiController extends Controller
{
    public function pointsDatatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll(
            "SELECT ap.*, f.name_en AS facility_name_en, f.name_ar AS facility_name_ar
             FROM access_points ap
             LEFT JOIN facilities f ON ap.facility_id = f.id
             ORDER BY ap.name_en"
        );
        $this->json([
            'draw' => (int)$request->get('draw', 1),
            'recordsTotal' => count($rows),
            'recordsFiltered' => count($rows),
            'data' => $rows,
        ]);
    }

    public function rulesDatatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll(
            "SELECT acr.*, ap.name_en AS point_name_en, ap.name_ar AS point_name_ar,
                    mt.name_en AS memtype_name_en, mt.name_ar AS memtype_name_ar
             FROM access_control_rules acr
             JOIN access_points ap ON acr.access_point_id = ap.id
             LEFT JOIN membership_types mt ON acr.membership_type_id = mt.id
             ORDER BY acr.priority DESC, acr.id"
        );
        $this->json([
            'draw' => (int)$request->get('draw', 1),
            'recordsTotal' => count($rows),
            'recordsFiltered' => count($rows),
            'data' => $rows,
        ]);
    }

    public function logDatatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $dateFrom  = $request->get('date_from', date('Y-m-d'));
        $dateTo    = $request->get('date_to', date('Y-m-d'));
        $direction = $request->get('direction', '');
        $granted   = $request->get('granted', '');

        $where = "DATE(mal.access_time) BETWEEN ? AND ?";
        $params = [$dateFrom, $dateTo];

        if ($direction) {
            $where .= " AND mal.direction = ?";
            $params[] = $direction;
        }
        if ($granted !== '') {
            $where .= " AND mal.granted = ?";
            $params[] = (int)$granted;
        }
        if ($search) {
            $where .= " AND (m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.membership_number LIKE ? OR mc.card_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $totalFiltered = (int)$db->fetchColumn(
            "SELECT COUNT(*)
             FROM member_access_log mal
             LEFT JOIN members m ON mal.member_id = m.id
             LEFT JOIN member_cards mc ON mal.card_id = mc.id
             WHERE {$where}", $params
        );

        $rows = $db->fetchAll(
            "SELECT mal.id, mal.direction, mal.access_time, mal.access_method, mal.granted, mal.denial_reason,
                    ap.name_en AS point_name_en, ap.name_ar AS point_name_ar, ap.point_type,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar,
                    mc.card_number,
                    mt.name_en AS memtype_name_en
             FROM member_access_log mal
             JOIN access_points ap ON mal.access_point_id = ap.id
             LEFT JOIN members m ON mal.member_id = m.id
             LEFT JOIN member_cards mc ON mal.card_id = mc.id
             LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
             WHERE {$where}
             ORDER BY mal.access_time DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalFiltered,
            'recordsFiltered' => $totalFiltered,
            'data'            => $rows,
        ]);
    }

    public function recordAccess(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'access_point_id' => 'required|integer|exists:access_points,id',
            'member_id'       => 'nullable|integer',
            'card_id'         => 'nullable|integer',
            'guest_pass_id'   => 'nullable|integer',
            'direction'       => 'required|in:entry,exit',
            'access_method'   => 'required|in:card,barcode,qr,rfid,fingerprint,face,manual',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $db = $this->db();

        $granted = true;
        $denialReason = null;

        if ($data['member_id']) {
            $member = $db->fetchOne("SELECT id, status, membership_type_id FROM members WHERE id = ? AND is_deleted = 0", [$data['member_id']]);
            if (!$member) {
                $granted = false;
                $denialReason = 'Member not found';
            } elseif ($member['status'] !== 'active' && $member['status'] !== 'frozen') {
                $granted = false;
                $denialReason = 'Member status: ' . $member['status'];
            }
        }

        $id = $db->insert('member_access_log', [
            'access_point_id' => $data['access_point_id'],
            'member_id'       => $data['member_id'] ?? null,
            'card_id'         => $data['card_id'] ?? null,
            'guest_pass_id'   => $data['guest_pass_id'] ?? null,
            'direction'       => $data['direction'],
            'access_method'   => $data['access_method'],
            'granted'         => $granted ? 1 : 0,
            'denial_reason'   => $denialReason,
        ]);

        $this->json([
            'success' => true,
            'granted' => $granted,
            'denial_reason' => $denialReason,
            'message' => $granted ? 'Access granted' : "Access denied: {$denialReason}",
        ]);
    }
}
```

## File: modules\cards\Controllers\AccessController.php

```php
<?php
class AccessController extends Controller
{
    public function points(Request $request): void
    {
        $db = $this->db();
        $points = $db->fetchAll(
            "SELECT ap.*, f.name_en AS facility_name_en, f.name_ar AS facility_name_ar
             FROM access_points ap
             LEFT JOIN facilities f ON ap.facility_id = f.id
             ORDER BY ap.name_en"
        );
        $facilities = $db->fetchAll("SELECT id, name_en, name_ar FROM facilities WHERE is_active = 1 ORDER BY name_en");

        $this->view('access/points', [
            'title'       => lang('cards.access_points'),
            'breadcrumbs' => [
                ['label' => lang('cards.access_control'), 'url' => '/access/log'],
                ['label' => lang('cards.access_points')],
            ],
            'points'     => $points,
            'facilities' => $facilities,
        ]);
    }

    public function storePoint(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'point_code'    => 'required|string|max:20',
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'location'      => 'nullable|string|max:300',
            'facility_id'   => 'nullable|integer',
            'point_type'    => 'required|in:main_gate,facility_entrance,parking,pool,gym,court,other',
            'hardware_type' => 'required|in:turnstile,gate,door,barrier,scanner,manual',
            'direction'     => 'required|in:entry,exit,both',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_active'] = 1;

        $id = $this->db()->insert('access_points', $data);
        $this->audit('create', 'access_points', $id, "Access point created: {$data['name_en']}");

        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }

    public function updatePoint(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');

        $validator = new Validator($request->all(), [
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'location'      => 'nullable|string|max:300',
            'facility_id'   => 'nullable|integer',
            'point_type'    => 'required|in:main_gate,facility_entrance,parking,pool,gym,court,other',
            'hardware_type' => 'required|in:turnstile,gate,door,barrier,scanner,manual',
            'direction'     => 'required|in:entry,exit,both',
            'is_active'     => 'nullable|in:0,1',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_active'] = $data['is_active'] ?? 1;
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM access_points WHERE id = ?", [$id]);
        $db->update('access_points', $data, 'id = ?', [$id]);
        $this->audit('update', 'access_points', $id, "Access point updated", $old, $data);

        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }

    public function rules(Request $request): void
    {
        $db = $this->db();
        $rules = $db->fetchAll(
            "SELECT acr.*, ap.name_en AS point_name_en, ap.name_ar AS point_name_ar,
                    mt.name_en AS memtype_name_en, mt.name_ar AS memtype_name_ar
             FROM access_control_rules acr
             JOIN access_points ap ON acr.access_point_id = ap.id
             LEFT JOIN membership_types mt ON acr.membership_type_id = mt.id
             ORDER BY acr.priority DESC, acr.id"
        );
        $points = $db->fetchAll("SELECT id, name_en, name_ar FROM access_points WHERE is_active = 1 ORDER BY name_en");
        $membershipTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('access/rules', [
            'title'       => lang('cards.access_rules'),
            'breadcrumbs' => [
                ['label' => lang('cards.access_control'), 'url' => '/access/log'],
                ['label' => lang('cards.access_rules')],
            ],
            'rules'           => $rules,
            'points'          => $points,
            'membershipTypes' => $membershipTypes,
        ]);
    }

    public function storeRule(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'access_point_id'        => 'required|integer|exists:access_points,id',
            'rule_name'              => 'required|string|max:200',
            'membership_type_id'     => 'nullable|integer',
            'member_status_required' => 'required|string|max:50',
            'subscription_required'  => 'nullable|in:0,1',
            'card_required'          => 'nullable|in:0,1',
            'allow_dependents'       => 'nullable|in:0,1',
            'allow_guests'           => 'nullable|in:0,1',
            'max_daily_entries'      => 'nullable|integer|min:1',
            'priority'               => 'nullable|integer|min:0',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['subscription_required'] = $data['subscription_required'] ?? 1;
        $data['card_required']         = $data['card_required'] ?? 1;
        $data['allow_dependents']      = $data['allow_dependents'] ?? 1;
        $data['allow_guests']          = $data['allow_guests'] ?? 0;
        $data['priority']              = $data['priority'] ?? 0;
        $data['is_active']             = 1;

        $id = $this->db()->insert('access_control_rules', $data);
        $this->audit('create', 'access_control_rules', $id, "Access rule created: {$data['rule_name']}");

        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }

    public function updateRule(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'rule_name'              => 'required|string|max:200',
            'membership_type_id'     => 'nullable|integer',
            'member_status_required' => 'required|string|max:50',
            'subscription_required'  => 'nullable|in:0,1',
            'card_required'          => 'nullable|in:0,1',
            'allow_dependents'       => 'nullable|in:0,1',
            'allow_guests'           => 'nullable|in:0,1',
            'max_daily_entries'      => 'nullable|integer|min:1',
            'priority'               => 'nullable|integer|min:0',
            'is_active'              => 'nullable|in:0,1',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['subscription_required'] = $data['subscription_required'] ?? 1;
        $data['card_required']         = $data['card_required'] ?? 1;
        $data['allow_dependents']      = $data['allow_dependents'] ?? 1;
        $data['allow_guests']          = $data['allow_guests'] ?? 0;
        $data['is_active']             = $data['is_active'] ?? 1;

        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM access_control_rules WHERE id = ?", [$id]);
        $db->update('access_control_rules', $data, 'id = ?', [$id]);
        $this->audit('update', 'access_control_rules', $id, "Access rule updated", $old, $data);

        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }

    public function deleteRule(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM access_control_rules WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Rule not found'], 404);
            return;
        }

        $db->delete('access_control_rules', 'id = ?', [$id]);
        $this->audit('delete', 'access_control_rules', $id, "Access rule deleted: {$old['rule_name']}", $old);

        $this->json(['success' => true, 'message' => lang('common.delete') . ' âœ“']);
    }

    public function log(Request $request): void
    {
        $db = $this->db();
        $todayEntries = (int)$db->fetchColumn("SELECT COUNT(*) FROM member_access_log WHERE DATE(access_time) = CURDATE() AND direction = 'entry' AND granted = 1");
        $todayDenied  = (int)$db->fetchColumn("SELECT COUNT(*) FROM member_access_log WHERE DATE(access_time) = CURDATE() AND granted = 0");
        $todayExits   = (int)$db->fetchColumn("SELECT COUNT(*) FROM member_access_log WHERE DATE(access_time) = CURDATE() AND direction = 'exit' AND granted = 1");

        $this->view('access/log', [
            'title'       => lang('cards.access_log'),
            'breadcrumbs' => [
                ['label' => lang('cards.access_control'), 'url' => '/access/log'],
                ['label' => lang('cards.access_log')],
            ],
            'todayEntries' => $todayEntries,
            'todayDenied'  => $todayDenied,
            'todayExits'   => $todayExits,
        ]);
    }
}
```

## File: modules\cards\Controllers\AccessLogController.php

```php
<?php
require_once __DIR__ . '/BaseController.php';

class AccessLogController extends BaseController
{
    public function index(Request $request): void
    {
        $db = Database::getInstance();
        $todayEntries = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_access_log WHERE DATE(access_time) = CURDATE() AND direction = 'entry' AND granted = 1");
        $todayDenied = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_access_log WHERE DATE(access_time) = CURDATE() AND granted = 0");
        $accessPoints = $db->fetchAll("SELECT id, name_en FROM access_points WHERE is_active = 1 ORDER BY name_en");

        $this->view('access-log/index', [
            'title' => lang('cards.access_log'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_log')]],
            'todayEntries' => $todayEntries,
            'todayDenied' => $todayDenied,
            'accessPoints' => $accessPoints,
        ]);
    }

    public function datatable(Request $request): void
    {
        $pointId = $request->get('filter_point', '');
        $direction = $request->get('filter_direction', '');
        $dateFrom = $request->get('filter_date_from', '');
        $dateTo = $request->get('filter_date_to', '');

        $where = "WHERE 1=1";
        $params = [];
        if ($pointId) { $where .= " AND mal.access_point_id = ?"; $params[] = $pointId; }
        if ($direction) { $where .= " AND mal.direction = ?"; $params[] = $direction; }
        if ($dateFrom) { $where .= " AND DATE(mal.access_time) >= ?"; $params[] = $dateFrom; }
        if ($dateTo) { $where .= " AND DATE(mal.access_time) <= ?"; $params[] = $dateTo; }
        if (!$dateFrom && !$dateTo) { $where .= " AND DATE(mal.access_time) = CURDATE()"; }

        $baseQuery = "SELECT mal.id, mal.direction, mal.access_time, mal.access_method, mal.granted, mal.denial_reason,
                       ap.name_en AS point_name,
                       m.membership_number, m.full_name_en AS member_name,
                       mc.card_number
                       FROM member_access_log mal
                       LEFT JOIN access_points ap ON mal.access_point_id = ap.id
                       LEFT JOIN members m ON mal.member_id = m.id
                       LEFT JOIN member_cards mc ON mal.card_id = mc.id
                       {$where}";

        $columns = [
            ['db' => 'id', 'searchable' => false],
            ['db' => 'access_time', 'searchable' => false],
            ['db' => 'direction', 'searchable' => false],
            ['db' => 'member_name', 'searchable' => true],
            ['db' => 'membership_number', 'searchable' => true],
            ['db' => 'point_name', 'searchable' => true],
            ['db' => 'access_method', 'searchable' => false],
            ['db' => 'granted', 'searchable' => false],
            ['db' => 'card_number', 'searchable' => true],
        ];

        $this->handleDataTable($request, $baseQuery, $params, $columns, 'mal.access_time DESC');
    }

    public function record(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $validator = new Validator($request->all(), [
            'access_point_id' => 'required|integer|exists:access_points,id',
            'direction' => 'required|in:entry,exit',
            'access_method' => 'required|in:card,barcode,qr,rfid,fingerprint,face,manual',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $db = Database::getInstance();
        $memberId = $request->post('member_id') ?: null;
        $cardId = $request->post('card_id') ?: null;
        $guestPassId = $request->post('guest_pass_id') ?: null;
        $granted = 1;
        $denialReason = null;

        // Validate access if member
        if ($memberId) {
            $member = $db->fetchOne("SELECT status FROM members WHERE id = ? AND is_deleted = 0", [$memberId]);
            if (!$member || $member['status'] !== 'active') {
                $granted = 0;
                $denialReason = 'Member not active (status: ' . ($member['status'] ?? 'not found') . ')';
            }
        }

        $id = $db->insert('member_access_log', [
            'access_point_id' => $request->post('access_point_id'),
            'member_id' => $memberId,
            'dependent_id' => $request->post('dependent_id') ?: null,
            'card_id' => $cardId,
            'guest_pass_id' => $guestPassId,
            'direction' => $request->post('direction'),
            'access_method' => $request->post('access_method'),
            'granted' => $granted,
            'denial_reason' => $denialReason,
            'notes' => $request->post('notes', ''),
        ]);

        $this->json(['success' => true, 'message' => $granted ? lang('cards.access_granted') : lang('cards.access_denied'), 'granted' => (bool) $granted]);
    }
}
```

## File: modules\cards\Controllers\AccessPointController.php

```php
<?php
require_once __DIR__ . '/BaseController.php';

class AccessPointController extends BaseController
{
    public function index(Request $request): void
    {
        $this->view('access-points/index', [
            'title' => lang('cards.access_points'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_points')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $baseQuery = "SELECT ap.id, ap.point_code, ap.name_en, ap.name_ar, ap.location, ap.point_type, ap.hardware_type, ap.direction, ap.is_active,
                       f.name_en AS facility_name
                       FROM access_points ap
                       LEFT JOIN facilities f ON ap.facility_id = f.id
                       WHERE 1=1";
        $columns = [
            ['db' => 'id', 'searchable' => false],
            ['db' => 'point_code', 'searchable' => true],
            ['db' => 'name_en', 'searchable' => true],
            ['db' => 'location', 'searchable' => true],
            ['db' => 'point_type', 'searchable' => true],
            ['db' => 'is_active', 'searchable' => false],
        ];
        $this->handleDataTable($request, $baseQuery, [], $columns, 'ap.id ASC');
    }

    public function create(Request $request): void
    {
        $db = Database::getInstance();
        $facilities = $db->fetchAll("SELECT id, name_en, name_ar FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $this->view('access-points/form', [
            'title' => lang('cards.create_access_point'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_points'), 'url' => '/access-points'], ['label' => lang('common.create')]],
            'point' => null, 'formAction' => '/access-points', 'facilities' => $facilities,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'point_code' => 'required|string|max:20|unique:access_points,point_code',
            'name_en' => 'required|string|max:200', 'name_ar' => 'required|string|max:200',
            'point_type' => 'required|in:main_gate,facility_entrance,parking,pool,gym,court,other',
            'hardware_type' => 'required|in:turnstile,gate,door,barrier,scanner,manual',
            'direction' => 'required|in:entry,exit,both',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['facility_id'] = $request->post('facility_id') ?: null;
        $data['location'] = $request->post('location', '');
        $data['hardware_id'] = $request->post('hardware_id', '');
        $data['is_active'] = $request->post('is_active') ? 1 : 0;
        $data['created_at'] = date('Y-m-d H:i:s');

        $db = Database::getInstance();
        $id = $db->insert('access_points', $data);
        AuditLog::log('create', 'cards', 'access_points', $id, "Access point {$data['point_code']} created");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/access-points']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $point = $db->fetchOne("SELECT * FROM access_points WHERE id = ?", [$id]);
        if (!$point) { $this->abort(404, 'Not found'); return; }
        $facilities = $db->fetchAll("SELECT id, name_en, name_ar FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $this->view('access-points/form', [
            'title' => lang('cards.edit_access_point'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_points'), 'url' => '/access-points'], ['label' => lang('common.edit')]],
            'point' => $point, 'formAction' => '/access-points/' . $id, 'facilities' => $facilities,
        ]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $validator = new Validator($request->all(), [
            'point_code' => "required|string|max:20|unique:access_points,point_code,{$id}",
            'name_en' => 'required|string|max:200', 'name_ar' => 'required|string|max:200',
            'point_type' => 'required|in:main_gate,facility_entrance,parking,pool,gym,court,other',
            'hardware_type' => 'required|in:turnstile,gate,door,barrier,scanner,manual',
            'direction' => 'required|in:entry,exit,both',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['facility_id'] = $request->post('facility_id') ?: null;
        $data['location'] = $request->post('location', '');
        $data['hardware_id'] = $request->post('hardware_id', '');
        $data['is_active'] = $request->post('is_active') ? 1 : 0;

        $db = Database::getInstance();
        $db->update('access_points', $data, 'id = ?', [$id]);
        AuditLog::log('update', 'cards', 'access_points', $id, "Access point updated");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/access-points']);
    }
}
```

## File: modules\cards\Controllers\AccessRuleController.php

```php
<?php
require_once __DIR__ . '/BaseController.php';

class AccessRuleController extends BaseController
{
    public function index(Request $request): void
    {
        $this->view('access-rules/index', [
            'title' => lang('cards.access_rules'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_rules')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $baseQuery = "SELECT acr.id, acr.rule_name, acr.member_status_required, acr.subscription_required,
                       acr.card_required, acr.allow_dependents, acr.allow_guests, acr.max_daily_entries, acr.priority, acr.is_active,
                       ap.name_en AS point_name, mt.name_en AS type_name
                       FROM access_control_rules acr
                       LEFT JOIN access_points ap ON acr.access_point_id = ap.id
                       LEFT JOIN membership_types mt ON acr.membership_type_id = mt.id
                       WHERE 1=1";
        $columns = [
            ['db' => 'id', 'searchable' => false],
            ['db' => 'rule_name', 'searchable' => true],
            ['db' => 'point_name', 'searchable' => true],
            ['db' => 'type_name', 'searchable' => true],
            ['db' => 'priority', 'searchable' => false],
            ['db' => 'is_active', 'searchable' => false],
        ];
        $this->handleDataTable($request, $baseQuery, [], $columns, 'acr.priority ASC');
    }

    public function create(Request $request): void
    {
        $db = Database::getInstance();
        $this->view('access-rules/form', [
            'title' => lang('cards.create_access_rule'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_rules'), 'url' => '/access-rules'], ['label' => lang('common.create')]],
            'rule' => null, 'formAction' => '/access-rules',
            'accessPoints' => $db->fetchAll("SELECT id, name_en FROM access_points WHERE is_active = 1 ORDER BY name_en"),
            'membershipTypes' => $db->fetchAll("SELECT id, name_en FROM membership_types WHERE is_active = 1 ORDER BY name_en"),
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'access_point_id' => 'required|integer|exists:access_points,id',
            'rule_name' => 'required|string|max:200',
            'member_status_required' => 'required|string|max:50',
            'priority' => 'required|integer|min:0',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['membership_type_id'] = $request->post('membership_type_id') ?: null;
        $data['subscription_required'] = $request->post('subscription_required') ? 1 : 0;
        $data['card_required'] = $request->post('card_required') ? 1 : 0;
        $data['allow_dependents'] = $request->post('allow_dependents') ? 1 : 0;
        $data['allow_guests'] = $request->post('allow_guests') ? 1 : 0;
        $data['max_daily_entries'] = $request->post('max_daily_entries') ?: null;
        $data['allowed_time_start'] = $request->post('allowed_time_start') ?: null;
        $data['allowed_time_end'] = $request->post('allowed_time_end') ?: null;
        $data['allowed_days'] = $request->post('allowed_days') ? json_encode($request->post('allowed_days')) : null;
        $data['is_active'] = $request->post('is_active') ? 1 : 0;
        $data['created_at'] = date('Y-m-d H:i:s');

        $db = Database::getInstance();
        $id = $db->insert('access_control_rules', $data);
        AuditLog::log('create', 'cards', 'access_control_rules', $id, "Access rule created");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/access-rules']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $rule = $db->fetchOne("SELECT * FROM access_control_rules WHERE id = ?", [$id]);
        if (!$rule) { $this->abort(404, 'Not found'); return; }
        $this->view('access-rules/form', [
            'title' => lang('cards.edit_access_rule'),
            'breadcrumbs' => [['label' => lang('cards.access_control')], ['label' => lang('cards.access_rules'), 'url' => '/access-rules'], ['label' => lang('common.edit')]],
            'rule' => $rule, 'formAction' => '/access-rules/' . $id,
            'accessPoints' => $db->fetchAll("SELECT id, name_en FROM access_points WHERE is_active = 1 ORDER BY name_en"),
            'membershipTypes' => $db->fetchAll("SELECT id, name_en FROM membership_types WHERE is_active = 1 ORDER BY name_en"),
        ]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $data = [
            'access_point_id' => $request->post('access_point_id'),
            'rule_name' => $request->post('rule_name'),
            'membership_type_id' => $request->post('membership_type_id') ?: null,
            'member_status_required' => $request->post('member_status_required', 'active'),
            'subscription_required' => $request->post('subscription_required') ? 1 : 0,
            'card_required' => $request->post('card_required') ? 1 : 0,
            'allow_dependents' => $request->post('allow_dependents') ? 1 : 0,
            'allow_guests' => $request->post('allow_guests') ? 1 : 0,
            'max_daily_entries' => $request->post('max_daily_entries') ?: null,
            'allowed_time_start' => $request->post('allowed_time_start') ?: null,
            'allowed_time_end' => $request->post('allowed_time_end') ?: null,
            'allowed_days' => $request->post('allowed_days') ? json_encode($request->post('allowed_days')) : null,
            'priority' => (int) $request->post('priority', 0),
            'is_active' => $request->post('is_active') ? 1 : 0,
        ];
        $db = Database::getInstance();
        $db->update('access_control_rules', $data, 'id = ?', [$id]);
        AuditLog::log('update', 'cards', 'access_control_rules', $id, "Access rule updated");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/access-rules']);
    }

    public function delete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $db->delete('access_control_rules', 'id = ?', [$id]);
        AuditLog::log('delete', 'cards', 'access_control_rules', $id, "Access rule deleted");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }
}
```

## File: modules\cards\Controllers\BaseController.php

```php
<?php

class BaseController extends Controller
{
    protected function handleDataTable(Request $request, string $baseQuery, array $baseParams, array $columns, ?string $defaultOrder = null): void
    {
        $db = Database::getInstance();
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $searchValue = trim($request->get('search')['value'] ?? '');

        // Total count
        $countSql = "SELECT COUNT(*) FROM ({$baseQuery}) AS total_count";
        $totalRecords = (int) $db->fetchColumn($countSql, $baseParams);

        // Search
        $filteredQuery = $baseQuery;
        $filteredParams = $baseParams;
        if ($searchValue !== '') {
            $searchClauses = [];
            foreach ($columns as $col) {
                if (!empty($col['searchable'])) {
                    $searchClauses[] = "{$col['db']} LIKE ?";
                    $filteredParams[] = "%{$searchValue}%";
                }
            }
            if (!empty($searchClauses)) {
                $filteredQuery = "SELECT * FROM ({$baseQuery}) AS sub WHERE (" . implode(' OR ', $searchClauses) . ")";
            }
        }

        $filteredCountSql = "SELECT COUNT(*) FROM ({$filteredQuery}) AS filtered_count";
        $filteredRecords = (int) $db->fetchColumn($filteredCountSql, $filteredParams);

        // Ordering
        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtoupper($request->get('order')[0]['dir'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
        $orderColumn = $columns[$orderCol]['db'] ?? $columns[0]['db'];
        if ($defaultOrder && $searchValue === '') {
            $orderClause = $defaultOrder;
        } else {
            $orderClause = "{$orderColumn} {$orderDir}";
        }

        $dataSql = "{$filteredQuery} ORDER BY {$orderClause} LIMIT " . (int)$length . " OFFSET " . (int)$start;
        $rows = $db->fetchAll($dataSql, $filteredParams);

        $this->json([
            'draw' => $draw,
            'recordsTotal' => $totalRecords,
            'recordsFiltered' => $filteredRecords,
            'data' => $rows,
        ]);
    }

    protected function memberSearchResults(Request $request): void
    {
        $q = trim($request->get('q', ''));
        if (mb_strlen($q) < 2) {
            $this->json(['results' => []]);
            return;
        }
        $db = Database::getInstance();
        $members = $db->fetchAll(
            "SELECT id, membership_number, full_name_en, full_name_ar, primary_phone, photo_path
             FROM members
             WHERE is_deleted = 0 AND (
                 membership_number LIKE ? OR full_name_en LIKE ? OR full_name_ar LIKE ?
                 OR national_id LIKE ? OR primary_phone LIKE ?
             )
             ORDER BY full_name_en LIMIT 20",
            ["%{$q}%", "%{$q}%", "%{$q}%", "%{$q}%", "%{$q}%"]
        );
        $results = [];
        foreach ($members as $m) {
            $results[] = [
                'id' => $m['id'],
                'text' => $m['membership_number'] . ' - ' . ($m['full_name_en'] ?: $m['full_name_ar']),
                'membership_number' => $m['membership_number'],
                'name' => $m['full_name_en'],
                'phone' => $m['primary_phone'],
            ];
        }
        $this->json(['results' => $results]);
    }
}
```

## File: modules\cards\Controllers\CardApiController.php

```php
<?php
class CardApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $statusFilter = $request->get('status', '');
        $typeFilter   = $request->get('card_type_id', '');

        $where = "1=1";
        $params = [];

        if ($statusFilter) {
            $where .= " AND mc.status = ?";
            $params[] = $statusFilter;
        }
        if ($typeFilter) {
            $where .= " AND mc.card_type_id = ?";
            $params[] = (int)$typeFilter;
        }
        if ($search) {
            $where .= " AND (mc.card_number LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $totalAll = (int)$db->fetchColumn("SELECT COUNT(*) FROM member_cards mc LEFT JOIN members m ON mc.member_id = m.id");
        $totalFiltered = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM member_cards mc LEFT JOIN members m ON mc.member_id = m.id WHERE {$where}", $params
        );

        $orderCol = $request->get('order')[0]['column'] ?? 0;
        $orderDir = ($request->get('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';
        $cols = ['mc.id', 'mc.card_number', 'm.full_name_en', 'ct.name_en', 'mc.issue_date', 'mc.expiry_date', 'mc.status'];
        $orderBy = ($cols[$orderCol] ?? 'mc.id') . ' ' . $orderDir;

        $rows = $db->fetchAll(
            "SELECT mc.id, mc.card_number, mc.status, mc.issue_date, mc.expiry_date,
                    ct.name_en AS type_name_en, ct.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar
             FROM member_cards mc
             JOIN card_types ct ON mc.card_type_id = ct.id
             LEFT JOIN members m ON mc.member_id = m.id
             WHERE {$where}
             ORDER BY {$orderBy}
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalFiltered,
            'data'            => $rows,
        ]);
    }

    public function requestsDatatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status', '');

        $where = "1=1";
        $params = [];

        if ($statusFilter) {
            $where .= " AND cr.status = ?";
            $params[] = $statusFilter;
        }
        if ($search) {
            $where .= " AND (cr.request_number LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $totalAll = (int)$db->fetchColumn("SELECT COUNT(*) FROM card_requests cr LEFT JOIN members m ON cr.member_id = m.id");
        $totalFiltered = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM card_requests cr LEFT JOIN members m ON cr.member_id = m.id WHERE {$where}", $params
        );

        $rows = $db->fetchAll(
            "SELECT cr.id, cr.request_number, cr.request_type, cr.status, cr.fee_amount, cr.fee_paid, cr.created_at,
                    ct.name_en AS type_name_en, ct.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar
             FROM card_requests cr
             JOIN card_types ct ON cr.card_type_id = ct.id
             LEFT JOIN members m ON cr.member_id = m.id
             WHERE {$where}
             ORDER BY cr.id DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalFiltered,
            'data'            => $rows,
        ]);
    }

    public function typesList(Request $request): void
    {
        $db = $this->db();
        $types = $db->fetchAll("SELECT id, type_code, name_en, name_ar, fee, replacement_fee, applicable_to, is_active FROM card_types ORDER BY name_en");
        $this->json(['success' => true, 'data' => $types]);
    }

    public function printDatatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);

        $totalAll = (int)$db->fetchColumn("SELECT COUNT(*) FROM card_print_batches");

        $rows = $db->fetchAll(
            "SELECT cpb.*, u.full_name_en AS printed_by_name
             FROM card_print_batches cpb
             LEFT JOIN auth_users u ON cpb.printed_by = u.id
             ORDER BY cpb.id DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start
        );

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalAll,
            'data'            => $rows,
        ]);
    }

    public function batchItems(Request $request): void
    {
        $batchId = (int)$request->routeParam('id');
        $db = $this->db();
        $items = $db->fetchAll(
            "SELECT cbi.*, mc.card_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar, m.membership_number
             FROM card_print_batch_items cbi
             LEFT JOIN member_cards mc ON cbi.card_id = mc.id
             LEFT JOIN members m ON mc.member_id = m.id
             WHERE cbi.batch_id = ?
             ORDER BY cbi.id", [$batchId]
        );
        $this->json(['success' => true, 'data' => $items]);
    }
}
```

## File: modules\cards\Controllers\CardController.php

```php
<?php
class CardController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $stats = [
            'total_active'   => (int)$db->fetchColumn("SELECT COUNT(*) FROM member_cards WHERE status='active'"),
            'expiring_30'    => (int)$db->fetchColumn("SELECT COUNT(*) FROM member_cards WHERE status='active' AND expiry_date IS NOT NULL AND expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)"),
            'lost_stolen'    => (int)$db->fetchColumn("SELECT COUNT(*) FROM member_cards WHERE status IN ('lost','stolen')"),
            'expired'        => (int)$db->fetchColumn("SELECT COUNT(*) FROM member_cards WHERE status='expired'"),
        ];

        $this->view('cards/index', [
            'title'       => lang('cards.all_cards'),
            'breadcrumbs' => [
                ['label' => lang('cards.module'), 'url' => '/cards'],
                ['label' => lang('cards.all_cards')],
            ],
            'stats' => $stats,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $card = $db->fetchOne(
            "SELECT mc.*, ct.name_en AS type_name_en, ct.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar,
                    m.primary_phone, m.photo_path AS member_photo,
                    md.full_name_en AS dep_name_en, md.full_name_ar AS dep_name_ar
             FROM member_cards mc
             JOIN card_types ct ON mc.card_type_id = ct.id
             LEFT JOIN members m ON mc.member_id = m.id
             LEFT JOIN member_dependents md ON mc.dependent_id = md.id
             WHERE mc.id = ?", [$id]
        );

        if (!$card) {
            $this->abort(404, 'Card not found');
        }

        $accessLog = $db->fetchAll(
            "SELECT mal.*, ap.name_en AS point_name_en, ap.name_ar AS point_name_ar
             FROM member_access_log mal
             LEFT JOIN access_points ap ON mal.access_point_id = ap.id
             WHERE mal.card_id = ?
             ORDER BY mal.access_time DESC LIMIT 20", [$id]
        );

        $replacementHistory = $db->fetchAll(
            "SELECT * FROM member_cards WHERE replaced_by_card_id = ? OR replaces_card_id = ? ORDER BY created_at DESC",
            [$id, $id]
        );

        $this->view('cards/show', [
            'title'       => lang('cards.card_details'),
            'breadcrumbs' => [
                ['label' => lang('cards.module'), 'url' => '/cards'],
                ['label' => $card['card_number']],
            ],
            'card'        => $card,
            'accessLog'   => $accessLog,
            'history'     => $replacementHistory,
        ]);
    }

    public function invalidate(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $reason = trim($request->post('reason', ''));
        $newStatus = $request->post('new_status', 'cancelled');

        if (!in_array($newStatus, ['lost', 'stolen', 'damaged', 'cancelled', 'suspended'])) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 422);
            return;
        }

        if (empty($reason)) {
            $this->json(['success' => false, 'errors' => ['reason' => ['Reason is required']]], 422);
            return;
        }

        $db = $this->db();
        $card = $db->fetchOne("SELECT * FROM member_cards WHERE id = ? AND status = 'active'", [$id]);

        if (!$card) {
            $this->json(['success' => false, 'message' => 'Active card not found'], 404);
            return;
        }

        $db->update('member_cards', [
            'status'            => $newStatus,
            'status_reason'     => $reason,
            'status_changed_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('update', 'member_cards', $id, "Card invalidated: {$newStatus}", $card, ['status' => $newStatus, 'status_reason' => $reason]);

        $this->json(['success' => true, 'message' => lang('cards.card_invalidated')]);
    }
}
```

## File: modules\cards\Controllers\CardPrintController.php

```php
<?php
class CardPrintController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('cards/print_batches', [
            'title'       => lang('cards.print_batches'),
            'breadcrumbs' => [
                ['label' => lang('cards.module'), 'url' => '/cards'],
                ['label' => lang('cards.print_batches')],
            ],
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $db = $this->db();

        $pendingRequests = $db->fetchAll(
            "SELECT cr.id AS request_id, cr.resulting_card_id
             FROM card_requests cr
             WHERE cr.status = 'approved' AND cr.resulting_card_id IS NOT NULL
             ORDER BY cr.id"
        );

        if (empty($pendingRequests)) {
            $this->json(['success' => false, 'message' => lang('cards.no_cards_to_print')]);
            return;
        }

        $batchNumber = SequenceGenerator::next('print_batch_number');

        $batchId = $db->insert('card_print_batches', [
            'batch_number' => $batchNumber,
            'batch_date'   => date('Y-m-d'),
            'total_cards'  => count($pendingRequests),
            'status'       => 'pending',
        ]);

        foreach ($pendingRequests as $pr) {
            $db->insert('card_print_batch_items', [
                'batch_id'        => $batchId,
                'card_request_id' => $pr['request_id'],
                'card_id'         => $pr['resulting_card_id'],
                'print_status'    => 'pending',
            ]);

            $db->update('card_requests', ['status' => 'in_production'], 'id = ?', [$pr['request_id']]);
        }

        $this->audit('create', 'card_print_batches', $batchId, "Print batch created: {$batchNumber} with " . count($pendingRequests) . " cards");

        $this->json(['success' => true, 'message' => lang('cards.batch_created'), 'redirect' => '/cards/print-batches']);
    }

    public function process(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $batchId = (int)$request->routeParam('id');
        $db = $this->db();

        $batch = $db->fetchOne("SELECT * FROM card_print_batches WHERE id = ? AND status IN ('pending','partial')", [$batchId]);
        if (!$batch) {
            $this->json(['success' => false, 'message' => 'Batch not found or already completed'], 404);
            return;
        }

        $items = $db->fetchAll("SELECT * FROM card_print_batch_items WHERE batch_id = ? AND print_status = 'pending'", [$batchId]);

        $printedCount = 0;
        $failedCount = 0;

        foreach ($items as $item) {
            $db->update('card_print_batch_items', ['print_status' => 'printed'], 'id = ?', [$item['id']]);

            if ($item['card_id']) {
                $db->update('member_cards', [
                    'print_count'     => $db->fetchColumn("SELECT print_count FROM member_cards WHERE id = ?", [$item['card_id']]) + 1,
                    'last_printed_at' => date('Y-m-d H:i:s'),
                ], 'id = ?', [$item['card_id']]);
            }

            if ($item['card_request_id']) {
                $db->update('card_requests', [
                    'status'      => 'ready',
                    'produced_at' => date('Y-m-d H:i:s'),
                ], 'id = ?', [$item['card_request_id']]);
            }

            $printedCount++;
        }

        $totalPrinted = (int)$db->fetchColumn("SELECT COUNT(*) FROM card_print_batch_items WHERE batch_id = ? AND print_status = 'printed'", [$batchId]);
        $totalFailed  = (int)$db->fetchColumn("SELECT COUNT(*) FROM card_print_batch_items WHERE batch_id = ? AND print_status = 'failed'", [$batchId]);

        $status = 'completed';
        if ($totalFailed > 0 && $totalPrinted > 0) $status = 'partial';
        if ($totalPrinted === 0) $status = 'cancelled';

        $db->update('card_print_batches', [
            'printed_cards' => $totalPrinted,
            'failed_cards'  => $totalFailed,
            'status'        => $status,
            'printed_by'    => Auth::id(),
            'printed_at'    => date('Y-m-d H:i:s'),
        ], 'id = ?', [$batchId]);

        $this->audit('update', 'card_print_batches', $batchId, "Print batch processed: {$totalPrinted} printed, {$totalFailed} failed");

        $this->json(['success' => true, 'message' => lang('cards.batch_processed')]);
    }
}
```

## File: modules\cards\Controllers\CardRequestController.php

```php
<?php
class CardRequestController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('cards/requests', [
            'title'       => lang('cards.card_requests'),
            'breadcrumbs' => [
                ['label' => lang('cards.module'), 'url' => '/cards'],
                ['label' => lang('cards.card_requests')],
            ],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $cardTypes = $db->fetchAll("SELECT id, type_code, name_en, name_ar, fee, replacement_fee, applicable_to FROM card_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('cards/request_create', [
            'title'       => lang('cards.new_request'),
            'breadcrumbs' => [
                ['label' => lang('cards.module'), 'url' => '/cards'],
                ['label' => lang('cards.card_requests'), 'url' => '/cards/requests'],
                ['label' => lang('cards.new_request')],
            ],
            'cardTypes' => $cardTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'member_id'    => 'required|integer|exists:members,id',
            'card_type_id' => 'required|integer|exists:card_types,id',
            'request_type' => 'required|in:new,replacement,renewal',
            'reason'       => 'nullable|string|max:500',
            'old_card_id'  => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $db = $this->db();

        $cardType = $db->fetchOne("SELECT * FROM card_types WHERE id = ?", [$data['card_type_id']]);
        $feeAmount = ($data['request_type'] === 'replacement') ? (float)$cardType['replacement_fee'] : (float)$cardType['fee'];

        $requestNumber = SequenceGenerator::next('card_request_number');

        $id = $db->insert('card_requests', [
            'request_number' => $requestNumber,
            'member_id'      => $data['member_id'],
            'card_type_id'   => $data['card_type_id'],
            'request_type'   => $data['request_type'],
            'reason'         => $data['reason'] ?? null,
            'old_card_id'    => $data['old_card_id'] ?? null,
            'status'         => 'pending',
            'fee_amount'     => $feeAmount,
            'fee_paid'       => 0,
            'created_by'     => Auth::id(),
        ]);

        if ($data['request_type'] === 'replacement' && !empty($data['old_card_id'])) {
            $db->update('member_cards', [
                'status'            => 'replaced',
                'status_reason'     => 'Replacement requested: ' . $requestNumber,
                'status_changed_at' => date('Y-m-d H:i:s'),
            ], 'id = ?', [$data['old_card_id']]);
        }

        $this->audit('create', 'card_requests', $id, "Card request created: {$requestNumber}");

        $this->json([
            'success'  => true,
            'message'  => lang('cards.request_created'),
            'redirect' => '/cards/requests',
        ]);
    }

    public function approve(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $cardReq = $db->fetchOne("SELECT * FROM card_requests WHERE id = ? AND status = 'pending'", [$id]);
        if (!$cardReq) {
            $this->json(['success' => false, 'message' => 'Request not found or not pending'], 404);
            return;
        }

        $cardNumber = SequenceGenerator::next('card_number');
        $cardType = $db->fetchOne("SELECT * FROM card_types WHERE id = ?", [$cardReq['card_type_id']]);
        $expiryDate = $cardType['validity_months'] ? date('Y-m-d', strtotime("+{$cardType['validity_months']} months")) : null;

        $member = $db->fetchOne("SELECT photo_path FROM members WHERE id = ?", [$cardReq['member_id']]);

        $cardId = $db->insert('member_cards', [
            'card_number'      => $cardNumber,
            'card_type_id'     => $cardReq['card_type_id'],
            'member_id'        => $cardReq['member_id'],
            'dependent_id'     => $cardReq['dependent_id'],
            'barcode'          => $cardNumber,
            'qr_code_data'     => json_encode(['card' => $cardNumber, 'member' => $cardReq['member_id']]),
            'issue_date'       => date('Y-m-d'),
            'expiry_date'      => $expiryDate,
            'status'           => 'active',
            'photo_path'       => $member['photo_path'] ?? null,
            'replaces_card_id' => $cardReq['old_card_id'],
            'created_by'       => Auth::id(),
        ]);

        if ($cardReq['old_card_id']) {
            $db->update('member_cards', ['replaced_by_card_id' => $cardId], 'id = ?', [$cardReq['old_card_id']]);
        }

        $db->update('card_requests', [
            'status'            => 'approved',
            'approved_by'       => Auth::id(),
            'approved_at'       => date('Y-m-d H:i:s'),
            'resulting_card_id' => $cardId,
        ], 'id = ?', [$id]);

        $this->audit('update', 'card_requests', $id, "Card request approved, card {$cardNumber} created");

        $this->json(['success' => true, 'message' => lang('cards.request_approved')]);
    }

    public function reject(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $reason = trim($request->post('reason', ''));
        $db = $this->db();

        $cardReq = $db->fetchOne("SELECT * FROM card_requests WHERE id = ? AND status = 'pending'", [$id]);
        if (!$cardReq) {
            $this->json(['success' => false, 'message' => 'Request not found or not pending'], 404);
            return;
        }

        $db->update('card_requests', [
            'status' => 'rejected',
            'notes'  => $reason,
        ], 'id = ?', [$id]);

        $this->audit('update', 'card_requests', $id, "Card request rejected: {$reason}");

        $this->json(['success' => true, 'message' => lang('cards.request_rejected')]);
    }

    public function deliver(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $deliveredTo = trim($request->post('delivered_to', ''));
        $db = $this->db();

        $cardReq = $db->fetchOne("SELECT * FROM card_requests WHERE id = ? AND status IN ('approved','in_production','ready')", [$id]);
        if (!$cardReq) {
            $this->json(['success' => false, 'message' => 'Request not found or not ready for delivery'], 404);
            return;
        }

        $db->update('card_requests', [
            'status'       => 'delivered',
            'delivered_at' => date('Y-m-d H:i:s'),
            'delivered_to' => $deliveredTo ?: null,
        ], 'id = ?', [$id]);

        $this->audit('update', 'card_requests', $id, "Card delivered to: {$deliveredTo}");

        $this->json(['success' => true, 'message' => lang('cards.card_delivered')]);
    }
}
```

## File: modules\cards\Controllers\CardTypeController.php

```php
<?php
class CardTypeController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $types = $db->fetchAll("SELECT * FROM card_types ORDER BY name_en");

        $this->view('cards/types', [
            'title'       => lang('cards.card_types'),
            'breadcrumbs' => [
                ['label' => lang('cards.module'), 'url' => '/cards'],
                ['label' => lang('cards.card_types')],
            ],
            'types' => $types,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'type_code'       => 'required|string|max:20',
            'name_en'         => 'required|string|max:100',
            'name_ar'         => 'required|string|max:100',
            'applicable_to'   => 'required|in:member,dependent,guest,staff',
            'validity_months' => 'nullable|integer|min:1',
            'fee'             => 'required|decimal|min:0',
            'replacement_fee' => 'required|decimal|min:0',
            'has_photo'       => 'nullable|in:0,1',
            'has_barcode'     => 'nullable|in:0,1',
            'has_qr_code'     => 'nullable|in:0,1',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['has_photo']   = $data['has_photo'] ?? 1;
        $data['has_barcode'] = $data['has_barcode'] ?? 1;
        $data['has_qr_code'] = $data['has_qr_code'] ?? 0;
        $data['is_active']   = 1;

        $db = $this->db();
        $id = $db->insert('card_types', $data);

        $this->audit('create', 'card_types', $id, "Card type created: {$data['name_en']}");
        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');

        $validator = new Validator($request->all(), [
            'name_en'         => 'required|string|max:100',
            'name_ar'         => 'required|string|max:100',
            'applicable_to'   => 'required|in:member,dependent,guest,staff',
            'validity_months' => 'nullable|integer|min:1',
            'fee'             => 'required|decimal|min:0',
            'replacement_fee' => 'required|decimal|min:0',
            'has_photo'       => 'nullable|in:0,1',
            'has_barcode'     => 'nullable|in:0,1',
            'has_qr_code'     => 'nullable|in:0,1',
            'is_active'       => 'nullable|in:0,1',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['has_photo']   = $data['has_photo'] ?? 1;
        $data['has_barcode'] = $data['has_barcode'] ?? 1;
        $data['has_qr_code'] = $data['has_qr_code'] ?? 0;
        $data['is_active']   = $data['is_active'] ?? 1;

        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM card_types WHERE id = ?", [$id]);
        $db->update('card_types', $data, 'id = ?', [$id]);

        $this->audit('update', 'card_types', $id, "Card type updated", $old, $data);
        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }
}
```

## File: modules\cards\Controllers\CarnetApiController.php

```php
<?php
class CarnetApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status', '');

        $where = "1=1";
        $params = [];

        if ($statusFilter) {
            $where .= " AND c.status = ?";
            $params[] = $statusFilter;
        }
        if ($search) {
            $where .= " AND (c.carnet_number LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $totalAll = (int)$db->fetchColumn("SELECT COUNT(*) FROM carnets c LEFT JOIN members m ON c.member_id = m.id");
        $totalFiltered = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM carnets c LEFT JOIN members m ON c.member_id = m.id WHERE {$where}", $params
        );

        $rows = $db->fetchAll(
            "SELECT c.id, c.carnet_number, c.purchase_date, c.expiry_date, c.total_entries, c.used_entries, c.remaining_entries, c.status, c.price_paid,
                    ct.name_en AS type_name_en, ct.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar
             FROM carnets c
             JOIN carnet_types ct ON c.carnet_type_id = ct.id
             JOIN members m ON c.member_id = m.id
             WHERE {$where}
             ORDER BY c.id DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalFiltered,
            'data'            => $rows,
        ]);
    }

    public function typesDatatable(Request $request): void
    {
        $rows = $this->db()->fetchAll("SELECT * FROM carnet_types ORDER BY name_en");
        $this->json([
            'draw'            => (int)$request->get('draw', 1),
            'recordsTotal'    => count($rows),
            'recordsFiltered' => count($rows),
            'data'            => $rows,
        ]);
    }

    public function entries(Request $request): void
    {
        $carnetId = (int)$request->routeParam('id');
        $entries = $this->db()->fetchAll(
            "SELECT ce.*, f.name_en AS facility_name_en, f.name_ar AS facility_name_ar, u.full_name_en AS recorded_by_name
             FROM carnet_entries ce
             LEFT JOIN facilities f ON ce.facility_id = f.id
             LEFT JOIN auth_users u ON ce.recorded_by = u.id
             WHERE ce.carnet_id = ?
             ORDER BY ce.entry_date DESC, ce.entry_time DESC", [$carnetId]
        );
        $this->json(['success' => true, 'data' => $entries]);
    }
}
```

## File: modules\cards\Controllers\CarnetController.php

```php
<?php
class CarnetController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('carnets/index', [
            'title'       => lang('cards.all_carnets'),
            'breadcrumbs' => [
                ['label' => lang('cards.carnets'), 'url' => '/carnets'],
                ['label' => lang('cards.all_carnets')],
            ],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $carnetTypes = $db->fetchAll("SELECT * FROM carnet_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('carnets/create', [
            'title'       => lang('cards.issue_carnet'),
            'breadcrumbs' => [
                ['label' => lang('cards.carnets'), 'url' => '/carnets'],
                ['label' => lang('cards.issue_carnet')],
            ],
            'carnetTypes' => $carnetTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'carnet_type_id' => 'required|integer|exists:carnet_types,id',
            'member_id'      => 'required|integer|exists:members,id',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $db = $this->db();

        $carnetType = $db->fetchOne("SELECT * FROM carnet_types WHERE id = ?", [$data['carnet_type_id']]);
        $carnetNumber = SequenceGenerator::next('carnet_number');

        $expiryDate = null;
        if ($carnetType['validity_days']) {
            $expiryDate = date('Y-m-d', strtotime("+{$carnetType['validity_days']} days"));
        }

        $id = $db->insert('carnets', [
            'carnet_number'    => $carnetNumber,
            'carnet_type_id'   => $data['carnet_type_id'],
            'member_id'        => $data['member_id'],
            'purchase_date'    => date('Y-m-d'),
            'activation_date'  => date('Y-m-d'),
            'expiry_date'      => $expiryDate,
            'total_entries'    => $carnetType['max_entries'],
            'used_entries'     => 0,
            'remaining_entries'=> $carnetType['max_entries'],
            'status'           => 'active',
            'price_paid'       => $carnetType['price'],
            'created_by'       => Auth::id(),
        ]);

        $this->audit('create', 'carnets', $id, "Carnet issued: {$carnetNumber}");

        $this->json([
            'success'  => true,
            'message'  => lang('cards.carnet_issued'),
            'redirect' => '/carnets/show/' . $id,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $carnet = $db->fetchOne(
            "SELECT c.*, ct.name_en AS type_name_en, ct.name_ar AS type_name_ar, ct.max_entries AS type_max,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar, m.primary_phone
             FROM carnets c
             JOIN carnet_types ct ON c.carnet_type_id = ct.id
             JOIN members m ON c.member_id = m.id
             WHERE c.id = ?", [$id]
        );

        if (!$carnet) {
            $this->abort(404, 'Carnet not found');
        }

        $entries = $db->fetchAll(
            "SELECT ce.*, f.name_en AS facility_name_en, f.name_ar AS facility_name_ar
             FROM carnet_entries ce
             LEFT JOIN facilities f ON ce.facility_id = f.id
             WHERE ce.carnet_id = ?
             ORDER BY ce.entry_date DESC, ce.entry_time DESC", [$id]
        );

        $this->view('carnets/show', [
            'title'       => lang('cards.carnet_details'),
            'breadcrumbs' => [
                ['label' => lang('cards.carnets'), 'url' => '/carnets'],
                ['label' => $carnet['carnet_number']],
            ],
            'carnet'  => $carnet,
            'entries' => $entries,
        ]);
    }

    public function recordEntry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $carnet = $db->fetchOne("SELECT * FROM carnets WHERE id = ? AND status = 'active'", [$id]);
        if (!$carnet) {
            $this->json(['success' => false, 'message' => lang('cards.carnet_not_active')], 422);
            return;
        }

        if ($carnet['remaining_entries'] <= 0) {
            $this->json(['success' => false, 'message' => lang('cards.carnet_exhausted')], 422);
            return;
        }

        if ($carnet['expiry_date'] && $carnet['expiry_date'] < date('Y-m-d')) {
            $this->json(['success' => false, 'message' => lang('cards.carnet_expired')], 422);
            return;
        }

        $facilityId = $request->post('facility_id') ? (int)$request->post('facility_id') : null;
        $entryNumber = $carnet['used_entries'] + 1;

        $entryId = $db->insert('carnet_entries', [
            'carnet_id'    => $id,
            'entry_number' => $entryNumber,
            'entry_date'   => date('Y-m-d'),
            'entry_time'   => date('H:i:s'),
            'facility_id'  => $facilityId,
            'used_by_member_id' => $carnet['member_id'],
            'recorded_by'  => Auth::id(),
            'notes'        => $request->post('notes', ''),
        ]);

        // The DB trigger handles updating used_entries/remaining_entries on carnets table

        $this->audit('create', 'carnet_entries', $entryId, "Carnet entry #{$entryNumber} recorded for carnet {$carnet['carnet_number']}");

        $this->json(['success' => true, 'message' => lang('cards.entry_recorded') . " (#{$entryNumber})"]);
    }

    public function voidEntry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $carnetId = (int)$request->routeParam('id');
        $entryId  = (int)$request->routeParam('entryId');
        $reason   = trim($request->post('reason', ''));

        $db = $this->db();
        $entry = $db->fetchOne("SELECT * FROM carnet_entries WHERE id = ? AND carnet_id = ? AND is_voided = 0", [$entryId, $carnetId]);

        if (!$entry) {
            $this->json(['success' => false, 'message' => 'Entry not found or already voided'], 404);
            return;
        }

        $db->update('carnet_entries', [
            'is_voided'   => 1,
            'voided_by'   => Auth::id(),
            'voided_at'   => date('Y-m-d H:i:s'),
            'void_reason' => $reason,
        ], 'id = ?', [$entryId]);

        // The DB trigger handles restoring the count on carnets table

        $this->audit('update', 'carnet_entries', $entryId, "Carnet entry voided: {$reason}");

        $this->json(['success' => true, 'message' => lang('cards.entry_voided')]);
    }

    public function types(Request $request): void
    {
        $db = $this->db();
        $types = $db->fetchAll("SELECT * FROM carnet_types ORDER BY name_en");

        $this->view('carnets/types', [
            'title'       => lang('cards.carnet_types'),
            'breadcrumbs' => [
                ['label' => lang('cards.carnets'), 'url' => '/carnets'],
                ['label' => lang('cards.carnet_types')],
            ],
            'types' => $types,
        ]);
    }

    public function storeType(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'type_code'     => 'required|string|max:20',
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'max_entries'   => 'required|integer|min:1',
            'validity_days' => 'nullable|integer|min:1',
            'price'         => 'required|decimal|min:0',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_active'] = 1;

        $id = $this->db()->insert('carnet_types', $data);
        $this->audit('create', 'carnet_types', $id, "Carnet type created: {$data['name_en']}");

        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }

    public function updateType(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'max_entries'   => 'required|integer|min:1',
            'validity_days' => 'nullable|integer|min:1',
            'price'         => 'required|decimal|min:0',
            'is_active'     => 'nullable|in:0,1',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_active'] = $data['is_active'] ?? 1;
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM carnet_types WHERE id = ?", [$id]);
        $db->update('carnet_types', $data, 'id = ?', [$id]);
        $this->audit('update', 'carnet_types', $id, "Carnet type updated", $old, $data);

        $this->json(['success' => true, 'message' => lang('common.save') . ' âœ“']);
    }
}
```

## File: modules\cards\Controllers\CarnetTypeController.php

```php
<?php
require_once __DIR__ . '/BaseController.php';

class CarnetTypeController extends BaseController
{
    public function index(Request $request): void
    {
        $this->view('carnet-types/index', [
            'title' => lang('cards.carnet_types'),
            'breadcrumbs' => [['label' => lang('cards.carnets'), 'url' => '/carnets'], ['label' => lang('cards.carnet_types')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $baseQuery = "SELECT id, type_code, name_en, name_ar, max_entries, validity_days, price, is_active FROM carnet_types WHERE 1=1";
        $columns = [
            ['db' => 'id', 'searchable' => false], ['db' => 'type_code', 'searchable' => true],
            ['db' => 'name_en', 'searchable' => true], ['db' => 'max_entries', 'searchable' => false],
            ['db' => 'validity_days', 'searchable' => false], ['db' => 'price', 'searchable' => false],
            ['db' => 'is_active', 'searchable' => false],
        ];
        $this->handleDataTable($request, $baseQuery, [], $columns, 'id ASC');
    }

    public function create(Request $request): void
    {
        $this->view('carnet-types/form', [
            'title' => lang('cards.create_carnet_type'), 'carnetType' => null, 'formAction' => '/carnet-types',
            'breadcrumbs' => [['label' => lang('cards.carnets'), 'url' => '/carnets'], ['label' => lang('cards.carnet_types'), 'url' => '/carnet-types'], ['label' => lang('common.create')]],
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'type_code' => 'required|string|max:20|unique:carnet_types,type_code',
            'name_en' => 'required|string|max:200', 'name_ar' => 'required|string|max:200',
            'max_entries' => 'required|integer|min:1', 'price' => 'required|decimal|min:0',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['validity_days'] = $request->post('validity_days') ?: null;
        $data['is_transferable'] = $request->post('is_transferable') ? 1 : 0;
        $data['is_active'] = $request->post('is_active', 1) ? 1 : 0;
        $data['created_at'] = date('Y-m-d H:i:s');

        $db = Database::getInstance();
        $id = $db->insert('carnet_types', $data);
        AuditLog::log('create', 'cards', 'carnet_types', $id, "Carnet type created");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/carnet-types']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $ct = $db->fetchOne("SELECT * FROM carnet_types WHERE id = ?", [$id]);
        if (!$ct) { $this->abort(404, 'Not found'); return; }
        $this->view('carnet-types/form', [
            'title' => lang('cards.edit_carnet_type'), 'carnetType' => $ct, 'formAction' => '/carnet-types/' . $id,
            'breadcrumbs' => [['label' => lang('cards.carnets'), 'url' => '/carnets'], ['label' => lang('cards.carnet_types'), 'url' => '/carnet-types'], ['label' => lang('common.edit')]],
        ]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $validator = new Validator($request->all(), [
            'type_code' => "required|string|max:20|unique:carnet_types,type_code,{$id}",
            'name_en' => 'required|string|max:200', 'name_ar' => 'required|string|max:200',
            'max_entries' => 'required|integer|min:1', 'price' => 'required|decimal|min:0',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['validity_days'] = $request->post('validity_days') ?: null;
        $data['is_transferable'] = $request->post('is_transferable') ? 1 : 0;
        $data['is_active'] = $request->post('is_active', 1) ? 1 : 0;

        $db = Database::getInstance();
        $db->update('carnet_types', $data, 'id = ?', [$id]);
        AuditLog::log('update', 'cards', 'carnet_types', $id, "Carnet type updated");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/carnet-types']);
    }
}
```

## File: modules\cards\Controllers\GuestApiController.php

```php
<?php
class GuestApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status', '');
        $dateFrom = $request->get('date_from', '');
        $dateTo   = $request->get('date_to', '');

        $where = "1=1";
        $params = [];

        if ($statusFilter) {
            $where .= " AND gp.status = ?";
            $params[] = $statusFilter;
        }
        if ($dateFrom) {
            $where .= " AND gp.valid_date >= ?";
            $params[] = $dateFrom;
        }
        if ($dateTo) {
            $where .= " AND gp.valid_date <= ?";
            $params[] = $dateTo;
        }
        if ($search) {
            $where .= " AND (gp.pass_number LIKE ? OR gp.guest_name LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s, $s]);
        }

        $totalAll = (int)$db->fetchColumn("SELECT COUNT(*) FROM guest_passes gp LEFT JOIN members m ON gp.host_member_id = m.id");
        $totalFiltered = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM guest_passes gp LEFT JOIN members m ON gp.host_member_id = m.id WHERE {$where}", $params
        );

        $rows = $db->fetchAll(
            "SELECT gp.id, gp.pass_number, gp.guest_name, gp.guest_phone, gp.valid_date, gp.status, gp.fee_amount, gp.check_in_time, gp.check_out_time,
                    gpt.name_en AS type_name_en, gpt.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS host_name_en, m.full_name_ar AS host_name_ar
             FROM guest_passes gp
             JOIN guest_pass_types gpt ON gp.pass_type_id = gpt.id
             JOIN members m ON gp.host_member_id = m.id
             WHERE {$where}
             ORDER BY gp.id DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalFiltered,
            'data'            => $rows,
        ]);
    }

    public function blacklistDatatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll(
            "SELECT gb.*, u.full_name_en AS blacklisted_by_name
             FROM guest_blacklist gb
             LEFT JOIN auth_users u ON gb.blacklisted_by = u.id
             WHERE gb.status = 'active'
             ORDER BY gb.created_at DESC"
        );

        $this->json([
            'draw'            => (int)$request->get('draw', 1),
            'recordsTotal'    => count($rows),
            'recordsFiltered' => count($rows),
            'data'            => $rows,
        ]);
    }

    public function checkBlacklist(Request $request): void
    {
        $nationalId = $request->get('national_id', '');
        $name = $request->get('name', '');
        $db = $this->db();

        $where = "status = 'active' AND (expires_at IS NULL OR expires_at >= CURDATE())";
        $params = [];

        if ($nationalId) {
            $where .= " AND guest_national_id = ?";
            $params[] = $nationalId;
        } elseif ($name) {
            $where .= " AND guest_name LIKE ?";
            $params[] = "%{$name}%";
        } else {
            $this->json(['success' => true, 'blacklisted' => false]);
            return;
        }

        $result = $this->db()->fetchOne("SELECT * FROM guest_blacklist WHERE {$where} LIMIT 1", $params);

        $this->json([
            'success'     => true,
            'blacklisted' => $result !== null,
            'data'        => $result,
        ]);
    }

    public function passTypes(Request $request): void
    {
        $types = $this->db()->fetchAll("SELECT * FROM guest_pass_types WHERE is_active = 1 ORDER BY name_en");
        $this->json(['success' => true, 'data' => $types]);
    }
}
```

## File: modules\cards\Controllers\GuestBlacklistController.php

```php
<?php
require_once __DIR__ . '/BaseController.php';

class GuestBlacklistController extends BaseController
{
    public function index(Request $request): void
    {
        $this->view('guest-blacklist/index', [
            'title' => lang('cards.guest_blacklist'),
            'breadcrumbs' => [['label' => lang('cards.guests')], ['label' => lang('cards.guest_blacklist')]],
        ]);
    }

    public function datatable(Request $request): void
    {
        $baseQuery = "SELECT id, guest_name, guest_national_id, guest_phone, reason, blacklisted_date, expires_at, status FROM guest_blacklist WHERE 1=1";
        $columns = [
            ['db' => 'id', 'searchable' => false],
            ['db' => 'guest_name', 'searchable' => true],
            ['db' => 'guest_national_id', 'searchable' => true],
            ['db' => 'guest_phone', 'searchable' => true],
            ['db' => 'reason', 'searchable' => true],
            ['db' => 'blacklisted_date', 'searchable' => false],
            ['db' => 'status', 'searchable' => true],
        ];
        $this->handleDataTable($request, $baseQuery, [], $columns, 'id DESC');
    }

    public function create(Request $request): void
    {
        $this->view('guest-blacklist/create', [
            'title' => lang('cards.add_to_blacklist'),
            'breadcrumbs' => [['label' => lang('cards.guests')], ['label' => lang('cards.guest_blacklist'), 'url' => '/guest-blacklist'], ['label' => lang('common.create')]],
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'guest_name' => 'required|string|max:300',
            'reason' => 'required|string',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $db = Database::getInstance();
        $id = $db->insert('guest_blacklist', [
            'guest_name' => $request->post('guest_name'),
            'guest_national_id' => $request->post('guest_national_id') ?: null,
            'guest_phone' => $request->post('guest_phone') ?: null,
            'reason' => $request->post('reason'),
            'blacklisted_date' => date('Y-m-d'),
            'blacklisted_by' => Auth::id(),
            'expires_at' => $request->post('expires_at') ?: null,
            'status' => 'active',
            'created_at' => date('Y-m-d H:i:s'),
        ]);
        AuditLog::log('create', 'cards', 'guest_blacklist', $id, "Guest blacklisted: " . $request->post('guest_name'));
        $this->json(['success' => true, 'message' => lang('cards.blacklist_added'), 'redirect' => '/guest-blacklist']);
    }

    public function remove(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $db->update('guest_blacklist', [
            'status' => 'removed',
            'removed_by' => Auth::id(),
            'removed_at' => date('Y-m-d H:i:s'),
            'removal_reason' => $request->post('reason', ''),
        ], 'id = ?', [$id]);
        AuditLog::log('update', 'cards', 'guest_blacklist', $id, "Removed from blacklist");
        $this->json(['success' => true, 'message' => lang('cards.blacklist_removed')]);
    }
}
```

## File: modules\cards\Controllers\GuestController.php

```php
<?php
class GuestController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $stats = [
            'today_active'  => (int)$db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE valid_date = CURDATE() AND status IN ('issued','active')"),
            'today_used'    => (int)$db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE valid_date = CURDATE() AND status = 'used'"),
            'month_total'   => (int)$db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE MONTH(valid_date) = MONTH(CURDATE()) AND YEAR(valid_date) = YEAR(CURDATE())"),
        ];

        $this->view('guests/index', [
            'title'       => lang('cards.guest_passes'),
            'breadcrumbs' => [
                ['label' => lang('cards.guests'), 'url' => '/guests'],
                ['label' => lang('cards.guest_passes')],
            ],
            'stats' => $stats,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $passTypes = $db->fetchAll("SELECT * FROM guest_pass_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('guests/create', [
            'title'       => lang('cards.issue_pass'),
            'breadcrumbs' => [
                ['label' => lang('cards.guests'), 'url' => '/guests'],
                ['label' => lang('cards.issue_pass')],
            ],
            'passTypes' => $passTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'pass_type_id'     => 'required|integer|exists:guest_pass_types,id',
            'host_member_id'   => 'required|integer|exists:members,id',
            'guest_name'       => 'required|string|max:300',
            'guest_phone'      => 'nullable|string|max:20',
            'guest_national_id'=> 'nullable|string|max:20',
            'guest_gender'     => 'nullable|in:male,female',
            'valid_date'       => 'required|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $db = $this->db();

        // Check blacklist
        if (!empty($data['guest_national_id'])) {
            $blacklisted = $db->fetchOne(
                "SELECT id FROM guest_blacklist WHERE guest_national_id = ? AND status = 'active' AND (expires_at IS NULL OR expires_at >= CURDATE())",
                [$data['guest_national_id']]
            );
            if ($blacklisted) {
                $this->json(['success' => false, 'message' => lang('cards.guest_blacklisted')], 422);
                return;
            }
        }

        // Check guest policy
        $member = $db->fetchOne("SELECT id, membership_type_id, status FROM members WHERE id = ? AND is_deleted = 0", [$data['host_member_id']]);
        if (!$member || $member['status'] !== 'active') {
            $this->json(['success' => false, 'message' => lang('cards.host_not_active')], 422);
            return;
        }

        $policy = $db->fetchOne(
            "SELECT * FROM guest_policy_rules WHERE (membership_type_id = ? OR membership_type_id IS NULL) AND (guest_pass_type_id = ? OR guest_pass_type_id IS NULL) AND is_active = 1 ORDER BY membership_type_id DESC, guest_pass_type_id DESC LIMIT 1",
            [$member['membership_type_id'], $data['pass_type_id']]
        );

        if ($policy) {
            if ($policy['max_visits_per_month']) {
                $monthCount = (int)$db->fetchColumn(
                    "SELECT COUNT(*) FROM guest_passes WHERE host_member_id = ? AND MONTH(valid_date) = MONTH(?) AND YEAR(valid_date) = YEAR(?)",
                    [$data['host_member_id'], $data['valid_date'], $data['valid_date']]
                );
                if ($monthCount >= $policy['max_visits_per_month']) {
                    $this->json(['success' => false, 'message' => lang('cards.guest_monthly_limit')], 422);
                    return;
                }
            }

            if ($policy['same_guest_max_visits_per_month'] && !empty($data['guest_national_id'])) {
                $sameGuestMonth = (int)$db->fetchColumn(
                    "SELECT COUNT(*) FROM guest_passes WHERE host_member_id = ? AND guest_national_id = ? AND MONTH(valid_date) = MONTH(?) AND YEAR(valid_date) = YEAR(?)",
                    [$data['host_member_id'], $data['guest_national_id'], $data['valid_date'], $data['valid_date']]
                );
                if ($sameGuestMonth >= $policy['same_guest_max_visits_per_month']) {
                    $this->json(['success' => false, 'message' => lang('cards.same_guest_limit')], 422);
                    return;
                }
            }
        }

        $passType = $db->fetchOne("SELECT * FROM guest_pass_types WHERE id = ?", [$data['pass_type_id']]);
        $passNumber = SequenceGenerator::next('guest_pass_number');

        $id = $db->insert('guest_passes', [
            'pass_number'       => $passNumber,
            'pass_type_id'      => $data['pass_type_id'],
            'host_member_id'    => $data['host_member_id'],
            'guest_name'        => $data['guest_name'],
            'guest_phone'       => $data['guest_phone'] ?? null,
            'guest_national_id' => $data['guest_national_id'] ?? null,
            'guest_gender'      => $data['guest_gender'] ?? null,
            'valid_date'        => $data['valid_date'],
            'fee_amount'        => $passType['fee'],
            'fee_paid'          => 0,
            'status'            => 'issued',
            'created_by'        => Auth::id(),
        ]);

        $this->audit('create', 'guest_passes', $id, "Guest pass issued: {$passNumber} for {$data['guest_name']}");

        $this->json([
            'success'  => true,
            'message'  => lang('cards.pass_issued'),
            'redirect' => '/guests',
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $pass = $db->fetchOne(
            "SELECT gp.*, gpt.name_en AS type_name_en, gpt.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS host_name_en, m.full_name_ar AS host_name_ar, m.primary_phone AS host_phone
             FROM guest_passes gp
             JOIN guest_pass_types gpt ON gp.pass_type_id = gpt.id
             JOIN members m ON gp.host_member_id = m.id
             WHERE gp.id = ?", [$id]
        );

        if (!$pass) {
            $this->abort(404, 'Guest pass not found');
        }

        $visits = $db->fetchAll("SELECT * FROM guest_visits WHERE guest_pass_id = ? ORDER BY visit_date DESC", [$id]);

        $this->view('guests/show', [
            'title'       => lang('cards.pass_details'),
            'breadcrumbs' => [
                ['label' => lang('cards.guests'), 'url' => '/guests'],
                ['label' => $pass['pass_number']],
            ],
            'pass'   => $pass,
            'visits' => $visits,
        ]);
    }

    public function checkin(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $pass = $db->fetchOne("SELECT * FROM guest_passes WHERE id = ? AND status IN ('issued','active') AND valid_date = CURDATE()", [$id]);
        if (!$pass) {
            $this->json(['success' => false, 'message' => lang('cards.pass_not_valid')], 422);
            return;
        }

        $db->update('guest_passes', [
            'status'        => 'active',
            'check_in_time' => date('Y-m-d H:i:s'),
            'checked_in_by' => Auth::id(),
        ], 'id = ?', [$id]);

        $db->insert('guest_visits', [
            'guest_pass_id' => $id,
            'visit_date'    => date('Y-m-d'),
            'check_in'      => date('Y-m-d H:i:s'),
            'host_present'  => 1,
            'recorded_by'   => Auth::id(),
        ]);

        $this->audit('update', 'guest_passes', $id, "Guest checked in: {$pass['pass_number']}");
        $this->json(['success' => true, 'message' => lang('cards.guest_checked_in')]);
    }

    public function checkout(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $pass = $db->fetchOne("SELECT * FROM guest_passes WHERE id = ? AND status = 'active'", [$id]);
        if (!$pass) {
            $this->json(['success' => false, 'message' => lang('cards.pass_not_active')], 422);
            return;
        }

        $db->update('guest_passes', [
            'status'         => 'used',
            'check_out_time' => date('Y-m-d H:i:s'),
            'checked_out_by' => Auth::id(),
        ], 'id = ?', [$id]);

        $visit = $db->fetchOne("SELECT id FROM guest_visits WHERE guest_pass_id = ? ORDER BY id DESC LIMIT 1", [$id]);
        if ($visit) {
            $db->update('guest_visits', ['check_out' => date('Y-m-d H:i:s')], 'id = ?', [$visit['id']]);
        }

        $this->audit('update', 'guest_passes', $id, "Guest checked out: {$pass['pass_number']}");
        $this->json(['success' => true, 'message' => lang('cards.guest_checked_out')]);
    }

    public function cancel(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $pass = $db->fetchOne("SELECT * FROM guest_passes WHERE id = ? AND status IN ('issued','active')", [$id]);
        if (!$pass) {
            $this->json(['success' => false, 'message' => 'Pass not found or cannot be cancelled'], 404);
            return;
        }

        $db->update('guest_passes', ['status' => 'cancelled'], 'id = ?', [$id]);
        $this->audit('update', 'guest_passes', $id, "Guest pass cancelled: {$pass['pass_number']}");

        $this->json(['success' => true, 'message' => lang('cards.pass_cancelled')]);
    }

    public function blacklist(Request $request): void
    {
        $this->view('guests/blacklist', [
            'title'       => lang('cards.guest_blacklist'),
            'breadcrumbs' => [
                ['label' => lang('cards.guests'), 'url' => '/guests'],
                ['label' => lang('cards.guest_blacklist')],
            ],
        ]);
    }

    public function storeBlacklist(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'guest_name'        => 'required|string|max:300',
            'guest_national_id' => 'nullable|string|max:20',
            'guest_phone'       => 'nullable|string|max:20',
            'reason'            => 'required|string|max:1000',
            'expires_at'        => 'nullable|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $id = $this->db()->insert('guest_blacklist', [
            'guest_name'        => $data['guest_name'],
            'guest_national_id' => $data['guest_national_id'] ?? null,
            'guest_phone'       => $data['guest_phone'] ?? null,
            'reason'            => $data['reason'],
            'blacklisted_date'  => date('Y-m-d'),
            'blacklisted_by'    => Auth::id(),
            'expires_at'        => $data['expires_at'] ?? null,
            'status'            => 'active',
        ]);

        $this->audit('create', 'guest_blacklist', $id, "Guest blacklisted: {$data['guest_name']}");
        $this->json(['success' => true, 'message' => lang('cards.blacklist_added')]);
    }

    public function removeBlacklist(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $reason = trim($request->post('removal_reason', ''));
        $db = $this->db();

        $db->update('guest_blacklist', [
            'status'          => 'removed',
            'removed_by'      => Auth::id(),
            'removed_at'      => date('Y-m-d H:i:s'),
            'removal_reason'  => $reason,
        ], 'id = ?', [$id]);

        $this->audit('update', 'guest_blacklist', $id, "Removed from blacklist");
        $this->json(['success' => true, 'message' => lang('cards.blacklist_removed')]);
    }
}
```

## File: modules\cards\Controllers\GuestPassController.php

```php
<?php
require_once __DIR__ . '/BaseController.php';

class GuestPassController extends BaseController
{
    public function index(Request $request): void
    {
        $db = Database::getInstance();
        $stats = [
            'active_today' => (int) $db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE valid_date = CURDATE() AND status IN ('issued','active')"),
            'issued_month' => (int) $db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE MONTH(valid_date) = MONTH(CURDATE()) AND YEAR(valid_date) = YEAR(CURDATE())"),
            'checked_in' => (int) $db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE valid_date = CURDATE() AND status = 'active' AND check_in_time IS NOT NULL AND check_out_time IS NULL"),
        ];
        $passTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM guest_pass_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('guests/index', [
            'title' => lang('cards.guest_passes'),
            'breadcrumbs' => [['label' => lang('cards.guests')], ['label' => lang('cards.guest_passes')]],
            'stats' => $stats,
            'passTypes' => $passTypes,
        ]);
    }

    public function datatable(Request $request): void
    {
        $status = $request->get('filter_status', '');
        $dateFrom = $request->get('filter_date_from', '');
        $dateTo = $request->get('filter_date_to', '');

        $where = "WHERE 1=1";
        $params = [];
        if ($status) { $where .= " AND gp.status = ?"; $params[] = $status; }
        if ($dateFrom) { $where .= " AND gp.valid_date >= ?"; $params[] = $dateFrom; }
        if ($dateTo) { $where .= " AND gp.valid_date <= ?"; $params[] = $dateTo; }

        $baseQuery = "SELECT gp.id, gp.pass_number, gp.guest_name, gp.guest_phone, gp.valid_date,
                       gp.fee_amount, gp.status, gp.check_in_time, gp.check_out_time,
                       gpt.name_en AS type_name,
                       m.membership_number, m.full_name_en AS host_name
                       FROM guest_passes gp
                       LEFT JOIN guest_pass_types gpt ON gp.pass_type_id = gpt.id
                       LEFT JOIN members m ON gp.host_member_id = m.id
                       {$where}";

        $columns = [
            ['db' => 'id', 'searchable' => false],
            ['db' => 'pass_number', 'searchable' => true],
            ['db' => 'guest_name', 'searchable' => true],
            ['db' => 'host_name', 'searchable' => true],
            ['db' => 'type_name', 'searchable' => true],
            ['db' => 'valid_date', 'searchable' => false],
            ['db' => 'status', 'searchable' => true],
            ['db' => 'guest_phone', 'searchable' => true],
        ];

        $this->handleDataTable($request, $baseQuery, $params, $columns, 'gp.id DESC');
    }

    public function create(Request $request): void
    {
        $db = Database::getInstance();
        $passTypes = $db->fetchAll("SELECT * FROM guest_pass_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('guests/create', [
            'title' => lang('cards.issue_guest_pass'),
            'breadcrumbs' => [['label' => lang('cards.guests')], ['label' => lang('cards.guest_passes'), 'url' => '/guests'], ['label' => lang('common.create')]],
            'passTypes' => $passTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $validator = new Validator($request->all(), [
            'host_member_id' => 'required|integer|exists:members,id',
            'pass_type_id' => 'required|integer|exists:guest_pass_types,id',
            'guest_name' => 'required|string|max:300',
            'valid_date' => 'required|date',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $db = Database::getInstance();
        $data = $validator->validated();

        // Check blacklist
        $nationalId = $request->post('guest_national_id', '');
        if ($nationalId) {
            $blacklisted = $db->fetchOne("SELECT id FROM guest_blacklist WHERE guest_national_id = ? AND status = 'active' AND (expires_at IS NULL OR expires_at > CURDATE())", [$nationalId]);
            if ($blacklisted) {
                $this->json(['success' => false, 'message' => lang('cards.guest_blacklisted')], 422);
                return;
            }
        }

        // Check guest policy
        $member = $db->fetchOne("SELECT membership_type_id FROM members WHERE id = ?", [$data['host_member_id']]);
        $policyViolation = $this->checkGuestPolicy($db, $data['host_member_id'], $member['membership_type_id'] ?? null, $data['pass_type_id']);
        if ($policyViolation) {
            $this->json(['success' => false, 'message' => $policyViolation], 422);
            return;
        }

        $passType = $db->fetchOne("SELECT * FROM guest_pass_types WHERE id = ?", [$data['pass_type_id']]);
        $passNumber = SequenceGenerator::next('guest_pass_number');

        $id = $db->insert('guest_passes', [
            'pass_number'     => $passNumber,
            'pass_type_id'    => $data['pass_type_id'],
            'host_member_id'  => $data['host_member_id'],
            'guest_name'      => $data['guest_name'],
            'guest_phone'     => $request->post('guest_phone', ''),
            'guest_national_id' => $nationalId ?: null,
            'guest_email'     => $request->post('guest_email', '') ?: null,
            'guest_gender'    => $request->post('guest_gender', '') ?: null,
            'valid_date'      => $data['valid_date'],
            'valid_from_time' => $request->post('valid_from_time') ?: null,
            'valid_to_time'   => $request->post('valid_to_time') ?: null,
            'fee_amount'      => $passType['fee'] ?? 0,
            'fee_paid'        => 0,
            'status'          => 'issued',
            'created_by'      => Auth::id(),
            'created_at'      => date('Y-m-d H:i:s'),
        ]);

        AuditLog::log('create', 'cards', 'guest_passes', $id, "Guest pass {$passNumber} issued for {$data['guest_name']}");
        $this->json(['success' => true, 'message' => lang('cards.guest_pass_issued'), 'redirect' => '/guests/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $pass = $db->fetchOne(
            "SELECT gp.*, gpt.name_en AS type_name, gpt.name_ar AS type_name_ar,
                    m.membership_number, m.full_name_en AS host_name, m.full_name_ar AS host_name_ar, m.primary_phone AS host_phone
             FROM guest_passes gp
             LEFT JOIN guest_pass_types gpt ON gp.pass_type_id = gpt.id
             LEFT JOIN members m ON gp.host_member_id = m.id
             WHERE gp.id = ?", [$id]
        );
        if (!$pass) { $this->abort(404, 'Not found'); return; }

        $visits = $db->fetchAll("SELECT * FROM guest_visits WHERE guest_pass_id = ? ORDER BY visit_date DESC", [$id]);

        $this->view('guests/show', [
            'title' => lang('cards.guest_pass') . ' - ' . $pass['pass_number'],
            'breadcrumbs' => [['label' => lang('cards.guests')], ['label' => lang('cards.guest_passes'), 'url' => '/guests'], ['label' => $pass['pass_number']]],
            'pass' => $pass,
            'visits' => $visits,
        ]);
    }

    public function checkIn(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $pass = $db->fetchOne("SELECT * FROM guest_passes WHERE id = ? AND status IN ('issued','active')", [$id]);
        if (!$pass) { $this->json(['success' => false, 'message' => 'Pass not found or not valid'], 404); return; }
        if ($pass['valid_date'] !== date('Y-m-d')) { $this->json(['success' => false, 'message' => 'Pass not valid for today'], 422); return; }

        $db->update('guest_passes', [
            'status' => 'active',
            'check_in_time' => date('Y-m-d H:i:s'),
            'checked_in_by' => Auth::id(),
        ], 'id = ?', [$id]);

        $this->json(['success' => true, 'message' => lang('cards.guest_checked_in')]);
    }

    public function checkOut(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $db->update('guest_passes', [
            'status' => 'used',
            'check_out_time' => date('Y-m-d H:i:s'),
            'checked_out_by' => Auth::id(),
        ], 'id = ? AND status = ?', [$id, 'active']);

        $this->json(['success' => true, 'message' => lang('cards.guest_checked_out')]);
    }

    public function cancel(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $db->update('guest_passes', ['status' => 'cancelled'], 'id = ? AND status IN (?,?)', [$id, 'issued', 'active']);
        AuditLog::log('update', 'cards', 'guest_passes', $id, "Guest pass cancelled");
        $this->json(['success' => true, 'message' => lang('cards.guest_pass_cancelled')]);
    }

    private function checkGuestPolicy(Database $db, int $memberId, ?int $membershipTypeId, int $passTypeId): ?string
    {
        $rules = $db->fetchAll(
            "SELECT * FROM guest_policy_rules WHERE is_active = 1 AND (membership_type_id IS NULL OR membership_type_id = ?) AND (guest_pass_type_id IS NULL OR guest_pass_type_id = ?)",
            [$membershipTypeId, $passTypeId]
        );
        foreach ($rules as $rule) {
            if ($rule['max_visits_per_month']) {
                $monthCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE host_member_id = ? AND MONTH(valid_date) = MONTH(CURDATE()) AND YEAR(valid_date) = YEAR(CURDATE()) AND status != 'cancelled'", [$memberId]);
                if ($monthCount >= $rule['max_visits_per_month']) return lang('cards.guest_month_limit');
            }
            if ($rule['max_visits_per_year']) {
                $yearCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE host_member_id = ? AND YEAR(valid_date) = YEAR(CURDATE()) AND status != 'cancelled'", [$memberId]);
                if ($yearCount >= $rule['max_visits_per_year']) return lang('cards.guest_year_limit');
            }
        }
        return null;
    }
}
```

## File: modules\cards\css\cards.css

```css
/* Cards Module Styles */
.card.border-primary { border-left: 4px solid var(--bs-primary) !important; }
.card.border-success { border-left: 4px solid var(--bs-success) !important; }
.card.border-warning { border-left: 4px solid var(--bs-warning) !important; }
.card.border-danger { border-left: 4px solid var(--bs-danger) !important; }
.card.border-info { border-left: 4px solid var(--bs-info) !important; }
.card.border-secondary { border-left: 4px solid var(--bs-secondary) !important; }

.stats-row .card {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.stats-row .card:hover {
    transform: translateY(-3px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

[dir="rtl"] .card.border-primary,
[dir="rtl"] .card.border-success,
[dir="rtl"] .card.border-warning,
[dir="rtl"] .card.border-danger,
[dir="rtl"] .card.border-info,
[dir="rtl"] .card.border-secondary {
    border-left: 1px solid #e2e8f0 !important;
    border-right-width: 4px !important;
}

#blacklistWarning {
    font-weight: 600;
    font-size: 0.875rem;
}

.progress {
    border-radius: 4px;
}
```

## File: modules\cards\js\cards.js

```js
/**
 * Cards Module â€” Global JS
 */
(function() {
    'use strict';

    // Auto-refresh access log every 30 seconds if on that page
    if (document.getElementById('accessLogTable')) {
        setInterval(function() {
            const dt = $.fn.dataTable.tables({ api: true });
            if (dt && dt.ajax) {
                dt.ajax.reload(null, false);
            }
        }, 30000);
    }

    // Member search Select2 initialization for forms that need it
    document.querySelectorAll('.member-search-select2').forEach(function(el) {
        $(el).select2({
            theme: 'bootstrap-5',
            width: '100%',
            ajax: {
                url: '/api/members/search',
                dataType: 'json',
                delay: 300,
                data: function(params) {
                    return { q: params.term };
                },
                processResults: function(data) {
                    return {
                        results: data.map(function(m) {
                            return {
                                id: m.id,
                                text: m.membership_number + ' - ' + (m.text || m.full_name_en || '')
                            };
                        })
                    };
                }
            },
            minimumInputLength: 2,
            placeholder: 'Search member...'
        });
    });
})();
```

## File: modules\cards\lang\ar.php

```php
<?php
return [
    'cards.module'              => 'Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª ÙˆØ§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.all_cards'           => 'Ø¬Ù…ÙŠØ¹ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª',
    'cards.card_number'         => 'Ø±Ù‚Ù… Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.member'              => 'Ø§Ù„Ø¹Ø¶Ùˆ',
    'cards.type'                => 'Ø§Ù„Ù†ÙˆØ¹',
    'cards.issue_date'          => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥ØµØ¯Ø§Ø±',
    'cards.expiry_date'         => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ù†ØªÙ‡Ø§Ø¡',
    'cards.active_cards'        => 'Ø¨Ø·Ø§Ù‚Ø§Øª Ù†Ø´Ø·Ø©',
    'cards.expiring_30'         => 'ØªÙ†ØªÙ‡ÙŠ Ø®Ù„Ø§Ù„ 30 ÙŠÙˆÙ…',
    'cards.lost_stolen'         => 'Ù…ÙÙ‚ÙˆØ¯Ø© / Ù…Ø³Ø±ÙˆÙ‚Ø©',
    'cards.expired'             => 'Ù…Ù†ØªÙ‡ÙŠØ©',
    'cards.card_details'        => 'ØªÙØ§ØµÙŠÙ„ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.invalidate'          => 'Ø¥Ø¨Ø·Ø§Ù„ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.card_invalidated'    => 'ØªÙ… Ø¥Ø¨Ø·Ø§Ù„ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'cards.recent_access'       => 'Ø¢Ø®Ø± Ø¹Ù…Ù„ÙŠØ§Øª Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.replacement_history' => 'Ø³Ø¬Ù„ Ø§Ù„Ø§Ø³ØªØ¨Ø¯Ø§Ù„',
    'cards.card_requests'       => 'Ø·Ù„Ø¨Ø§Øª Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª',
    'cards.new_request'         => 'Ø·Ù„Ø¨ Ø¨Ø·Ø§Ù‚Ø© Ø¬Ø¯ÙŠØ¯',
    'cards.request_created'     => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø·Ù„Ø¨ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.request_approved'    => 'ØªÙ…Øª Ø§Ù„Ù…ÙˆØ§ÙÙ‚Ø© Ø¹Ù„Ù‰ Ø§Ù„Ø·Ù„Ø¨ ÙˆØ¥ØµØ¯Ø§Ø± Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.request_rejected'    => 'ØªÙ… Ø±ÙØ¶ Ø·Ù„Ø¨ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.card_delivered'      => 'ØªÙ… ØªØ³Ù„ÙŠÙ… Ø§Ù„Ø¨Ø·Ø§Ù‚Ø©',
    'cards.card_types'          => 'Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª',
    'cards.print_batches'       => 'Ø¯ÙØ¹Ø§Øª Ø§Ù„Ø·Ø¨Ø§Ø¹Ø©',
    'cards.no_cards_to_print'   => 'Ù„Ø§ ØªÙˆØ¬Ø¯ Ø·Ù„Ø¨Ø§Øª Ù…Ø¹ØªÙ…Ø¯Ø© Ù„Ù„Ø·Ø¨Ø§Ø¹Ø©',
    'cards.batch_created'       => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø¯ÙØ¹Ø© Ø§Ù„Ø·Ø¨Ø§Ø¹Ø©',
    'cards.batch_processed'     => 'ØªÙ…Øª Ù…Ø¹Ø§Ù„Ø¬Ø© Ø¯ÙØ¹Ø© Ø§Ù„Ø·Ø¨Ø§Ø¹Ø©',
    'cards.access_control'      => 'Ø§Ù„ØªØ­ÙƒÙ… Ø¨Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.access_points'       => 'Ù†Ù‚Ø§Ø· Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.access_rules'        => 'Ù‚ÙˆØ§Ø¹Ø¯ Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.access_log'          => 'Ø³Ø¬Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.today_entries'       => 'Ø¯Ø®ÙˆÙ„ Ø§Ù„ÙŠÙˆÙ…',
    'cards.guests'              => 'Ø§Ù„Ø¶ÙŠÙˆÙ',
    'cards.guest_passes'        => 'ØªØµØ§Ø±ÙŠØ­ Ø§Ù„Ø¶ÙŠÙˆÙ',
    'cards.issue_pass'          => 'Ø¥ØµØ¯Ø§Ø± ØªØµØ±ÙŠØ­',
    'cards.pass_issued'         => 'ØªÙ… Ø¥ØµØ¯Ø§Ø± ØªØµØ±ÙŠØ­ Ø§Ù„Ø¶ÙŠÙ',
    'cards.pass_details'        => 'ØªÙØ§ØµÙŠÙ„ Ø§Ù„ØªØµØ±ÙŠØ­',
    'cards.pass_not_valid'      => 'Ø§Ù„ØªØµØ±ÙŠØ­ ØºÙŠØ± ØµØ§Ù„Ø­ Ù„Ù„Ø¯Ø®ÙˆÙ„ Ø§Ù„ÙŠÙˆÙ…',
    'cards.pass_not_active'     => 'Ø§Ù„ØªØµØ±ÙŠØ­ ØºÙŠØ± Ù†Ø´Ø·',
    'cards.guest_checked_in'    => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø¯Ø®ÙˆÙ„ Ø§Ù„Ø¶ÙŠÙ',
    'cards.guest_checked_out'   => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø®Ø±ÙˆØ¬ Ø§Ù„Ø¶ÙŠÙ',
    'cards.pass_cancelled'      => 'ØªÙ… Ø¥Ù„ØºØ§Ø¡ ØªØµØ±ÙŠØ­ Ø§Ù„Ø¶ÙŠÙ',
    'cards.guest_blacklist'     => 'Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø³ÙˆØ¯Ø§Ø¡ Ù„Ù„Ø¶ÙŠÙˆÙ',
    'cards.guest_blacklisted'   => 'Ù‡Ø°Ø§ Ø§Ù„Ø¶ÙŠÙ Ù…Ø¯Ø±Ø¬ ÙÙŠ Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø³ÙˆØ¯Ø§Ø¡',
    'cards.host_not_active'     => 'Ø§Ù„Ø¹Ø¶Ùˆ Ø§Ù„Ù…Ø¶ÙŠÙ ØºÙŠØ± Ù†Ø´Ø·',
    'cards.guest_monthly_limit' => 'ØªÙ… Ø§Ù„ÙˆØµÙˆÙ„ Ù„Ù„Ø­Ø¯ Ø§Ù„Ø£Ù‚ØµÙ‰ Ø§Ù„Ø´Ù‡Ø±ÙŠ Ù„Ù„Ø¶ÙŠÙˆÙ',
    'cards.same_guest_limit'    => 'ØªÙ… Ø§Ù„ÙˆØµÙˆÙ„ Ù„Ù„Ø­Ø¯ Ø§Ù„Ø£Ù‚ØµÙ‰ Ù„Ø²ÙŠØ§Ø±Ø§Øª Ù†ÙØ³ Ø§Ù„Ø¶ÙŠÙ',
    'cards.blacklist_added'     => 'ØªÙ…Øª Ø¥Ø¶Ø§ÙØ© Ø§Ù„Ø¶ÙŠÙ Ù„Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø³ÙˆØ¯Ø§Ø¡',
    'cards.blacklist_removed'   => 'ØªÙ…Øª Ø¥Ø²Ø§Ù„Ø© Ø§Ù„Ø¶ÙŠÙ Ù…Ù† Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø³ÙˆØ¯Ø§Ø¡',
    'cards.active_guest_passes' => 'ØªØµØ§Ø±ÙŠØ­ Ø§Ù„Ø¶ÙŠÙˆÙ Ø§Ù„Ù†Ø´Ø·Ø©',
    'cards.carnets'             => 'Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡Ø§Øª',
    'cards.all_carnets'         => 'Ø¬Ù…ÙŠØ¹ Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡Ø§Øª',
    'cards.issue_carnet'        => 'Ø¥ØµØ¯Ø§Ø± ÙƒØ§Ø±Ù†ÙŠÙ‡',
    'cards.carnet_issued'       => 'ØªÙ… Ø¥ØµØ¯Ø§Ø± Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡',
    'cards.carnet_details'      => 'ØªÙØ§ØµÙŠÙ„ Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡',
    'cards.carnet_not_active'   => 'Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡ ØºÙŠØ± Ù†Ø´Ø·',
    'cards.carnet_exhausted'    => 'Ù„Ø§ ØªÙˆØ¬Ø¯ Ø¯Ø®Ù„Ø§Øª Ù…ØªØ¨Ù‚ÙŠØ©',
    'cards.carnet_expired'      => 'Ø§Ù†ØªÙ‡Øª ØµÙ„Ø§Ø­ÙŠØ© Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡',
    'cards.entry_recorded'      => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.entry_voided'        => 'ØªÙ… Ø¥Ø¨Ø·Ø§Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'cards.carnet_types'        => 'Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡Ø§Øª',
];
```

## File: modules\cards\lang\en.php

```php
<?php
return [
    'cards.module'              => 'Cards & Access',
    'cards.all_cards'           => 'All Cards',
    'cards.card_number'         => 'Card Number',
    'cards.member'              => 'Member',
    'cards.type'                => 'Type',
    'cards.issue_date'          => 'Issue Date',
    'cards.expiry_date'         => 'Expiry Date',
    'cards.active_cards'        => 'Active Cards',
    'cards.expiring_30'         => 'Expiring (30 days)',
    'cards.lost_stolen'         => 'Lost / Stolen',
    'cards.expired'             => 'Expired',
    'cards.card_details'        => 'Card Details',
    'cards.invalidate'          => 'Invalidate Card',
    'cards.card_invalidated'    => 'Card invalidated successfully',
    'cards.recent_access'       => 'Recent Access',
    'cards.replacement_history' => 'Replacement History',
    'cards.card_requests'       => 'Card Requests',
    'cards.new_request'         => 'New Card Request',
    'cards.request_created'     => 'Card request created',
    'cards.request_approved'    => 'Card request approved and card issued',
    'cards.request_rejected'    => 'Card request rejected',
    'cards.card_delivered'      => 'Card marked as delivered',
    'cards.card_types'          => 'Card Types',
    'cards.print_batches'       => 'Print Batches',
    'cards.no_cards_to_print'   => 'No approved card requests to print',
    'cards.batch_created'       => 'Print batch created',
    'cards.batch_processed'     => 'Print batch processed',
    'cards.access_control'      => 'Access Control',
    'cards.access_points'       => 'Access Points',
    'cards.access_rules'        => 'Access Rules',
    'cards.access_log'          => 'Access Log',
    'cards.today_entries'       => 'Today\'s Entries',
    'cards.guests'              => 'Guests',
    'cards.guest_passes'        => 'Guest Passes',
    'cards.issue_pass'          => 'Issue Guest Pass',
    'cards.pass_issued'         => 'Guest pass issued successfully',
    'cards.pass_details'        => 'Pass Details',
    'cards.pass_not_valid'      => 'Pass not valid for check-in today',
    'cards.pass_not_active'     => 'Pass is not in active status',
    'cards.guest_checked_in'    => 'Guest checked in',
    'cards.guest_checked_out'   => 'Guest checked out',
    'cards.pass_cancelled'      => 'Guest pass cancelled',
    'cards.guest_blacklist'     => 'Guest Blacklist',
    'cards.guest_blacklisted'   => 'This guest is blacklisted and cannot be issued a pass',
    'cards.host_not_active'     => 'Host member is not active',
    'cards.guest_monthly_limit' => 'Monthly guest limit reached for this member',
    'cards.same_guest_limit'    => 'Same guest visit limit per month reached',
    'cards.blacklist_added'     => 'Guest added to blacklist',
    'cards.blacklist_removed'   => 'Guest removed from blacklist',
    'cards.active_guest_passes' => 'Active Guest Passes',
    'cards.carnets'             => 'Carnets',
    'cards.all_carnets'         => 'All Carnets',
    'cards.issue_carnet'        => 'Issue Carnet',
    'cards.carnet_issued'       => 'Carnet issued successfully',
    'cards.carnet_details'      => 'Carnet Details',
    'cards.carnet_not_active'   => 'Carnet is not active',
    'cards.carnet_exhausted'    => 'Carnet has no remaining entries',
    'cards.carnet_expired'      => 'Carnet has expired',
    'cards.entry_recorded'      => 'Entry recorded',
    'cards.entry_voided'        => 'Entry voided',
    'cards.carnet_types'        => 'Carnet Types',
];
```

## File: modules\cards\module.json

```json
{
    "name": "cards",
    "label_en": "Cards, Access & Guests",
    "label_ar": "Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª ÙˆØ§Ù„Ø¯Ø®ÙˆÙ„ ÙˆØ§Ù„Ø¶ÙŠÙˆÙ",
    "version": "1.0.0",
    "description": "Card issuance, access control, guest passes, and carnet management",
    "sort_order": 50,
    "icon": "fas fa-id-card",
    "routes_file": "routes.php",
    "css_files": ["css/cards.css"],
    "js_files": ["js/cards.js"],
    "menu": [
        {
            "label_en": "ID Cards",
            "label_ar": "Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª",
            "icon": "fas fa-id-card",
            "permission": "cards.view",
            "sort_order": 50,
            "children": [
                {
                    "label_en": "All Cards",
                    "label_ar": "Ø¬Ù…ÙŠØ¹ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª",
                    "route": "/cards",
                    "permission": "cards.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Card Requests",
                    "label_ar": "Ø·Ù„Ø¨Ø§Øª Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª",
                    "route": "/cards/requests",
                    "permission": "cards.view",
                    "sort_order": 2
                },
                {
                    "label_en": "Print Batches",
                    "label_ar": "Ø¯ÙØ¹Ø§Øª Ø§Ù„Ø·Ø¨Ø§Ø¹Ø©",
                    "route": "/cards/print-batches",
                    "permission": "cards.print",
                    "sort_order": 3
                },
                {
                    "label_en": "Card Types",
                    "label_ar": "Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª",
                    "route": "/cards/types",
                    "permission": "cards.manage",
                    "sort_order": 4
                }
            ]
        },
        {
            "label_en": "Access Control",
            "label_ar": "Ø§Ù„ØªØ­ÙƒÙ… Ø¨Ø§Ù„Ø¯Ø®ÙˆÙ„",
            "icon": "fas fa-door-open",
            "permission": "access.view",
            "sort_order": 51,
            "children": [
                {
                    "label_en": "Access Log",
                    "label_ar": "Ø³Ø¬Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„",
                    "route": "/access/log",
                    "permission": "access.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Access Points",
                    "label_ar": "Ù†Ù‚Ø§Ø· Ø§Ù„Ø¯Ø®ÙˆÙ„",
                    "route": "/access/points",
                    "permission": "access.manage",
                    "sort_order": 2
                },
                {
                    "label_en": "Access Rules",
                    "label_ar": "Ù‚ÙˆØ§Ø¹Ø¯ Ø§Ù„Ø¯Ø®ÙˆÙ„",
                    "route": "/access/rules",
                    "permission": "access.manage",
                    "sort_order": 3
                }
            ]
        },
        {
            "label_en": "Guest Passes",
            "label_ar": "ØªØµØ§Ø±ÙŠØ­ Ø§Ù„Ø¶ÙŠÙˆÙ",
            "icon": "fas fa-user-friends",
            "permission": "guests.view",
            "sort_order": 52,
            "children": [
                {
                    "label_en": "All Passes",
                    "label_ar": "Ø¬Ù…ÙŠØ¹ Ø§Ù„ØªØµØ§Ø±ÙŠØ­",
                    "route": "/guests",
                    "permission": "guests.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Issue Pass",
                    "label_ar": "Ø¥ØµØ¯Ø§Ø± ØªØµØ±ÙŠØ­",
                    "route": "/guests/create",
                    "permission": "guests.create",
                    "sort_order": 2
                },
                {
                    "label_en": "Blacklist",
                    "label_ar": "Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø³ÙˆØ¯Ø§Ø¡",
                    "route": "/guests/blacklist",
                    "permission": "guests.view",
                    "sort_order": 3
                }
            ]
        },
        {
            "label_en": "Carnets",
            "label_ar": "Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡Ø§Øª",
            "icon": "fas fa-ticket-alt",
            "permission": "cards.view",
            "sort_order": 53,
            "children": [
                {
                    "label_en": "All Carnets",
                    "label_ar": "Ø¬Ù…ÙŠØ¹ Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡Ø§Øª",
                    "route": "/carnets",
                    "permission": "cards.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Issue Carnet",
                    "label_ar": "Ø¥ØµØ¯Ø§Ø± ÙƒØ§Ø±Ù†ÙŠÙ‡",
                    "route": "/carnets/create",
                    "permission": "cards.manage",
                    "sort_order": 2
                },
                {
                    "label_en": "Carnet Types",
                    "label_ar": "Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„ÙƒØ§Ø±Ù†ÙŠÙ‡Ø§Øª",
                    "route": "/carnets/types",
                    "permission": "cards.manage",
                    "sort_order": 3
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "expiring_cards",
            "label_en": "Expiring Cards",
            "label_ar": "Ø¨Ø·Ø§Ù‚Ø§Øª Ù‚Ø§Ø±Ø¨Øª Ø§Ù„Ø§Ù†ØªÙ‡Ø§Ø¡",
            "permission": "cards.view",
            "partial": "views/widgets/expiring_cards.php",
            "size": "col-md-4",
            "sort_order": 50
        },
        {
            "widget_id": "today_access",
            "label_en": "Today's Access",
            "label_ar": "Ø¯Ø®ÙˆÙ„ Ø§Ù„ÙŠÙˆÙ…",
            "permission": "access.view",
            "partial": "views/widgets/today_access.php",
            "size": "col-md-4",
            "sort_order": 51
        },
        {
            "widget_id": "active_guests",
            "label_en": "Active Guest Passes",
            "label_ar": "ØªØµØ§Ø±ÙŠØ­ Ø§Ù„Ø¶ÙŠÙˆÙ Ø§Ù„Ù†Ø´Ø·Ø©",
            "permission": "guests.view",
            "partial": "views/widgets/active_guests.php",
            "size": "col-md-4",
            "sort_order": 52
        }
    ]
}
```

## File: modules\cards\routes.php

```php
<?php
return [
    // â”€â”€ Cards â”€â”€
    ['GET',  '/cards',                              'CardController@index',            'cards.view'],
    ['GET',  '/cards/show/{id}',                    'CardController@show',             'cards.view'],
    ['POST', '/cards/{id}/invalidate',              'CardController@invalidate',       'cards.manage'],

    // â”€â”€ Card Requests â”€â”€
    ['GET',  '/cards/requests',                     'CardRequestController@index',     'cards.view'],
    ['GET',  '/cards/requests/create',              'CardRequestController@create',    'cards.manage'],
    ['POST', '/cards/requests',                     'CardRequestController@store',     'cards.manage'],
    ['POST', '/cards/requests/{id}/approve',        'CardRequestController@approve',   'cards.manage'],
    ['POST', '/cards/requests/{id}/reject',         'CardRequestController@reject',    'cards.manage'],
    ['POST', '/cards/requests/{id}/deliver',        'CardRequestController@deliver',   'cards.manage'],

    // â”€â”€ Card Types â”€â”€
    ['GET',  '/cards/types',                        'CardTypeController@index',        'cards.manage'],
    ['POST', '/cards/types',                        'CardTypeController@store',        'cards.manage'],
    ['POST', '/cards/types/{id}',                   'CardTypeController@update',       'cards.manage'],

    // â”€â”€ Card Print Batches â”€â”€
    ['GET',  '/cards/print-batches',                'CardPrintController@index',       'cards.print'],
    ['POST', '/cards/print-batches',                'CardPrintController@store',       'cards.print'],
    ['POST', '/cards/print-batches/{id}/process',   'CardPrintController@process',     'cards.print'],

    // â”€â”€ Card API â”€â”€
    ['GET',  '/api/cards/datatable',                'CardApiController@datatable',     'cards.view'],
    ['GET',  '/api/cards/requests/datatable',       'CardApiController@requestsDatatable', 'cards.view'],
    ['GET',  '/api/cards/types/list',               'CardApiController@typesList',     'cards.view'],
    ['GET',  '/api/cards/print-batches/datatable',  'CardApiController@printDatatable','cards.print'],
    ['GET',  '/api/cards/print-batches/{id}/items', 'CardApiController@batchItems',    'cards.print'],

    // â”€â”€ Access Points â”€â”€
    ['GET',  '/access/points',                      'AccessController@points',         'access.manage'],
    ['POST', '/access/points',                      'AccessController@storePoint',     'access.manage'],
    ['POST', '/access/points/{id}',                 'AccessController@updatePoint',    'access.manage'],

    // â”€â”€ Access Rules â”€â”€
    ['GET',  '/access/rules',                       'AccessController@rules',          'access.manage'],
    ['POST', '/access/rules',                       'AccessController@storeRule',      'access.manage'],
    ['POST', '/access/rules/{id}',                  'AccessController@updateRule',     'access.manage'],
    ['POST', '/access/rules/{id}/delete',           'AccessController@deleteRule',     'access.manage'],

    // â”€â”€ Access Log â”€â”€
    ['GET',  '/access/log',                         'AccessController@log',            'access.view'],

    // â”€â”€ Access API â”€â”€
    ['GET',  '/api/access/points/datatable',        'AccessApiController@pointsDatatable', 'access.view'],
    ['GET',  '/api/access/rules/datatable',         'AccessApiController@rulesDatatable',  'access.manage'],
    ['GET',  '/api/access/log/datatable',           'AccessApiController@logDatatable',    'access.view'],
    ['POST', '/api/access/record',                  'AccessApiController@recordAccess',    'access.manage'],

    // â”€â”€ Guest Passes â”€â”€
    ['GET',  '/guests',                             'GuestController@index',           'guests.view'],
    ['GET',  '/guests/create',                      'GuestController@create',          'guests.create'],
    ['POST', '/guests',                             'GuestController@store',           'guests.create'],
    ['GET',  '/guests/show/{id}',                   'GuestController@show',            'guests.view'],
    ['POST', '/guests/{id}/checkin',                'GuestController@checkin',         'guests.create'],
    ['POST', '/guests/{id}/checkout',               'GuestController@checkout',        'guests.create'],
    ['POST', '/guests/{id}/cancel',                 'GuestController@cancel',          'guests.create'],

    // â”€â”€ Guest Blacklist â”€â”€
    ['GET',  '/guests/blacklist',                   'GuestController@blacklist',       'guests.view'],
    ['POST', '/guests/blacklist',                   'GuestController@storeBlacklist',  'guests.create'],
    ['POST', '/guests/blacklist/{id}/remove',       'GuestController@removeBlacklist', 'guests.create'],

    // â”€â”€ Guest API â”€â”€
    ['GET',  '/api/guests/datatable',               'GuestApiController@datatable',          'guests.view'],
    ['GET',  '/api/guests/blacklist/datatable',     'GuestApiController@blacklistDatatable',  'guests.view'],
    ['GET',  '/api/guests/check-blacklist',         'GuestApiController@checkBlacklist',      'guests.create'],
    ['GET',  '/api/guests/pass-types',              'GuestApiController@passTypes',           'guests.view'],

    // â”€â”€ Carnets â”€â”€
    ['GET',  '/carnets',                            'CarnetController@index',          'cards.view'],
    ['GET',  '/carnets/create',                     'CarnetController@create',         'cards.manage'],
    ['POST', '/carnets',                            'CarnetController@store',          'cards.manage'],
    ['GET',  '/carnets/show/{id}',                  'CarnetController@show',           'cards.view'],
    ['POST', '/carnets/{id}/entry',                 'CarnetController@recordEntry',    'cards.manage'],
    ['POST', '/carnets/{id}/void-entry/{entryId}',  'CarnetController@voidEntry',      'cards.manage'],

    // â”€â”€ Carnet Types â”€â”€
    ['GET',  '/carnets/types',                      'CarnetController@types',          'cards.manage'],
    ['POST', '/carnets/types',                      'CarnetController@storeType',      'cards.manage'],
    ['POST', '/carnets/types/{id}',                 'CarnetController@updateType',     'cards.manage'],

    // â”€â”€ Carnet API â”€â”€
    ['GET',  '/api/carnets/datatable',              'CarnetApiController@datatable',       'cards.view'],
    ['GET',  '/api/carnets/types/datatable',        'CarnetApiController@typesDatatable',  'cards.manage'],
    ['GET',  '/api/carnets/{id}/entries',            'CarnetApiController@entries',         'cards.view'],
];
```

## File: modules\cards\views\access\log.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-4">
        <div class="card border-success h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Entries Today</h6><h3 class="mb-0 text-success"><?= number_format($todayEntries) ?></h3></div><div class="align-self-center"><i class="fas fa-sign-in-alt fa-2x text-success opacity-50"></i></div></div></div></div>
    </div>
    <div class="col-md-4">
        <div class="card border-info h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Exits Today</h6><h3 class="mb-0 text-info"><?= number_format($todayExits) ?></h3></div><div class="align-self-center"><i class="fas fa-sign-out-alt fa-2x text-info opacity-50"></i></div></div></div></div>
    </div>
    <div class="col-md-4">
        <div class="card border-danger h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Denied Today</h6><h3 class="mb-0 text-danger"><?= number_format($todayDenied) ?></h3></div><div class="align-self-center"><i class="fas fa-hand-paper fa-2x text-danger opacity-50"></i></div></div></div></div>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body">
        <div class="row g-2">
            <div class="col-md-2"><input type="date" class="form-control form-control-sm" id="filter-from" value="<?= date('Y-m-d') ?>"></div>
            <div class="col-md-2"><input type="date" class="form-control form-control-sm" id="filter-to" value="<?= date('Y-m-d') ?>"></div>
            <div class="col-md-2"><select id="filter-direction" class="form-select form-select-sm"><option value="">All</option><option value="entry">Entry</option><option value="exit">Exit</option></select></div>
            <div class="col-md-2"><select id="filter-granted" class="form-select form-select-sm"><option value="">All</option><option value="1">Granted</option><option value="0">Denied</option></select></div>
            <div class="col-md-2"><button class="btn btn-sm btn-outline-primary w-100" id="refreshLog"><i class="fas fa-sync me-1"></i>Refresh</button></div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="accessLogTable" class="table table-striped table-hover w-100">
            <thead><tr><th>#</th><th>Time</th><th>Direction</th><th>Member</th><th>Card</th><th>Access Point</th><th>Method</th><th>Result</th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = $('#accessLogTable').DataTable({
        processing: true, serverSide: true,
        ajax: {
            url: '/api/access/log/datatable', type: 'GET',
            data: function(d){
                d.date_from = $('#filter-from').val(); d.date_to = $('#filter-to').val();
                d.direction = $('#filter-direction').val(); d.granted = $('#filter-granted').val();
            }
        },
        columns: [
            { data: 'id' },
            { data: 'access_time' },
            { data: 'direction', render: function(d){ return d==='entry'?'<span class="badge bg-success"><i class="fas fa-sign-in-alt me-1"></i>Entry</span>':'<span class="badge bg-info"><i class="fas fa-sign-out-alt me-1"></i>Exit</span>'; }},
            { data: 'member_name_en', render: function(d,t,r){ return d ? `${d}<br><small class="text-muted">${r.membership_number||''}</small>` : '<span class="text-muted">Guest/Unknown</span>'; }},
            { data: 'card_number', render: function(d){ return d ? `<code>${d}</code>` : '-'; }},
            { data: 'point_name_en', render: function(d,t,r){ return `${d}<br><small class="badge bg-secondary">${r.point_type}</small>`; }},
            { data: 'access_method', render: function(d){ return `<span class="badge bg-outline-secondary">${d}</span>`; }},
            { data: 'granted', render: function(d,t,r){ return d==1?'<span class="text-success fw-bold"><i class="fas fa-check-circle"></i> Granted</span>':'<span class="text-danger"><i class="fas fa-times-circle"></i> '+(r.denial_reason||'Denied')+'</span>'; }}
        ],
        order: [[1, 'desc']], pageLength: 50
    });

    $('#filter-from, #filter-to, #filter-direction, #filter-granted').on('change', function(){ dt.ajax.reload(); });
    $('#refreshLog').on('click', function(){ dt.ajax.reload(); });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\access\points.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-map-marker-alt me-2"></i><?= lang('cards.access_points') ?></h4>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#pointModal" onclick="resetPointForm()"><i class="fas fa-plus me-1"></i>Add Point</button>
</div>

<div class="card">
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-striped">
                <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Type</th><th>Hardware</th><th>Direction</th><th>Facility</th><th>Active</th><th>Actions</th></tr></thead>
                <tbody>
                <?php foreach ($points as $p): ?>
                <tr>
                    <td><code><?= e($p['point_code']) ?></code></td>
                    <td><?= e($p['name_en']) ?></td>
                    <td><?= e($p['name_ar']) ?></td>
                    <td><span class="badge bg-info"><?= e($p['point_type']) ?></span></td>
                    <td><?= e($p['hardware_type']) ?></td>
                    <td><?= e($p['direction']) ?></td>
                    <td><?= e($p['facility_name_en'] ?? '-') ?></td>
                    <td><?= $p['is_active'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>' ?></td>
                    <td><button class="btn btn-sm btn-outline-warning btn-edit-point" data-point='<?= e(json_encode($p)) ?>'><i class="fas fa-edit"></i></button></td>
                </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>

<div class="modal fade" id="pointModal" tabindex="-1">
    <div class="modal-dialog modal-lg"><div class="modal-content">
        <form id="pointForm"><?= csrf_field() ?>
            <input type="hidden" name="_point_id" id="pointId">
            <div class="modal-header"><h5 class="modal-title" id="pointModalTitle">Add Access Point</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="row g-3">
                    <div class="col-md-4"><label class="form-label">Code *</label><input type="text" name="point_code" class="form-control" id="pointCode" required></div>
                    <div class="col-md-4"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" required></div>
                    <div class="col-md-4"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" required></div>
                    <div class="col-md-4"><label class="form-label">Type *</label><select name="point_type" class="form-select" required><option value="main_gate">Main Gate</option><option value="facility_entrance">Facility Entrance</option><option value="parking">Parking</option><option value="pool">Pool</option><option value="gym">Gym</option><option value="court">Court</option><option value="other">Other</option></select></div>
                    <div class="col-md-4"><label class="form-label">Hardware *</label><select name="hardware_type" class="form-select" required><option value="manual">Manual</option><option value="turnstile">Turnstile</option><option value="gate">Gate</option><option value="door">Door</option><option value="barrier">Barrier</option><option value="scanner">Scanner</option></select></div>
                    <div class="col-md-4"><label class="form-label">Direction *</label><select name="direction" class="form-select" required><option value="both">Both</option><option value="entry">Entry Only</option><option value="exit">Exit Only</option></select></div>
                    <div class="col-md-6"><label class="form-label">Facility</label><select name="facility_id" class="form-select"><option value="">None</option><?php foreach ($facilities as $f): ?><option value="<?= $f['id'] ?>"><?= e($f['name_en']) ?></option><?php endforeach; ?></select></div>
                    <div class="col-md-6"><label class="form-label">Location</label><input type="text" name="location" class="form-control"></div>
                    <div class="col-md-3"><label class="form-label">Active</label><select name="is_active" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
            </div>
        </form>
    </div></div>
</div>

<?php $__view->push('scripts') ?>
<script>
function resetPointForm(){ $('#pointForm')[0].reset(); $('#pointId').val(''); $('#pointCode').prop('disabled',false); $('#pointModalTitle').text('Add Access Point'); }
$(document).on('click', '.btn-edit-point', function(){
    const p = $(this).data('point');
    $('#pointId').val(p.id); $('#pointCode').val(p.point_code).prop('disabled',true);
    Object.keys(p).forEach(k => { const el = $(`#pointForm [name="${k}"]`); if(el.length) el.val(p[k]); });
    $('#pointModalTitle').text('Edit Access Point');
    new bootstrap.Modal('#pointModal').show();
});
$('#pointForm').on('submit', function(e){
    e.preventDefault();
    const id = $('#pointId').val();
    $.post(id ? '/access/points/'+id : '/access/points', $(this).serialize(), function(res){
        if(res.success) location.reload(); else Swal.fire({icon:'error',title:'Error',text:res.message||JSON.stringify(res.errors)});
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\access\rules.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-shield-alt me-2"></i><?= lang('cards.access_rules') ?></h4>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#ruleModal" onclick="resetRuleForm()"><i class="fas fa-plus me-1"></i>Add Rule</button>
</div>

<div class="card">
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-striped">
                <thead><tr><th>Rule Name</th><th>Access Point</th><th>Membership Type</th><th>Status Req.</th><th>Card Req.</th><th>Priority</th><th>Active</th><th>Actions</th></tr></thead>
                <tbody>
                <?php foreach ($rules as $r): ?>
                <tr>
                    <td><?= e($r['rule_name']) ?></td>
                    <td><?= e($r['point_name_en']) ?></td>
                    <td><?= e($r['memtype_name_en'] ?? 'All') ?></td>
                    <td><?= e($r['member_status_required']) ?></td>
                    <td><?= $r['card_required'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-muted"></i>' ?></td>
                    <td><?= (int)$r['priority'] ?></td>
                    <td><?= $r['is_active'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>' ?></td>
                    <td>
                        <button class="btn btn-sm btn-outline-warning me-1 btn-edit-rule" data-rule='<?= e(json_encode($r)) ?>'><i class="fas fa-edit"></i></button>
                        <form method="POST" action="/access/rules/<?= $r['id'] ?>/delete" class="d-inline" onsubmit="return confirm('Delete this rule?')">
                            <?= csrf_field() ?>
                            <button type="submit" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button>
                        </form>
                    </td>
                </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>

<div class="modal fade" id="ruleModal" tabindex="-1">
    <div class="modal-dialog modal-lg"><div class="modal-content">
        <form id="ruleForm"><?= csrf_field() ?>
            <input type="hidden" name="_rule_id" id="ruleId">
            <div class="modal-header"><h5 class="modal-title" id="ruleModalTitle">Add Access Rule</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="row g-3">
                    <div class="col-md-6"><label class="form-label">Access Point *</label><select name="access_point_id" class="form-select" required><option value="">-- Select --</option><?php foreach ($points as $p): ?><option value="<?= $p['id'] ?>"><?= e($p['name_en']) ?></option><?php endforeach; ?></select></div>
                    <div class="col-md-6"><label class="form-label">Rule Name *</label><input type="text" name="rule_name" class="form-control" required></div>
                    <div class="col-md-4"><label class="form-label">Membership Type</label><select name="membership_type_id" class="form-select"><option value="">All Types</option><?php foreach ($membershipTypes as $mt): ?><option value="<?= $mt['id'] ?>"><?= e($mt['name_en']) ?></option><?php endforeach; ?></select></div>
                    <div class="col-md-4"><label class="form-label">Member Status Required</label><input type="text" name="member_status_required" class="form-control" value="active"></div>
                    <div class="col-md-4"><label class="form-label">Priority</label><input type="number" name="priority" class="form-control" value="0" min="0"></div>
                    <div class="col-md-3"><label class="form-label">Subscription Req.</label><select name="subscription_required" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                    <div class="col-md-3"><label class="form-label">Card Req.</label><select name="card_required" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                    <div class="col-md-3"><label class="form-label">Allow Dependents</label><select name="allow_dependents" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                    <div class="col-md-3"><label class="form-label">Allow Guests</label><select name="allow_guests" class="form-select"><option value="0">No</option><option value="1">Yes</option></select></div>
                    <div class="col-md-4"><label class="form-label">Max Daily Entries</label><input type="number" name="max_daily_entries" class="form-control" min="1" placeholder="Unlimited"></div>
                    <div class="col-md-4"><label class="form-label">Active</label><select name="is_active" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
            </div>
        </form>
    </div></div>
</div>

<?php $__view->push('scripts') ?>
<script>
function resetRuleForm(){ $('#ruleForm')[0].reset(); $('#ruleId').val(''); $('#ruleModalTitle').text('Add Access Rule'); }
$(document).on('click', '.btn-edit-rule', function(){
    const r = $(this).data('rule'); $('#ruleId').val(r.id);
    Object.keys(r).forEach(k => { const el = $(`#ruleForm [name="${k}"]`); if(el.length) el.val(r[k]); });
    $('#ruleModalTitle').text('Edit Access Rule'); new bootstrap.Modal('#ruleModal').show();
});
$('#ruleForm').on('submit', function(e){
    e.preventDefault();
    const id = $('#ruleId').val();
    $.post(id ? '/access/rules/'+id : '/access/rules', $(this).serialize(), function(res){
        if(res.success) location.reload(); else Swal.fire({icon:'error',title:'Error',text:res.message||JSON.stringify(res.errors)});
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\access-log\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-clipboard-list me-2"></i><?= lang('cards.access_log') ?></h4>
</div>
<div class="row g-3 mb-4">
    <div class="col-md-4"><div class="card border-success h-100"><div class="card-body"><h6 class="text-muted">Today's Entries</h6><h3><?= number_format($todayEntries) ?></h3></div></div></div>
    <div class="col-md-4"><div class="card border-danger h-100"><div class="card-body"><h6 class="text-muted">Denied Today</h6><h3><?= number_format($todayDenied) ?></h3></div></div></div>
</div>
<div class="card mb-3"><div class="card-body">
    <form class="row g-3" data-filter-table="log-table">
        <div class="col-md-3"><select name="filter_point" class="form-select"><option value="">All Points</option>
            <?php foreach($accessPoints as $ap): ?><option value="<?= $ap['id'] ?>"><?= e($ap['name_en']) ?></option><?php endforeach; ?>
        </select></div>
        <div class="col-md-2"><select name="filter_direction" class="form-select"><option value="">All</option><option value="entry">Entry</option><option value="exit">Exit</option></select></div>
        <div class="col-md-2"><input type="date" name="filter_date_from" class="form-control" placeholder="From"></div>
        <div class="col-md-2"><input type="date" name="filter_date_to" class="form-control" placeholder="To"></div>
        <div class="col-md-2"><button type="button" class="btn btn-secondary" onclick="DataTableHelper.reload('log-table')"><?= lang('common.filter') ?></button></div>
    </form>
</div></div>
<div class="card"><div class="card-body">
    <table id="log-table" class="table table-striped w-100" data-url="/access-log/datatable"
           data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"access_time","title":"Time"},{"data":"direction","title":"Dir","render":"renderDir"},{"data":"member_name","title":"Member"},{"data":"membership_number","title":"Mem #"},{"data":"point_name","title":"Point"},{"data":"access_method","title":"Method"},{"data":"granted","title":"Granted","render":"renderGranted"},{"data":"card_number","title":"Card #"}]'>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
function renderDir(d){return d==='entry'?'<span class="badge bg-success">IN</span>':'<span class="badge bg-info">OUT</span>';}
function renderGranted(d){return d==1?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-danger"></i>';}
document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('log-table');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\access-points\form.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $point ? lang('cards.edit_access_point') : lang('cards.create_access_point') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $formAction ?>" data-ajax="true"><?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Code *</label><input type="text" name="point_code" class="form-control" value="<?= e($point['point_code'] ?? '') ?>" required><div class="invalid-feedback" data-field="point_code"></div></div>
                <div class="col-md-4"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" value="<?= e($point['name_en'] ?? '') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" value="<?= e($point['name_ar'] ?? '') ?>" required></div>
                <div class="col-md-6"><label class="form-label">Type *</label><select name="point_type" class="form-select" required>
                    <?php foreach(['main_gate','facility_entrance','parking','pool','gym','court','other'] as $v): ?><option value="<?= $v ?>" <?= selected($v, $point['point_type'] ?? '') ?>><?= ucfirst(str_replace('_',' ',$v)) ?></option><?php endforeach; ?>
                </select></div>
                <div class="col-md-6"><label class="form-label">Hardware *</label><select name="hardware_type" class="form-select" required>
                    <?php foreach(['turnstile','gate','door','barrier','scanner','manual'] as $v): ?><option value="<?= $v ?>" <?= selected($v, $point['hardware_type'] ?? 'manual') ?>><?= ucfirst($v) ?></option><?php endforeach; ?>
                </select></div>
                <div class="col-md-4"><label class="form-label">Direction *</label><select name="direction" class="form-select" required>
                    <?php foreach(['entry','exit','both'] as $v): ?><option value="<?= $v ?>" <?= selected($v, $point['direction'] ?? 'both') ?>><?= ucfirst($v) ?></option><?php endforeach; ?>
                </select></div>
                <div class="col-md-4"><label class="form-label">Facility</label><select name="facility_id" class="form-select"><option value="">None</option>
                    <?php foreach($facilities as $f): ?><option value="<?= $f['id'] ?>" <?= selected($f['id'], $point['facility_id'] ?? '') ?>><?= e($f['name_en']) ?></option><?php endforeach; ?>
                </select></div>
                <div class="col-md-4"><label class="form-label">Location</label><input type="text" name="location" class="form-control" value="<?= e($point['location'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Hardware ID</label><input type="text" name="hardware_id" class="form-control" value="<?= e($point['hardware_id'] ?? '') ?>"></div>
                <div class="col-md-4"><div class="form-check mt-4"><input type="checkbox" name="is_active" class="form-check-input" id="is_active" <?= checked(1, $point['is_active'] ?? 1) ?>><label class="form-check-label" for="is_active">Active</label></div></div>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/access-points" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
```

## File: modules\cards\views\access-points\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-door-open me-2"></i><?= lang('cards.access_points') ?></h4>
    <a href="/access-points/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card"><div class="card-body">
    <table id="ap-table" class="table table-striped w-100" data-url="/access-points/datatable"
           data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"point_code","title":"Code"},{"data":"name_en","title":"Name"},{"data":"location","title":"Location"},{"data":"point_type","title":"Type"},{"data":"is_active","title":"Active","render":"renderBool"},{"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderAPActions"}]'>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
function renderBool(d){return d==1?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-danger"></i>';}
function renderAPActions(d){return '<a href="/access-points/'+d+'/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>';}
document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('ap-table');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\access-rules\form.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $rule ? lang('cards.edit_access_rule') : lang('cards.create_access_rule') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $formAction ?>" data-ajax="true"><?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6"><label class="form-label">Rule Name *</label><input type="text" name="rule_name" class="form-control" value="<?= e($rule['rule_name'] ?? '') ?>" required><div class="invalid-feedback" data-field="rule_name"></div></div>
                <div class="col-md-6"><label class="form-label">Access Point *</label><select name="access_point_id" class="form-select" required><option value="">Select</option>
                    <?php foreach($accessPoints as $ap): ?><option value="<?= $ap['id'] ?>" <?= selected($ap['id'], $rule['access_point_id'] ?? '') ?>><?= e($ap['name_en']) ?></option><?php endforeach; ?>
                </select><div class="invalid-feedback" data-field="access_point_id"></div></div>
                <div class="col-md-6"><label class="form-label">Membership Type</label><select name="membership_type_id" class="form-select"><option value="">All Types</option>
                    <?php foreach($membershipTypes as $mt): ?><option value="<?= $mt['id'] ?>" <?= selected($mt['id'], $rule['membership_type_id'] ?? '') ?>><?= e($mt['name_en']) ?></option><?php endforeach; ?>
                </select></div>
                <div class="col-md-3"><label class="form-label">Required Status</label><input type="text" name="member_status_required" class="form-control" value="<?= e($rule['member_status_required'] ?? 'active') ?>"></div>
                <div class="col-md-3"><label class="form-label">Priority</label><input type="number" name="priority" class="form-control" value="<?= e($rule['priority'] ?? '0') ?>" required></div>
                <div class="col-md-3"><label class="form-label">Time Start</label><input type="time" name="allowed_time_start" class="form-control" value="<?= e($rule['allowed_time_start'] ?? '') ?>"></div>
                <div class="col-md-3"><label class="form-label">Time End</label><input type="time" name="allowed_time_end" class="form-control" value="<?= e($rule['allowed_time_end'] ?? '') ?>"></div>
                <div class="col-md-3"><label class="form-label">Max Daily Entries</label><input type="number" name="max_daily_entries" class="form-control" value="<?= e($rule['max_daily_entries'] ?? '') ?>"></div>
                <div class="col-md-9">
                    <div class="d-flex flex-wrap gap-4 mt-4">
                        <div class="form-check"><input type="checkbox" name="subscription_required" class="form-check-input" id="sub_req" <?= checked(1, $rule['subscription_required'] ?? 1) ?>><label class="form-check-label" for="sub_req">Subscription Required</label></div>
                        <div class="form-check"><input type="checkbox" name="card_required" class="form-check-input" id="card_req" <?= checked(1, $rule['card_required'] ?? 1) ?>><label class="form-check-label" for="card_req">Card Required</label></div>
                        <div class="form-check"><input type="checkbox" name="allow_dependents" class="form-check-input" id="allow_dep" <?= checked(1, $rule['allow_dependents'] ?? 1) ?>><label class="form-check-label" for="allow_dep">Allow Dependents</label></div>
                        <div class="form-check"><input type="checkbox" name="allow_guests" class="form-check-input" id="allow_guests" <?= checked(1, $rule['allow_guests'] ?? 0) ?>><label class="form-check-label" for="allow_guests">Allow Guests</label></div>
                        <div class="form-check"><input type="checkbox" name="is_active" class="form-check-input" id="is_active" <?= checked(1, $rule['is_active'] ?? 1) ?>><label class="form-check-label" for="is_active">Active</label></div>
                    </div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/access-rules" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
```

## File: modules\cards\views\access-rules\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-shield-alt me-2"></i><?= lang('cards.access_rules') ?></h4>
    <a href="/access-rules/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card"><div class="card-body">
    <table id="rules-table" class="table table-striped w-100" data-url="/access-rules/datatable"
           data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"rule_name","title":"Rule"},{"data":"point_name","title":"Access Point"},{"data":"type_name","title":"Membership Type"},{"data":"priority","title":"Priority"},{"data":"is_active","title":"Active","render":"renderBool"},{"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderRuleActions"}]'>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
function renderBool(d){return d==1?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-danger"></i>';}
function renderRuleActions(d){return '<a href="/access-rules/'+d+'/edit" class="btn btn-sm btn-warning me-1"><i class="fas fa-edit"></i></a><button class="btn btn-sm btn-danger" onclick="deleteRule('+d+')"><i class="fas fa-trash"></i></button>';}
function deleteRule(id){Swal.fire({title:'Delete this rule?',icon:'warning',showCancelButton:true,confirmButtonColor:'#e53e3e',confirmButtonText:'Delete'}).then(r=>{if(r.isConfirmed){$.post('/access-rules/'+id+'/delete',{_token:'<?= csrf_token() ?>'},function(d){if(d.success){FormHelper.showToast('success',d.message);DataTableHelper.reload('rules-table');}});}});}
document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('rules-table');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\card-print\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-print me-2"></i><?= lang('cards.print_queue') ?></h4>
    <form method="POST" action="/card-print/create-batch" data-ajax="true" class="d-inline">
        <?= csrf_field() ?>
        <button type="submit" class="btn btn-primary"><i class="fas fa-plus me-1"></i>Create Print Batch (<?= $readyRequests ?> ready)</button>
    </form>
</div>
<div class="card"><div class="card-body">
    <table id="print-table" class="table table-striped w-100" data-url="/card-print/datatable"
           data-columns='[
               {"data":"id","title":"#","width":"50px"},
               {"data":"batch_number","title":"Batch #"},
               {"data":"batch_date","title":"Date"},
               {"data":"total_cards","title":"Total"},
               {"data":"printed_cards","title":"Printed"},
               {"data":"status","title":"Status","render":"renderStatusBadge"},
               {"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderPrintActions"}
           ]'>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
function renderPrintActions(data, type, row) {
    if (row.status === 'pending') {
        return '<form method="POST" action="/card-print/'+data+'/complete" data-ajax="true" class="d-inline">' +
            '<input type="hidden" name="_token" value="<?= csrf_token() ?>">' +
            '<button type="submit" class="btn btn-sm btn-success"><i class="fas fa-check me-1"></i>Complete</button></form>';
    }
    return '<span class="text-muted">-</span>';
}
document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('print-table'); });
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\card-requests\create.php

```php
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-plus me-2"></i><?= lang('cards.new_request') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/card-requests" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-12">
                    <label class="form-label"><?= lang('cards.member') ?> <span class="text-danger">*</span></label>
                    <select name="member_id" id="member-select" class="form-select" required></select>
                    <div class="invalid-feedback" data-field="member_id"></div>
                </div>
                <div class="col-md-6">
                    <label class="form-label"><?= lang('cards.card_type') ?> <span class="text-danger">*</span></label>
                    <select name="card_type_id" class="form-select" required>
                        <option value="">-- Select --</option>
                        <?php foreach ($cardTypes as $ct): ?>
                        <option value="<?= $ct['id'] ?>" data-fee="<?= $ct['fee'] ?>" data-replace-fee="<?= $ct['replacement_fee'] ?>"><?= e($ct['name_en']) ?> (<?= money($ct['fee']) ?>)</option>
                        <?php endforeach; ?>
                    </select>
                    <div class="invalid-feedback" data-field="card_type_id"></div>
                </div>
                <div class="col-md-6">
                    <label class="form-label"><?= lang('cards.request_type') ?> <span class="text-danger">*</span></label>
                    <select name="request_type" class="form-select" required>
                        <option value="new">New</option><option value="replacement">Replacement</option><option value="renewal">Renewal</option>
                    </select>
                    <div class="invalid-feedback" data-field="request_type"></div>
                </div>
                <div class="col-md-12">
                    <label class="form-label"><?= lang('cards.reason') ?></label>
                    <textarea name="reason" class="form-control" rows="3"></textarea>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/card-requests" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div>
</div>
</div>

<?php $__view->push('scripts') ?>
<script>
$('#member-select').select2({
    theme: 'bootstrap-5', width: '100%', placeholder: 'Search member...',
    minimumInputLength: 2,
    ajax: { url: '/cards/api/member-search', dataType: 'json', delay: 300,
        data: function(p) { return {q: p.term}; },
        processResults: function(d) { return d; }
    }
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\card-requests\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-clipboard-list me-2"></i><?= lang('cards.card_requests') ?></h4>
    <a href="/card-requests/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('cards.new_request') ?></a>
</div>

<div class="row g-3 mb-4">
    <div class="col-md-3"><div class="card border-warning h-100"><div class="card-body"><h6 class="text-muted">Pending</h6><h3><?= $stats['pending'] ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-success h-100"><div class="card-body"><h6 class="text-muted">Approved</h6><h3><?= $stats['approved'] ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-info h-100"><div class="card-body"><h6 class="text-muted">In Production</h6><h3><?= $stats['in_production'] ?></h3></div></div></div>
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><h6 class="text-muted">Ready</h6><h3><?= $stats['ready'] ?></h3></div></div></div>
</div>

<div class="card mb-3"><div class="card-body">
    <form class="row g-3" data-filter-table="requests-table">
        <div class="col-md-3"><select name="filter_status" class="form-select"><option value="">All Statuses</option>
            <option value="pending">Pending</option><option value="approved">Approved</option><option value="in_production">In Production</option>
            <option value="ready">Ready</option><option value="delivered">Delivered</option><option value="rejected">Rejected</option>
        </select></div>
        <div class="col-md-2"><button type="button" class="btn btn-secondary" onclick="DataTableHelper.reload('requests-table')"><?= lang('common.filter') ?></button></div>
    </form>
</div></div>

<div class="card"><div class="card-body">
    <table id="requests-table" class="table table-striped w-100" data-url="/card-requests/datatable"
           data-columns='[
               {"data":"id","title":"#","width":"50px"},
               {"data":"request_number","title":"Request #"},
               {"data":"member_name","title":"Member"},
               {"data":"membership_number","title":"Membership #"},
               {"data":"type_name","title":"Card Type"},
               {"data":"request_type","title":"Type"},
               {"data":"status","title":"Status","render":"renderStatusBadge"},
               {"data":"fee_amount","title":"Fee","render":"renderMoney"},
               {"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderReqActions"}
           ]'>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
function renderMoney(data) { return parseFloat(data||0).toFixed(2); }
function renderReqActions(data, type, row) {
    return '<a href="/card-requests/' + data + '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a>';
}
document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('requests-table'); });
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\card-requests\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><?= lang('cards.request_details') ?> - <?= e($req['request_number']) ?></h4>
    <a href="/card-requests" class="btn btn-secondary"><i class="fas fa-arrow-left me-1"></i><?= lang('common.back') ?></a>
</div>
<div class="row g-4">
<div class="col-md-6">
    <div class="card">
        <div class="card-header"><h6 class="mb-0">Request Info</h6></div>
        <div class="card-body">
            <table class="table table-borderless mb-0">
                <tr><th width="40%">Request #</th><td><?= e($req['request_number']) ?></td></tr>
                <tr><th>Status</th><td><?= status_badge($req['status']) ?></td></tr>
                <tr><th>Type</th><td><?= ucfirst($req['request_type']) ?></td></tr>
                <tr><th>Card Type</th><td><?= e($req['type_name']) ?></td></tr>
                <tr><th>Member</th><td><?= e($req['member_name']) ?> (<?= e($req['membership_number']) ?>)</td></tr>
                <?php if ($req['dependent_name']): ?><tr><th>Dependent</th><td><?= e($req['dependent_name']) ?></td></tr><?php endif; ?>
                <tr><th>Fee</th><td><?= money($req['fee_amount']) ?></td></tr>
                <tr><th>Fee Paid</th><td><?= $req['fee_paid'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-warning">No</span>' ?></td></tr>
                <?php if ($req['reason']): ?><tr><th>Reason</th><td><?= e($req['reason']) ?></td></tr><?php endif; ?>
                <tr><th>Created</th><td><?= format_datetime($req['created_at']) ?></td></tr>
                <?php if ($req['approved_by_name']): ?><tr><th>Approved By</th><td><?= e($req['approved_by_name']) ?> at <?= format_datetime($req['approved_at']) ?></td></tr><?php endif; ?>
                <?php if ($req['resulting_card_id']): ?><tr><th>Card</th><td><a href="/cards/<?= $req['resulting_card_id'] ?>">View Card</a></td></tr><?php endif; ?>
            </table>
        </div>
    </div>
</div>
<div class="col-md-6">
    <div class="card">
        <div class="card-header"><h6 class="mb-0">Actions</h6></div>
        <div class="card-body d-flex flex-wrap gap-2">
            <?php if ($req['status'] === 'pending' && auth_can('cards.manage')): ?>
            <form method="POST" action="/card-requests/<?= $req['id'] ?>/approve" data-ajax="true"><?= csrf_field() ?>
                <button type="submit" class="btn btn-success"><i class="fas fa-check me-1"></i>Approve</button></form>
            <button class="btn btn-danger" onclick="rejectRequest(<?= $req['id'] ?>)"><i class="fas fa-times me-1"></i>Reject</button>
            <?php endif; ?>
            <?php if ($req['status'] === 'approved' && auth_can('cards.manage')): ?>
            <form method="POST" action="/card-requests/<?= $req['id'] ?>/produce" data-ajax="true"><?= csrf_field() ?>
                <button type="submit" class="btn btn-primary"><i class="fas fa-cog me-1"></i>Produce Card</button></form>
            <?php endif; ?>
            <?php if ($req['status'] === 'ready' && auth_can('cards.manage')): ?>
            <form method="POST" action="/card-requests/<?= $req['id'] ?>/deliver" data-ajax="true"><?= csrf_field() ?>
                <input type="hidden" name="delivered_to" value="Member">
                <button type="submit" class="btn btn-info"><i class="fas fa-hand-holding me-1"></i>Mark Delivered</button></form>
            <?php endif; ?>
        </div>
    </div>
</div>
</div>

<?php $__view->push('scripts') ?>
<script>
function rejectRequest(id) {
    Swal.fire({title:'Reject Reason',input:'textarea',showCancelButton:true,confirmButtonText:'Reject',confirmButtonColor:'#e53e3e'}).then(r=>{
        if(r.isConfirmed){$.ajax({url:'/card-requests/'+id+'/reject',type:'POST',data:{_token:'<?= csrf_token() ?>',reason:r.value},success:function(d){if(d.success){FormHelper.showToast('success',d.message);setTimeout(()=>location.reload(),800);}}})}
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\cards\index.php

```php
<?php $__view->push('styles') ?>
<style>
.stats-row .card { transition: transform 0.2s; }
.stats-row .card:hover { transform: translateY(-2px); }
</style>
<?php $__view->endPush() ?>

<div class="row g-3 mb-4 stats-row">
    <div class="col-md-3">
        <div class="card border-primary h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('cards.active_cards') ?></h6>
                        <h3 class="mb-0"><?= number_format($stats['total_active']) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-id-card fa-2x text-primary opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-warning h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('cards.expiring_30') ?></h6>
                        <h3 class="mb-0"><?= number_format($stats['expiring_30']) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-clock fa-2x text-warning opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('cards.lost_stolen') ?></h6>
                        <h3 class="mb-0"><?= number_format($stats['lost_stolen']) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-exclamation-triangle fa-2x text-danger opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-secondary h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('cards.expired') ?></h6>
                        <h3 class="mb-0"><?= number_format($stats['expired']) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-ban fa-2x text-secondary opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body">
        <div class="row g-2" data-filter-table="cardsTable">
            <div class="col-md-3">
                <select name="status" class="form-select form-select-sm" id="filter-status">
                    <option value=""><?= lang('common.all') ?> â€” <?= lang('common.status') ?></option>
                    <option value="active"><?= lang('common.status') ?>: Active</option>
                    <option value="expired">Expired</option>
                    <option value="lost">Lost</option>
                    <option value="stolen">Stolen</option>
                    <option value="damaged">Damaged</option>
                    <option value="suspended">Suspended</option>
                    <option value="cancelled">Cancelled</option>
                </select>
            </div>
            <div class="col-md-3">
                <select name="card_type_id" class="form-select form-select-sm" id="filter-type"></select>
            </div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h5 class="mb-0"><i class="fas fa-id-card me-2"></i><?= lang('cards.all_cards') ?></h5>
    </div>
    <div class="card-body">
        <table id="cardsTable" class="table table-striped table-hover w-100">
            <thead>
                <tr>
                    <th>#</th>
                    <th><?= lang('cards.card_number') ?></th>
                    <th><?= lang('cards.member') ?></th>
                    <th><?= lang('cards.type') ?></th>
                    <th><?= lang('cards.issue_date') ?></th>
                    <th><?= lang('cards.expiry_date') ?></th>
                    <th><?= lang('common.status') ?></th>
                    <th><?= lang('common.actions') ?></th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    // Load card types for filter
    $.get('/api/cards/types/list', function(res){
        if(res.success){
            let opts = '<option value="">All Types</option>';
            res.data.forEach(t => { opts += `<option value="${t.id}">${t.name_en} (${t.name_ar})</option>`; });
            $('#filter-type').html(opts);
        }
    });

    const dt = $('#cardsTable').DataTable({
        processing: true, serverSide: true,
        ajax: {
            url: '/api/cards/datatable', type: 'GET',
            data: function(d){
                d.status = $('#filter-status').val();
                d.card_type_id = $('#filter-type').val();
            }
        },
        columns: [
            { data: 'id' },
            { data: 'card_number', render: function(d,t,r){ return `<a href="/cards/show/${r.id}" class="fw-bold">${d}</a>`; }},
            { data: 'member_name_en', render: function(d,t,r){ return `${r.member_name_en || ''}<br><small class="text-muted">${r.membership_number || ''}</small>`; }},
            { data: 'type_name_en', render: function(d,t,r){ return `${r.type_name_en}<br><small>${r.type_name_ar || ''}</small>`; }},
            { data: 'issue_date' },
            { data: 'expiry_date', render: function(d){ if(!d) return '-'; const diff = Math.ceil((new Date(d)-new Date())/(1000*60*60*24)); return d + (diff <= 30 && diff > 0 ? ` <span class="badge bg-warning">${diff}d</span>` : diff <= 0 ? ' <span class="badge bg-danger">Expired</span>' : ''); }},
            { data: 'status', render: function(d){ const m = {active:'success',expired:'secondary',lost:'danger',stolen:'danger',damaged:'warning',cancelled:'dark',suspended:'info',replaced:'secondary'}; return `<span class="badge bg-${m[d]||'secondary'}">${d}</span>`; }},
            { data: 'id', orderable: false, render: function(d,t,r){ return `<a href="/cards/show/${d}" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>`; }}
        ],
        order: [[0, 'desc']], pageLength: 25, responsive: true
    });

    $('#filter-status, #filter-type').on('change', function(){ dt.ajax.reload(); });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\cards\print_batches.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-print me-2"></i><?= lang('cards.print_batches') ?></h4>
    <form method="POST" action="/cards/print-batches" id="createBatchForm">
        <?= csrf_field() ?>
        <button type="submit" class="btn btn-primary"><i class="fas fa-plus me-1"></i>Create Batch from Approved Requests</button>
    </form>
</div>

<div class="card">
    <div class="card-body">
        <table id="printTable" class="table table-striped table-hover w-100">
            <thead><tr><th>#</th><th>Batch #</th><th>Date</th><th>Total</th><th>Printed</th><th>Failed</th><th>Status</th><th>Printed By</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>

<div class="modal fade" id="batchItemsModal" tabindex="-1">
    <div class="modal-dialog modal-lg"><div class="modal-content">
        <div class="modal-header"><h5 class="modal-title">Batch Items</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body" id="batchItemsBody"><div class="text-center py-3"><div class="spinner-border"></div></div></div>
    </div></div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = $('#printTable').DataTable({
        processing: true, serverSide: true,
        ajax: { url: '/api/cards/print-batches/datatable', type: 'GET' },
        columns: [
            { data: 'id' },
            { data: 'batch_number', render: function(d){ return `<strong>${d}</strong>`; }},
            { data: 'batch_date' },
            { data: 'total_cards' },
            { data: 'printed_cards' },
            { data: 'failed_cards' },
            { data: 'status', render: function(d){ const m = {pending:'warning',printing:'info',completed:'success',partial:'warning',cancelled:'dark'}; return `<span class="badge bg-${m[d]||'secondary'}">${d}</span>`; }},
            { data: 'printed_by_name', render: function(d){ return d || '-'; }},
            { data: 'id', orderable: false, render: function(d,t,r){
                let btns = `<button class="btn btn-sm btn-outline-info me-1 btn-view-items" data-id="${d}"><i class="fas fa-list"></i></button>`;
                if(r.status === 'pending' || r.status === 'partial'){
                    btns += `<button class="btn btn-sm btn-success btn-process" data-id="${d}"><i class="fas fa-print"></i> Process</button>`;
                }
                return btns;
            }}
        ],
        order: [[0, 'desc']], pageLength: 25
    });

    $('#createBatchForm').on('submit', function(e){
        e.preventDefault();
        $.post($(this).attr('action'), $(this).serialize(), function(res){
            if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
            else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
        });
    });

    $(document).on('click', '.btn-process', function(){
        const id = $(this).data('id');
        Swal.fire({title:'Process Batch?',text:'Mark all pending items as printed?',icon:'question',showCancelButton:true,confirmButtonText:'Process'}).then(r=>{
            if(r.isConfirmed){
                $.post('/cards/print-batches/'+id+'/process', {_token: $('meta[name="csrf-token"]').attr('content')}, function(res){
                    if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
                    else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
                });
            }
        });
    });

    $(document).on('click', '.btn-view-items', function(){
        const id = $(this).data('id');
        $('#batchItemsBody').html('<div class="text-center py-3"><div class="spinner-border"></div></div>');
        new bootstrap.Modal('#batchItemsModal').show();
        $.get('/api/cards/print-batches/'+id+'/items', function(res){
            if(res.success && res.data.length){
                let html = '<table class="table table-sm"><thead><tr><th>Card #</th><th>Member</th><th>Status</th></tr></thead><tbody>';
                res.data.forEach(i => { html += `<tr><td>${i.card_number||'-'}</td><td>${i.member_name_en||''} (${i.membership_number||''})</td><td><span class="badge bg-${i.print_status==='printed'?'success':'warning'}">${i.print_status}</span></td></tr>`; });
                html += '</tbody></table>';
                $('#batchItemsBody').html(html);
            } else { $('#batchItemsBody').html('<p class="text-center text-muted">No items</p>'); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\cards\request_create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-plus-circle me-2"></i><?= lang('cards.new_request') ?></h5></div>
            <div class="card-body">
                <form id="requestForm" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('cards.member') ?> <span class="text-danger">*</span></label>
                            <select name="member_id" class="form-select select2" id="memberSearch" required></select>
                            <div class="invalid-feedback" data-field="member_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Request Type <span class="text-danger">*</span></label>
                            <select name="request_type" class="form-select" required>
                                <option value="new">New Card</option>
                                <option value="replacement">Replacement</option>
                                <option value="renewal">Renewal</option>
                            </select>
                            <div class="invalid-feedback" data-field="request_type"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Card Type <span class="text-danger">*</span></label>
                            <select name="card_type_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($cardTypes as $ct): ?>
                                <option value="<?= $ct['id'] ?>" data-fee="<?= $ct['fee'] ?>" data-repfee="<?= $ct['replacement_fee'] ?>"><?= e($ct['name_en']) ?> (<?= e($ct['name_ar']) ?>) â€” Fee: <?= number_format($ct['fee'], 2) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="card_type_id"></div>
                        </div>
                        <div class="col-md-6" id="oldCardGroup" style="display:none;">
                            <label class="form-label">Old Card ID (for replacement)</label>
                            <input type="number" name="old_card_id" class="form-control" placeholder="Old card ID">
                        </div>
                        <div class="col-12">
                            <label class="form-label">Reason / Notes</label>
                            <textarea name="reason" class="form-control" rows="3"></textarea>
                            <div class="invalid-feedback" data-field="reason"></div>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/cards/requests" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    $('#memberSearch').select2({
        theme: 'bootstrap-5', width: '100%',
        ajax: {
            url: '/api/members/search', dataType: 'json', delay: 300,
            data: function(p){ return {q: p.term}; },
            processResults: function(d){ return {results: d.map(m => ({id: m.id, text: m.membership_number + ' - ' + m.text}))}; }
        },
        minimumInputLength: 2, placeholder: 'Search member...'
    });

    $('[name="request_type"]').on('change', function(){
        $('#oldCardGroup').toggle($(this).val() === 'replacement');
    });

    $('#requestForm').on('submit', function(e){
        e.preventDefault();
        const form = $(this);
        const btn = form.find('[type="submit"]');
        btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Saving...');
        $.ajax({
            url: '/cards/requests', method: 'POST', data: form.serialize(),
            success: function(res){
                if(res.success){
                    Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}).then(()=>{ window.location.href = res.redirect || '/cards/requests'; });
                } else {
                    if(res.errors){
                        Object.entries(res.errors).forEach(([k,v])=>{
                            form.find(`[name="${k}"]`).addClass('is-invalid');
                            form.find(`[data-field="${k}"]`).text(Array.isArray(v)?v[0]:v).show();
                        });
                    }
                    if(res.message) Swal.fire({icon:'error',title:'Error',text:res.message});
                }
            },
            complete: function(){ btn.prop('disabled', false).html('<i class="fas fa-save me-1"></i><?= lang("common.save") ?>'); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\cards\requests.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-clipboard-list me-2"></i><?= lang('cards.card_requests') ?></h4>
    <?php if (auth_can('cards.manage')): ?>
    <a href="/cards/requests/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('cards.new_request') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3">
    <div class="card-body">
        <div class="row g-2">
            <div class="col-md-3">
                <select id="filter-req-status" class="form-select form-select-sm">
                    <option value="">All Statuses</option>
                    <option value="pending">Pending</option>
                    <option value="approved">Approved</option>
                    <option value="in_production">In Production</option>
                    <option value="ready">Ready</option>
                    <option value="delivered">Delivered</option>
                    <option value="rejected">Rejected</option>
                </select>
            </div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="requestsTable" class="table table-striped table-hover w-100">
            <thead>
                <tr>
                    <th>#</th>
                    <th>Request #</th>
                    <th><?= lang('cards.member') ?></th>
                    <th><?= lang('cards.type') ?></th>
                    <th>Request Type</th>
                    <th>Fee</th>
                    <th><?= lang('common.status') ?></th>
                    <th>Date</th>
                    <th><?= lang('common.actions') ?></th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<!-- Approve/Reject Modals -->
<div class="modal fade" id="approveModal" tabindex="-1">
    <div class="modal-dialog"><div class="modal-content">
        <div class="modal-header"><h5 class="modal-title">Approve Request</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body"><p>Approve this card request and issue the card?</p></div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
            <button type="button" class="btn btn-success" id="confirmApprove"><i class="fas fa-check me-1"></i>Approve & Issue Card</button>
        </div>
    </div></div>
</div>

<div class="modal fade" id="rejectModal" tabindex="-1">
    <div class="modal-dialog"><div class="modal-content">
        <form id="rejectForm">
            <?= csrf_field() ?>
            <div class="modal-header"><h5 class="modal-title">Reject Request</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="mb-3"><label class="form-label">Reason</label><textarea name="reason" class="form-control" rows="3"></textarea></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                <button type="submit" class="btn btn-danger"><i class="fas fa-times me-1"></i>Reject</button>
            </div>
        </form>
    </div></div>
</div>

<div class="modal fade" id="deliverModal" tabindex="-1">
    <div class="modal-dialog"><div class="modal-content">
        <form id="deliverForm">
            <?= csrf_field() ?>
            <div class="modal-header"><h5 class="modal-title">Mark as Delivered</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="mb-3"><label class="form-label">Delivered To</label><input type="text" name="delivered_to" class="form-control" placeholder="Name of person receiving the card"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                <button type="submit" class="btn btn-primary"><i class="fas fa-hand-holding me-1"></i>Confirm Delivery</button>
            </div>
        </form>
    </div></div>
</div>

<?php $__view->push('scripts') ?>
<script>
let activeReqId = null;

const dt = $('#requestsTable').DataTable({
    processing: true, serverSide: true,
    ajax: {
        url: '/api/cards/requests/datatable', type: 'GET',
        data: function(d){ d.status = $('#filter-req-status').val(); }
    },
    columns: [
        { data: 'id' },
        { data: 'request_number', render: function(d){ return `<strong>${d}</strong>`; }},
        { data: 'member_name_en', render: function(d,t,r){ return `${r.member_name_en || ''}<br><small class="text-muted">${r.membership_number || ''}</small>`; }},
        { data: 'type_name_en' },
        { data: 'request_type', render: function(d){ const m = {'new':'primary','replacement':'warning','renewal':'info'}; return `<span class="badge bg-${m[d]||'secondary'}">${d}</span>`; }},
        { data: 'fee_amount', render: function(d,t,r){ return parseFloat(d).toFixed(2) + (r.fee_paid == 1 ? ' <i class="fas fa-check text-success"></i>' : ''); }},
        { data: 'status', render: function(d){ const m = {pending:'warning',approved:'success',in_production:'info',ready:'primary',delivered:'success',rejected:'danger',cancelled:'dark'}; return `<span class="badge bg-${m[d]||'secondary'}">${d}</span>`; }},
        { data: 'created_at', render: function(d){ return d ? d.substring(0,10) : ''; }},
        { data: 'id', orderable: false, render: function(d,t,r){
            let btns = '';
            if(r.status === 'pending'){
                btns += `<button class="btn btn-sm btn-success me-1 btn-approve" data-id="${d}" title="Approve"><i class="fas fa-check"></i></button>`;
                btns += `<button class="btn btn-sm btn-danger me-1 btn-reject" data-id="${d}" title="Reject"><i class="fas fa-times"></i></button>`;
            }
            if(['approved','in_production','ready'].includes(r.status)){
                btns += `<button class="btn btn-sm btn-primary btn-deliver" data-id="${d}" title="Deliver"><i class="fas fa-hand-holding"></i></button>`;
            }
            return btns || '-';
        }}
    ],
    order: [[0, 'desc']], pageLength: 25
});

$('#filter-req-status').on('change', function(){ dt.ajax.reload(); });

$(document).on('click', '.btn-approve', function(){ activeReqId = $(this).data('id'); new bootstrap.Modal('#approveModal').show(); });
$(document).on('click', '.btn-reject', function(){ activeReqId = $(this).data('id'); new bootstrap.Modal('#rejectModal').show(); });
$(document).on('click', '.btn-deliver', function(){ activeReqId = $(this).data('id'); new bootstrap.Modal('#deliverModal').show(); });

$('#confirmApprove').on('click', function(){
    $.post('/cards/requests/'+activeReqId+'/approve', {_token: $('meta[name="csrf-token"]').attr('content')}, function(res){
        bootstrap.Modal.getInstance('#approveModal').hide();
        if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
        else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
    });
});

$('#rejectForm').on('submit', function(e){
    e.preventDefault();
    $.post('/cards/requests/'+activeReqId+'/reject', $(this).serialize(), function(res){
        bootstrap.Modal.getInstance('#rejectModal').hide();
        if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
        else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
    });
});

$('#deliverForm').on('submit', function(e){
    e.preventDefault();
    $.post('/cards/requests/'+activeReqId+'/deliver', $(this).serialize(), function(res){
        bootstrap.Modal.getInstance('#deliverModal').hide();
        if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
        else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\cards\show.php

```php
<div class="row g-4">
    <div class="col-md-4">
        <div class="card">
            <div class="card-body text-center">
                <?php if ($card['member_photo']): ?>
                    <img src="/uploads/<?= e($card['member_photo']) ?>" alt="Photo" class="rounded-circle mb-3" style="width:120px;height:120px;object-fit:cover;">
                <?php else: ?>
                    <div class="bg-light rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width:120px;height:120px;">
                        <i class="fas fa-id-card fa-3x text-muted"></i>
                    </div>
                <?php endif; ?>

                <h5><?= e($card['card_number']) ?></h5>
                <p class="text-muted mb-1"><?= e($card['type_name_en']) ?> / <?= e($card['type_name_ar']) ?></p>
                <div class="mb-3"><?= status_badge($card['status']) ?></div>

                <table class="table table-sm text-start">
                    <tr><th><?= lang('cards.member') ?></th><td><a href="/members/show/<?= $card['member_id'] ?>"><?= e($card['member_name_en']) ?></a><br><small><?= e($card['membership_number']) ?></small></td></tr>
                    <?php if ($card['dep_name_en']): ?>
                    <tr><th>Dependent</th><td><?= e($card['dep_name_en']) ?></td></tr>
                    <?php endif; ?>
                    <tr><th><?= lang('cards.issue_date') ?></th><td><?= e($card['issue_date']) ?></td></tr>
                    <tr><th><?= lang('cards.expiry_date') ?></th><td><?= e($card['expiry_date'] ?? 'N/A') ?></td></tr>
                    <tr><th>Barcode</th><td><code><?= e($card['barcode'] ?? '-') ?></code></td></tr>
                    <tr><th>Print Count</th><td><?= (int)$card['print_count'] ?></td></tr>
                </table>

                <?php if ($card['status'] === 'active'): ?>
                <div class="mt-3">
                    <button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#invalidateModal"><i class="fas fa-ban me-1"></i><?= lang('cards.invalidate') ?></button>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>

    <div class="col-md-8">
        <ul class="nav nav-tabs mb-3" role="tablist">
            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-access"><?= lang('cards.recent_access') ?></a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-history"><?= lang('cards.replacement_history') ?></a></li>
        </ul>
        <div class="tab-content">
            <div class="tab-pane fade show active" id="tab-access">
                <div class="card">
                    <div class="card-body">
                        <?php if (empty($accessLog)): ?>
                            <p class="text-muted text-center py-3"><i class="fas fa-info-circle me-1"></i>No access records</p>
                        <?php else: ?>
                        <div class="table-responsive">
                            <table class="table table-sm">
                                <thead><tr><th>Time</th><th>Direction</th><th>Point</th><th>Method</th><th>Granted</th></tr></thead>
                                <tbody>
                                <?php foreach ($accessLog as $log): ?>
                                <tr>
                                    <td><?= e($log['access_time']) ?></td>
                                    <td><?= $log['direction'] === 'entry' ? '<span class="text-success"><i class="fas fa-sign-in-alt"></i> Entry</span>' : '<span class="text-info"><i class="fas fa-sign-out-alt"></i> Exit</span>' ?></td>
                                    <td><?= e($log['point_name_en']) ?></td>
                                    <td><span class="badge bg-secondary"><?= e($log['access_method']) ?></span></td>
                                    <td><?= $log['granted'] ? '<span class="text-success"><i class="fas fa-check"></i></span>' : '<span class="text-danger"><i class="fas fa-times"></i> ' . e($log['denial_reason']) . '</span>' ?></td>
                                </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
            <div class="tab-pane fade" id="tab-history">
                <div class="card">
                    <div class="card-body">
                        <?php if (empty($history)): ?>
                            <p class="text-muted text-center py-3">No replacement history</p>
                        <?php else: ?>
                        <div class="table-responsive">
                            <table class="table table-sm">
                                <thead><tr><th>Card #</th><th>Status</th><th>Issue Date</th><th>Reason</th></tr></thead>
                                <tbody>
                                <?php foreach ($history as $h): ?>
                                <tr>
                                    <td><a href="/cards/show/<?= $h['id'] ?>"><?= e($h['card_number']) ?></a></td>
                                    <td><?= status_badge($h['status']) ?></td>
                                    <td><?= e($h['issue_date']) ?></td>
                                    <td><?= e($h['status_reason'] ?? '-') ?></td>
                                </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<?php if ($card['status'] === 'active'): ?>
<div class="modal fade" id="invalidateModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form id="invalidateForm" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-header">
                    <h5 class="modal-title"><?= lang('cards.invalidate') ?></h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <div class="mb-3">
                        <label class="form-label">New Status <span class="text-danger">*</span></label>
                        <select name="new_status" class="form-select" required>
                            <option value="lost">Lost</option>
                            <option value="stolen">Stolen</option>
                            <option value="damaged">Damaged</option>
                            <option value="cancelled">Cancelled</option>
                            <option value="suspended">Suspended</option>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Reason <span class="text-danger">*</span></label>
                        <textarea name="reason" class="form-control" rows="3" required></textarea>
                        <div class="invalid-feedback" data-field="reason"></div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                    <button type="submit" class="btn btn-danger"><i class="fas fa-ban me-1"></i><?= lang('cards.invalidate') ?></button>
                </div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$('#invalidateForm').on('submit', function(e){
    e.preventDefault();
    const form = $(this);
    $.ajax({
        url: '/cards/<?= $card['id'] ?>/invalidate',
        method: 'POST',
        data: form.serialize(),
        success: function(res){
            if(res.success){
                Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}).then(()=>location.reload());
            } else {
                Swal.fire({icon:'error',title:'Error',text:res.message});
            }
        }
    });
});
</script>
<?php $__view->endPush() ?>
<?php endif; ?>
```

## File: modules\cards\views\cards\types.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tags me-2"></i><?= lang('cards.card_types') ?></h4>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#typeModal" onclick="resetTypeForm()"><i class="fas fa-plus me-1"></i>Add Type</button>
</div>

<div class="card">
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-striped table-hover">
                <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>For</th><th>Validity</th><th>Fee</th><th>Replace Fee</th><th>Active</th><th>Actions</th></tr></thead>
                <tbody>
                <?php foreach ($types as $t): ?>
                <tr>
                    <td><code><?= e($t['type_code']) ?></code></td>
                    <td><?= e($t['name_en']) ?></td>
                    <td><?= e($t['name_ar']) ?></td>
                    <td><span class="badge bg-info"><?= e($t['applicable_to']) ?></span></td>
                    <td><?= $t['validity_months'] ? $t['validity_months'] . ' months' : 'N/A' ?></td>
                    <td><?= number_format($t['fee'], 2) ?></td>
                    <td><?= number_format($t['replacement_fee'], 2) ?></td>
                    <td><?= $t['is_active'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>' ?></td>
                    <td><button class="btn btn-sm btn-outline-warning btn-edit-type" data-type='<?= e(json_encode($t)) ?>'><i class="fas fa-edit"></i></button></td>
                </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>

<div class="modal fade" id="typeModal" tabindex="-1">
    <div class="modal-dialog modal-lg"><div class="modal-content">
        <form id="typeForm">
            <?= csrf_field() ?>
            <input type="hidden" name="_type_id" id="typeId">
            <div class="modal-header"><h5 class="modal-title" id="typeModalTitle">Add Card Type</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="row g-3">
                    <div class="col-md-4"><label class="form-label">Code *</label><input type="text" name="type_code" class="form-control" id="typeCode" required></div>
                    <div class="col-md-4"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" required></div>
                    <div class="col-md-4"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" required></div>
                    <div class="col-md-4"><label class="form-label">Applicable To</label><select name="applicable_to" class="form-select"><option value="member">Member</option><option value="dependent">Dependent</option><option value="guest">Guest</option><option value="staff">Staff</option></select></div>
                    <div class="col-md-4"><label class="form-label">Validity (months)</label><input type="number" name="validity_months" class="form-control" min="1"></div>
                    <div class="col-md-4"><label class="form-label">Fee *</label><input type="number" name="fee" class="form-control" step="0.01" min="0" required></div>
                    <div class="col-md-4"><label class="form-label">Replacement Fee *</label><input type="number" name="replacement_fee" class="form-control" step="0.01" min="0" required></div>
                    <div class="col-md-2"><label class="form-label">Photo</label><select name="has_photo" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                    <div class="col-md-2"><label class="form-label">Barcode</label><select name="has_barcode" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                    <div class="col-md-2"><label class="form-label">QR Code</label><select name="has_qr_code" class="form-select"><option value="0">No</option><option value="1">Yes</option></select></div>
                    <div class="col-md-2"><label class="form-label">Active</label><select name="is_active" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
            </div>
        </form>
    </div></div>
</div>

<?php $__view->push('scripts') ?>
<script>
function resetTypeForm(){
    $('#typeForm')[0].reset(); $('#typeId').val(''); $('#typeCode').prop('disabled',false); $('#typeModalTitle').text('Add Card Type');
}

$(document).on('click', '.btn-edit-type', function(){
    const t = $(this).data('type');
    $('#typeId').val(t.id); $('#typeCode').val(t.type_code).prop('disabled',true);
    $('#typeForm [name="name_en"]').val(t.name_en); $('#typeForm [name="name_ar"]').val(t.name_ar);
    $('#typeForm [name="applicable_to"]').val(t.applicable_to); $('#typeForm [name="validity_months"]').val(t.validity_months);
    $('#typeForm [name="fee"]').val(t.fee); $('#typeForm [name="replacement_fee"]').val(t.replacement_fee);
    $('#typeForm [name="has_photo"]').val(t.has_photo); $('#typeForm [name="has_barcode"]').val(t.has_barcode);
    $('#typeForm [name="has_qr_code"]').val(t.has_qr_code); $('#typeForm [name="is_active"]').val(t.is_active);
    $('#typeModalTitle').text('Edit Card Type');
    new bootstrap.Modal('#typeModal').show();
});

$('#typeForm').on('submit', function(e){
    e.preventDefault();
    const id = $('#typeId').val();
    const url = id ? '/cards/types/'+id : '/cards/types';
    $.post(url, $(this).serialize(), function(res){
        if(res.success){ location.reload(); } else { Swal.fire({icon:'error',title:'Error',text:res.message || JSON.stringify(res.errors)}); }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\card-types\form.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $cardType ? lang('cards.edit_card_type') : lang('cards.create_card_type') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $formAction ?>" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="type_code" class="form-control" value="<?= e($cardType['type_code'] ?? '') ?>" required><div class="invalid-feedback" data-field="type_code"></div></div>
                <div class="col-md-4"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" value="<?= e($cardType['name_en'] ?? '') ?>" required><div class="invalid-feedback" data-field="name_en"></div></div>
                <div class="col-md-4"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" value="<?= e($cardType['name_ar'] ?? '') ?>" required><div class="invalid-feedback" data-field="name_ar"></div></div>
                <div class="col-md-4"><label class="form-label">Applicable To <span class="text-danger">*</span></label>
                    <select name="applicable_to" class="form-select" required>
                        <?php foreach(['member','dependent','guest','staff'] as $v): ?><option value="<?= $v ?>" <?= selected($v, $cardType['applicable_to'] ?? 'member') ?>><?= ucfirst($v) ?></option><?php endforeach; ?>
                    </select></div>
                <div class="col-md-4"><label class="form-label">Validity (months)</label><input type="number" name="validity_months" class="form-control" value="<?= e($cardType['validity_months'] ?? '') ?>"></div>
                <div class="col-md-4"><label class="form-label">Fee <span class="text-danger">*</span></label><input type="number" step="0.01" name="fee" class="form-control" value="<?= e($cardType['fee'] ?? '0') ?>" required></div>
                <div class="col-md-4"><label class="form-label">Replacement Fee <span class="text-danger">*</span></label><input type="number" step="0.01" name="replacement_fee" class="form-control" value="<?= e($cardType['replacement_fee'] ?? '0') ?>" required></div>
                <div class="col-md-8">
                    <div class="d-flex gap-4 mt-4">
                        <div class="form-check"><input type="checkbox" name="has_photo" class="form-check-input" id="has_photo" <?= checked(1, $cardType['has_photo'] ?? 1) ?>><label class="form-check-label" for="has_photo">Photo</label></div>
                        <div class="form-check"><input type="checkbox" name="has_barcode" class="form-check-input" id="has_barcode" <?= checked(1, $cardType['has_barcode'] ?? 1) ?>><label class="form-check-label" for="has_barcode">Barcode</label></div>
                        <div class="form-check"><input type="checkbox" name="has_qr_code" class="form-check-input" id="has_qr" <?= checked(1, $cardType['has_qr_code'] ?? 0) ?>><label class="form-check-label" for="has_qr">QR Code</label></div>
                        <div class="form-check"><input type="checkbox" name="has_rfid" class="form-check-input" id="has_rfid" <?= checked(1, $cardType['has_rfid'] ?? 0) ?>><label class="form-check-label" for="has_rfid">RFID</label></div>
                        <div class="form-check"><input type="checkbox" name="is_active" class="form-check-input" id="is_active" <?= checked(1, $cardType['is_active'] ?? 1) ?>><label class="form-check-label" for="is_active">Active</label></div>
                    </div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/card-types" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\cards\views\card-types\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tags me-2"></i><?= lang('cards.card_types') ?></h4>
    <a href="/card-types/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card"><div class="card-body">
    <table id="types-table" class="table table-striped w-100" data-url="/card-types/datatable"
           data-columns='[
               {"data":"id","title":"#","width":"50px"},
               {"data":"type_code","title":"Code"},
               {"data":"name_en","title":"Name (EN)"},
               {"data":"name_ar","title":"Name (AR)"},
               {"data":"applicable_to","title":"For"},
               {"data":"fee","title":"Fee","render":"renderMoney"},
               {"data":"is_active","title":"Active","render":"renderBool"},
               {"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderTypeActions"}
           ]'>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
function renderBool(d){return d==1?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-danger"></i>';}
function renderMoney(d){return parseFloat(d||0).toFixed(2);}
function renderTypeActions(d){return '<a href="/card-types/'+d+'/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>';}
document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('types-table');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\carnets\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-6">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-ticket-alt me-2"></i><?= lang('cards.issue_carnet') ?></h5></div>
            <div class="card-body">
                <form id="carnetForm" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label">Member <span class="text-danger">*</span></label>
                        <select name="member_id" class="form-select select2" id="carnetMemberSearch" required></select>
                        <div class="invalid-feedback" data-field="member_id"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Carnet Type <span class="text-danger">*</span></label>
                        <select name="carnet_type_id" class="form-select" required>
                            <option value="">-- Select --</option>
                            <?php foreach ($carnetTypes as $ct): ?>
                            <option value="<?= $ct['id'] ?>"><?= e($ct['name_en']) ?> (<?= e($ct['name_ar']) ?>) â€” <?= (int)$ct['max_entries'] ?> entries â€” <?= number_format($ct['price'], 2) ?></option>
                            <?php endforeach; ?>
                        </select>
                        <div class="invalid-feedback" data-field="carnet_type_id"></div>
                    </div>
                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('cards.issue_carnet') ?></button>
                        <a href="/carnets" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    $('#carnetMemberSearch').select2({
        theme:'bootstrap-5', width:'100%',
        ajax:{ url:'/api/members/search', dataType:'json', delay:300, data:function(p){return{q:p.term};}, processResults:function(d){return{results:d.map(m=>({id:m.id,text:m.membership_number+' - '+m.text}))};} },
        minimumInputLength:2, placeholder:'Search member...'
    });
    $('#carnetForm').on('submit', function(e){
        e.preventDefault();
        const form = $(this), btn = form.find('[type="submit"]');
        btn.prop('disabled',true);
        $.ajax({
            url: '/carnets', method: 'POST', data: form.serialize(),
            success: function(res){
                if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}).then(()=>{window.location.href=res.redirect||'/carnets';}); }
                else { if(res.message) Swal.fire({icon:'error',title:'Error',text:res.message}); }
            },
            complete: function(){ btn.prop('disabled',false); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\carnets\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-ticket-alt me-2"></i><?= lang('cards.all_carnets') ?></h4>
    <?php if (auth_can('cards.manage')): ?>
    <a href="/carnets/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('cards.issue_carnet') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3"><div class="card-body"><div class="row g-2">
    <div class="col-md-3"><select id="filter-cstatus" class="form-select form-select-sm"><option value="">All Statuses</option><option value="active">Active</option><option value="purchased">Purchased</option><option value="exhausted">Exhausted</option><option value="expired">Expired</option><option value="cancelled">Cancelled</option></select></div>
</div></div></div>

<div class="card"><div class="card-body">
    <table id="carnetsTable" class="table table-striped table-hover w-100">
        <thead><tr><th>#</th><th>Carnet #</th><th>Member</th><th>Type</th><th>Used/Total</th><th>Remaining</th><th>Expiry</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = $('#carnetsTable').DataTable({
        processing: true, serverSide: true,
        ajax: { url: '/api/carnets/datatable', type: 'GET', data: function(d){ d.status = $('#filter-cstatus').val(); }},
        columns: [
            { data: 'id' },
            { data: 'carnet_number', render: function(d,t,r){ return `<a href="/carnets/show/${r.id}" class="fw-bold">${d}</a>`; }},
            { data: 'member_name_en', render: function(d,t,r){ return `${d}<br><small class="text-muted">${r.membership_number}</small>`; }},
            { data: 'type_name_en' },
            { data: 'used_entries', render: function(d,t,r){ return `${d} / ${r.total_entries}`; }},
            { data: 'remaining_entries', render: function(d){ return d <= 0 ? '<span class="badge bg-danger">0</span>' : `<span class="badge bg-success">${d}</span>`; }},
            { data: 'expiry_date', render: function(d){ return d||'N/A'; }},
            { data: 'status', render: function(d){ const m = {active:'success',purchased:'info',exhausted:'warning',expired:'secondary',cancelled:'dark',frozen:'info'}; return `<span class="badge bg-${m[d]||'secondary'}">${d}</span>`; }},
            { data: 'id', orderable: false, render: function(d){ return `<a href="/carnets/show/${d}" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>`; }}
        ],
        order: [[0, 'desc']], pageLength: 25
    });
    $('#filter-cstatus').on('change', function(){ dt.ajax.reload(); });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\carnets\show.php

```php
<div class="row g-4">
    <div class="col-md-4">
        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h5 class="mb-0"><?= e($carnet['carnet_number']) ?></h5>
                <?= status_badge($carnet['status']) ?>
            </div>
            <div class="card-body">
                <div class="text-center mb-3">
                    <div class="display-4 fw-bold <?= $carnet['remaining_entries'] <= 0 ? 'text-danger' : 'text-success' ?>"><?= (int)$carnet['remaining_entries'] ?></div>
                    <small class="text-muted">of <?= (int)$carnet['total_entries'] ?> remaining</small>
                    <div class="progress mt-2" style="height:8px;">
                        <?php $pct = $carnet['total_entries'] > 0 ? round(($carnet['used_entries'] / $carnet['total_entries']) * 100) : 0; ?>
                        <div class="progress-bar <?= $pct > 80 ? 'bg-danger' : ($pct > 50 ? 'bg-warning' : 'bg-success') ?>" style="width:<?= $pct ?>%"></div>
                    </div>
                </div>
                <table class="table table-sm">
                    <tr><th>Type</th><td><?= e($carnet['type_name_en']) ?></td></tr>
                    <tr><th>Member</th><td><a href="/members/show/<?= $carnet['member_id'] ?>"><?= e($carnet['member_name_en']) ?></a><br><small><?= e($carnet['membership_number']) ?></small></td></tr>
                    <tr><th>Purchase Date</th><td><?= e($carnet['purchase_date']) ?></td></tr>
                    <tr><th>Expiry</th><td><?= e($carnet['expiry_date'] ?? 'N/A') ?></td></tr>
                    <tr><th>Price Paid</th><td><?= money($carnet['price_paid']) ?></td></tr>
                </table>

                <?php if ($carnet['status'] === 'active' && $carnet['remaining_entries'] > 0): ?>
                <form id="entryForm" class="mt-3">
                    <?= csrf_field() ?>
                    <div class="mb-2"><label class="form-label">Facility (optional)</label>
                        <select name="facility_id" class="form-select form-select-sm">
                            <option value="">N/A</option>
                        </select>
                    </div>
                    <div class="mb-2"><label class="form-label">Notes</label><input type="text" name="notes" class="form-control form-control-sm"></div>
                    <button type="submit" class="btn btn-success btn-sm w-100"><i class="fas fa-stamp me-1"></i>Record Entry</button>
                </form>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-8">
        <div class="card"><div class="card-header"><h6 class="mb-0"><i class="fas fa-list me-2"></i>Entry Log (<?= count($entries) ?>)</h6></div><div class="card-body">
            <?php if (empty($entries)): ?>
                <p class="text-muted text-center py-3">No entries recorded yet</p>
            <?php else: ?>
            <div class="table-responsive">
                <table class="table table-sm table-striped">
                    <thead><tr><th>#</th><th>Date</th><th>Time</th><th>Facility</th><th>Status</th><th>Actions</th></tr></thead>
                    <tbody>
                    <?php foreach ($entries as $e_entry): ?>
                    <tr class="<?= $e_entry['is_voided'] ? 'text-decoration-line-through text-muted' : '' ?>">
                        <td><?= (int)$e_entry['entry_number'] ?></td>
                        <td><?= e($e_entry['entry_date']) ?></td>
                        <td><?= e($e_entry['entry_time']) ?></td>
                        <td><?= e($e_entry['facility_name_en'] ?? '-') ?></td>
                        <td><?= $e_entry['is_voided'] ? '<span class="badge bg-danger">Voided</span>' : '<span class="badge bg-success">Valid</span>' ?></td>
                        <td>
                            <?php if (!$e_entry['is_voided']): ?>
                            <button class="btn btn-sm btn-outline-danger btn-void-entry" data-id="<?= $e_entry['id'] ?>"><i class="fas fa-undo"></i></button>
                            <?php else: ?>
                            <small><?= e($e_entry['void_reason'] ?? '') ?></small>
                            <?php endif; ?>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
            <?php endif; ?>
        </div></div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    $('#entryForm').on('submit', function(e){
        e.preventDefault();
        $.post('/carnets/<?= $carnet['id'] ?>/entry', $(this).serialize(), function(res){
            if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1000,showConfirmButton:false}).then(()=>location.reload()); }
            else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
        });
    });

    $(document).on('click', '.btn-void-entry', function(){
        const entryId = $(this).data('id');
        Swal.fire({title:'Void this entry?',input:'text',inputPlaceholder:'Reason for voiding',showCancelButton:true,confirmButtonText:'Void',confirmButtonColor:'#dc3545'}).then(r=>{
            if(r.isConfirmed){
                $.post('/carnets/<?= $carnet['id'] ?>/void-entry/'+entryId, {_token:$('meta[name="csrf-token"]').attr('content'),reason:r.value}, function(res){
                    if(res.success){ location.reload(); } else { Swal.fire({icon:'error',text:res.message}); }
                });
            }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\carnets\types.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tags me-2"></i><?= lang('cards.carnet_types') ?></h4>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#ctModal" onclick="resetCtForm()"><i class="fas fa-plus me-1"></i>Add Type</button>
</div>

<div class="card"><div class="card-body"><div class="table-responsive">
    <table class="table table-striped">
        <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Entries</th><th>Validity</th><th>Price</th><th>Active</th><th>Actions</th></tr></thead>
        <tbody>
        <?php foreach ($types as $t): ?>
        <tr>
            <td><code><?= e($t['type_code']) ?></code></td>
            <td><?= e($t['name_en']) ?></td>
            <td><?= e($t['name_ar']) ?></td>
            <td><?= (int)$t['max_entries'] ?></td>
            <td><?= $t['validity_days'] ? $t['validity_days'].' days' : 'N/A' ?></td>
            <td><?= number_format($t['price'], 2) ?></td>
            <td><?= $t['is_active'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>' ?></td>
            <td><button class="btn btn-sm btn-outline-warning btn-edit-ct" data-ct='<?= e(json_encode($t)) ?>'><i class="fas fa-edit"></i></button></td>
        </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
</div></div></div>

<div class="modal fade" id="ctModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <form id="ctForm"><?= csrf_field() ?><input type="hidden" name="_ct_id" id="ctId">
        <div class="modal-header"><h5 class="modal-title" id="ctModalTitle">Add Carnet Type</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body">
            <div class="row g-3">
                <div class="col-12"><label class="form-label">Code *</label><input type="text" name="type_code" class="form-control" id="ctCode" required></div>
                <div class="col-md-6"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" required></div>
                <div class="col-md-6"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" required></div>
                <div class="col-md-4"><label class="form-label">Max Entries *</label><input type="number" name="max_entries" class="form-control" min="1" required></div>
                <div class="col-md-4"><label class="form-label">Validity (days)</label><input type="number" name="validity_days" class="form-control" min="1"></div>
                <div class="col-md-4"><label class="form-label">Price *</label><input type="number" name="price" class="form-control" step="0.01" min="0" required></div>
                <div class="col-md-4"><label class="form-label">Active</label><select name="is_active" class="form-select"><option value="1">Yes</option><option value="0">No</option></select></div>
            </div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
            <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
        </div>
    </form>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
function resetCtForm(){ $('#ctForm')[0].reset(); $('#ctId').val(''); $('#ctCode').prop('disabled',false); $('#ctModalTitle').text('Add Carnet Type'); }
$(document).on('click', '.btn-edit-ct', function(){
    const t = $(this).data('ct'); $('#ctId').val(t.id); $('#ctCode').val(t.type_code).prop('disabled',true);
    Object.keys(t).forEach(k => { const el=$(`#ctForm [name="${k}"]`); if(el.length) el.val(t[k]); });
    $('#ctModalTitle').text('Edit Carnet Type'); new bootstrap.Modal('#ctModal').show();
});
$('#ctForm').on('submit', function(e){
    e.preventDefault(); const id = $('#ctId').val();
    $.post(id ? '/carnets/types/'+id : '/carnets/types', $(this).serialize(), function(res){
        if(res.success) location.reload(); else Swal.fire({icon:'error',text:res.message||JSON.stringify(res.errors)});
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\carnet-types\form.php

```php
<div class="row justify-content-center"><div class="col-lg-6"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $carnetType ? lang('cards.edit_carnet_type') : lang('cards.create_carnet_type') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $formAction ?>" data-ajax="true"><?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6"><label class="form-label">Code *</label><input type="text" name="type_code" class="form-control" value="<?= e($carnetType['type_code'] ?? '') ?>" required><div class="invalid-feedback" data-field="type_code"></div></div>
                <div class="col-md-6"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" value="<?= e($carnetType['name_en'] ?? '') ?>" required></div>
                <div class="col-md-6"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" value="<?= e($carnetType['name_ar'] ?? '') ?>" required></div>
                <div class="col-md-6"><label class="form-label">Max Entries *</label><input type="number" name="max_entries" class="form-control" value="<?= e($carnetType['max_entries'] ?? '') ?>" required min="1"></div>
                <div class="col-md-6"><label class="form-label">Validity (days)</label><input type="number" name="validity_days" class="form-control" value="<?= e($carnetType['validity_days'] ?? '') ?>"><small class="text-muted">Leave empty for unlimited</small></div>
                <div class="col-md-6"><label class="form-label">Price *</label><input type="number" step="0.01" name="price" class="form-control" value="<?= e($carnetType['price'] ?? '0') ?>" required></div>
                <div class="col-md-12"><div class="d-flex gap-4">
                    <div class="form-check"><input type="checkbox" name="is_transferable" class="form-check-input" id="transfer" <?= checked(1, $carnetType['is_transferable'] ?? 0) ?>><label class="form-check-label" for="transfer">Transferable</label></div>
                    <div class="form-check"><input type="checkbox" name="is_active" class="form-check-input" id="active" <?= checked(1, $carnetType['is_active'] ?? 1) ?>><label class="form-check-label" for="active">Active</label></div>
                </div></div>
            </div>
            <div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button><a href="/carnet-types" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
```

## File: modules\cards\views\carnet-types\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tags me-2"></i><?= lang('cards.carnet_types') ?></h4>
    <a href="/carnet-types/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card"><div class="card-body">
    <table id="ct-table" class="table table-striped w-100" data-url="/carnet-types/datatable"
           data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"type_code","title":"Code"},{"data":"name_en","title":"Name"},{"data":"max_entries","title":"Entries"},{"data":"validity_days","title":"Days"},{"data":"price","title":"Price","render":"renderMoney"},{"data":"is_active","title":"Active","render":"renderBool"},{"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderCTActions"}]'>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
function renderBool(d){return d==1?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-danger"></i>';}
function renderMoney(d){return parseFloat(d||0).toFixed(2);}
function renderCTActions(d){return '<a href="/carnet-types/'+d+'/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>';}
document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('ct-table');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\guest-blacklist\create.php

```php
<div class="row justify-content-center"><div class="col-lg-6"><div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-ban me-2"></i><?= lang('cards.add_to_blacklist') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/guest-blacklist" data-ajax="true"><?= csrf_field() ?>
            <div class="mb-3"><label class="form-label">Guest Name <span class="text-danger">*</span></label><input type="text" name="guest_name" class="form-control" required><div class="invalid-feedback" data-field="guest_name"></div></div>
            <div class="mb-3"><label class="form-label">National ID</label><input type="text" name="guest_national_id" class="form-control"></div>
            <div class="mb-3"><label class="form-label">Phone</label><input type="text" name="guest_phone" class="form-control"></div>
            <div class="mb-3"><label class="form-label">Reason <span class="text-danger">*</span></label><textarea name="reason" class="form-control" rows="3" required></textarea><div class="invalid-feedback" data-field="reason"></div></div>
            <div class="mb-3"><label class="form-label">Expires At</label><input type="date" name="expires_at" class="form-control"><small class="text-muted">Leave empty for permanent</small></div>
            <div class="d-flex gap-2"><button type="submit" class="btn btn-danger"><i class="fas fa-ban me-1"></i>Blacklist</button><a href="/guest-blacklist" class="btn btn-secondary"><?= lang('common.cancel') ?></a></div>
        </form>
    </div>
</div></div></div>
```

## File: modules\cards\views\guest-blacklist\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-ban me-2"></i><?= lang('cards.guest_blacklist') ?></h4>
    <?php if (auth_can('guests.create')): ?><a href="/guest-blacklist/create" class="btn btn-danger"><i class="fas fa-plus me-1"></i><?= lang('cards.add_to_blacklist') ?></a><?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="bl-table" class="table table-striped w-100" data-url="/guest-blacklist/datatable"
           data-columns='[{"data":"id","title":"#","width":"50px"},{"data":"guest_name","title":"Name"},{"data":"guest_national_id","title":"National ID"},{"data":"guest_phone","title":"Phone"},{"data":"reason","title":"Reason"},{"data":"blacklisted_date","title":"Date"},{"data":"status","title":"Status","render":"renderStatusBadge"},{"data":"id","title":"Actions","orderable":false,"searchable":false,"render":"renderBLActions"}]'>
    </table>
</div></div>
<?php $__view->push('scripts') ?>
<script>
function renderBLActions(d,t,r){
    if(r.status==='active') return '<button class="btn btn-sm btn-success" onclick="removeBL('+d+')"><i class="fas fa-undo me-1"></i>Remove</button>';
    return '<span class="text-muted">'+r.status+'</span>';
}
function removeBL(id){Swal.fire({title:'Remove from blacklist?',input:'textarea',inputPlaceholder:'Reason',showCancelButton:true,confirmButtonText:'Remove'}).then(r=>{
    if(r.isConfirmed){$.post('/guest-blacklist/'+id+'/remove',{_token:'<?= csrf_token() ?>',reason:r.value},function(d){if(d.success){FormHelper.showToast('success',d.message);DataTableHelper.reload('bl-table');}});}
});}
document.addEventListener('DOMContentLoaded',function(){DataTableHelper.init('bl-table');});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\guests\blacklist.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-ban me-2"></i><?= lang('cards.guest_blacklist') ?></h4>
    <button class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#addBlModal"><i class="fas fa-plus me-1"></i>Add to Blacklist</button>
</div>

<div class="card"><div class="card-body">
    <table id="blacklistTable" class="table table-striped table-hover w-100">
        <thead><tr><th>#</th><th>Name</th><th>National ID</th><th>Phone</th><th>Reason</th><th>Date</th><th>Expires</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<div class="modal fade" id="addBlModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <form id="addBlForm"><?= csrf_field() ?>
        <div class="modal-header"><h5 class="modal-title">Add to Blacklist</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body">
            <div class="mb-3"><label class="form-label">Name *</label><input type="text" name="guest_name" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">National ID</label><input type="text" name="guest_national_id" class="form-control"></div>
            <div class="mb-3"><label class="form-label">Phone</label><input type="text" name="guest_phone" class="form-control"></div>
            <div class="mb-3"><label class="form-label">Reason *</label><textarea name="reason" class="form-control" rows="3" required></textarea></div>
            <div class="mb-3"><label class="form-label">Expires On</label><input type="date" name="expires_at" class="form-control datepicker"><small class="text-muted">Leave blank for permanent</small></div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
            <button type="submit" class="btn btn-danger"><i class="fas fa-ban me-1"></i>Add to Blacklist</button>
        </div>
    </form>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = $('#blacklistTable').DataTable({
        processing: true, serverSide: true,
        ajax: { url: '/api/guests/blacklist/datatable', type: 'GET' },
        columns: [
            { data: 'id' },
            { data: 'guest_name' },
            { data: 'guest_national_id', render: function(d){ return d||'-'; }},
            { data: 'guest_phone', render: function(d){ return d||'-'; }},
            { data: 'reason', render: function(d){ return d.length>60?d.substring(0,60)+'...':d; }},
            { data: 'blacklisted_date' },
            { data: 'expires_at', render: function(d){ return d||'<span class="badge bg-danger">Permanent</span>'; }},
            { data: 'id', orderable: false, render: function(d){ return `<button class="btn btn-sm btn-outline-success btn-remove-bl" data-id="${d}"><i class="fas fa-undo me-1"></i>Remove</button>`; }}
        ],
        order: [[0, 'desc']]
    });

    $('#addBlForm').on('submit', function(e){
        e.preventDefault();
        $.post('/guests/blacklist', $(this).serialize(), function(res){
            if(res.success){ bootstrap.Modal.getInstance('#addBlModal').hide(); Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
            else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
        });
    });

    $(document).on('click', '.btn-remove-bl', function(){
        const id = $(this).data('id');
        Swal.fire({title:'Remove from blacklist?',input:'text',inputPlaceholder:'Removal reason',showCancelButton:true,confirmButtonText:'Remove'}).then(r=>{
            if(r.isConfirmed){
                $.post('/guests/blacklist/'+id+'/remove', {_token:$('meta[name="csrf-token"]').attr('content'),removal_reason:r.value}, function(res){
                    if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
                });
            }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\guests\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-ticket-alt me-2"></i><?= lang('cards.issue_pass') ?></h5></div>
            <div class="card-body">
                <form id="guestForm" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Host Member <span class="text-danger">*</span></label>
                            <select name="host_member_id" class="form-select select2" id="hostSearch" required></select>
                            <div class="invalid-feedback" data-field="host_member_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Pass Type <span class="text-danger">*</span></label>
                            <select name="pass_type_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($passTypes as $pt): ?>
                                <option value="<?= $pt['id'] ?>"><?= e($pt['name_en']) ?> (<?= e($pt['name_ar']) ?>) â€” <?= number_format($pt['fee'], 2) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="pass_type_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Guest Name <span class="text-danger">*</span></label>
                            <input type="text" name="guest_name" class="form-control" required>
                            <div class="invalid-feedback" data-field="guest_name"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Guest Phone</label>
                            <input type="text" name="guest_phone" class="form-control">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Guest National ID</label>
                            <input type="text" name="guest_national_id" class="form-control" id="guestNid">
                            <div class="invalid-feedback" data-field="guest_national_id"></div>
                            <div id="blacklistWarning" class="text-danger mt-1" style="display:none;"><i class="fas fa-exclamation-triangle me-1"></i>This guest is BLACKLISTED!</div>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Gender</label>
                            <select name="guest_gender" class="form-select">
                                <option value="">--</option>
                                <option value="male">Male</option>
                                <option value="female">Female</option>
                            </select>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label">Valid Date <span class="text-danger">*</span></label>
                            <input type="date" name="valid_date" class="form-control datepicker" value="<?= date('Y-m-d') ?>" required>
                            <div class="invalid-feedback" data-field="valid_date"></div>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('cards.issue_pass') ?></button>
                        <a href="/guests" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    $('#hostSearch').select2({
        theme: 'bootstrap-5', width: '100%',
        ajax: { url: '/api/members/search', dataType: 'json', delay: 300, data: function(p){ return {q:p.term}; }, processResults: function(d){ return {results: d.map(m=>({id:m.id,text:m.membership_number+' - '+m.text}))}; } },
        minimumInputLength: 2, placeholder: 'Search host member...'
    });

    let blDebounce;
    $('#guestNid').on('input', function(){
        clearTimeout(blDebounce);
        const nid = $(this).val();
        if(nid.length >= 10){
            blDebounce = setTimeout(function(){
                $.get('/api/guests/check-blacklist', {national_id: nid}, function(res){
                    $('#blacklistWarning').toggle(res.blacklisted === true);
                });
            }, 500);
        } else { $('#blacklistWarning').hide(); }
    });

    $('#guestForm').on('submit', function(e){
        e.preventDefault();
        const form = $(this), btn = form.find('[type="submit"]');
        form.find('.is-invalid').removeClass('is-invalid');
        btn.prop('disabled',true).html('<span class="spinner-border spinner-border-sm me-1"></span>Processing...');
        $.ajax({
            url: '/guests', method: 'POST', data: form.serialize(),
            success: function(res){
                if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}).then(()=>{ window.location.href = res.redirect || '/guests'; }); }
                else {
                    if(res.errors) Object.entries(res.errors).forEach(([k,v])=>{ form.find(`[name="${k}"]`).addClass('is-invalid'); form.find(`[data-field="${k}"]`).text(Array.isArray(v)?v[0]:v).show(); });
                    if(res.message) Swal.fire({icon:'error',title:'Error',text:res.message});
                }
            },
            complete: function(){ btn.prop('disabled',false).html('<i class="fas fa-save me-1"></i><?= lang("cards.issue_pass") ?>'); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\guests\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-4"><div class="card border-primary h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Active Today</h6><h3 class="mb-0"><?= $stats['today_active'] ?></h3></div><div class="align-self-center"><i class="fas fa-user-friends fa-2x text-primary opacity-50"></i></div></div></div></div></div>
    <div class="col-md-4"><div class="card border-success h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Used Today</h6><h3 class="mb-0"><?= $stats['today_used'] ?></h3></div><div class="align-self-center"><i class="fas fa-check-circle fa-2x text-success opacity-50"></i></div></div></div></div></div>
    <div class="col-md-4"><div class="card border-info h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">This Month</h6><h3 class="mb-0"><?= $stats['month_total'] ?></h3></div><div class="align-self-center"><i class="fas fa-calendar-alt fa-2x text-info opacity-50"></i></div></div></div></div></div>
</div>

<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-user-friends me-2"></i><?= lang('cards.guest_passes') ?></h4>
    <?php if (auth_can('guests.create')): ?>
    <a href="/guests/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('cards.issue_pass') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3"><div class="card-body">
    <div class="row g-2">
        <div class="col-md-2"><select id="filter-gstatus" class="form-select form-select-sm"><option value="">All Statuses</option><option value="issued">Issued</option><option value="active">Active</option><option value="used">Used</option><option value="expired">Expired</option><option value="cancelled">Cancelled</option></select></div>
        <div class="col-md-2"><input type="date" id="filter-gfrom" class="form-control form-control-sm" value="<?= date('Y-m-01') ?>"></div>
        <div class="col-md-2"><input type="date" id="filter-gto" class="form-control form-control-sm" value="<?= date('Y-m-d') ?>"></div>
    </div>
</div></div>

<div class="card"><div class="card-body">
    <table id="guestsTable" class="table table-striped table-hover w-100">
        <thead><tr><th>#</th><th>Pass #</th><th>Guest</th><th>Host Member</th><th>Type</th><th>Date</th><th>Check In</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = $('#guestsTable').DataTable({
        processing: true, serverSide: true,
        ajax: {
            url: '/api/guests/datatable', type: 'GET',
            data: function(d){ d.status = $('#filter-gstatus').val(); d.date_from = $('#filter-gfrom').val(); d.date_to = $('#filter-gto').val(); }
        },
        columns: [
            { data: 'id' },
            { data: 'pass_number', render: function(d,t,r){ return `<a href="/guests/show/${r.id}" class="fw-bold">${d}</a>`; }},
            { data: 'guest_name', render: function(d,t,r){ return `${d}${r.guest_phone?'<br><small class="text-muted">'+r.guest_phone+'</small>':''}`; }},
            { data: 'host_name_en', render: function(d,t,r){ return `${d}<br><small class="text-muted">${r.membership_number}</small>`; }},
            { data: 'type_name_en' },
            { data: 'valid_date' },
            { data: 'check_in_time', render: function(d){ return d || '<span class="text-muted">-</span>'; }},
            { data: 'status', render: function(d){ const m = {issued:'warning',active:'success',used:'info',expired:'secondary',cancelled:'dark',revoked:'danger'}; return `<span class="badge bg-${m[d]||'secondary'}">${d}</span>`; }},
            { data: 'id', orderable: false, render: function(d,t,r){
                let btns = `<a href="/guests/show/${d}" class="btn btn-sm btn-outline-primary me-1"><i class="fas fa-eye"></i></a>`;
                if(r.status === 'issued') btns += `<button class="btn btn-sm btn-success me-1 btn-checkin" data-id="${d}"><i class="fas fa-sign-in-alt"></i></button>`;
                if(r.status === 'active') btns += `<button class="btn btn-sm btn-info me-1 btn-checkout" data-id="${d}"><i class="fas fa-sign-out-alt"></i></button>`;
                if(['issued','active'].includes(r.status)) btns += `<button class="btn btn-sm btn-danger btn-cancel-pass" data-id="${d}"><i class="fas fa-times"></i></button>`;
                return btns;
            }}
        ],
        order: [[0, 'desc']], pageLength: 25
    });
    $('#filter-gstatus, #filter-gfrom, #filter-gto').on('change', function(){ dt.ajax.reload(); });

    function postAction(url, msg){
        $.post(url, {_token: $('meta[name="csrf-token"]').attr('content')}, function(res){
            if(res.success){ Swal.fire({icon:'success',title:res.message,timer:1500,showConfirmButton:false}); dt.ajax.reload(); }
            else { Swal.fire({icon:'error',title:'Error',text:res.message}); }
        });
    }
    $(document).on('click', '.btn-checkin', function(){ postAction('/guests/'+$(this).data('id')+'/checkin'); });
    $(document).on('click', '.btn-checkout', function(){ postAction('/guests/'+$(this).data('id')+'/checkout'); });
    $(document).on('click', '.btn-cancel-pass', function(){
        const id = $(this).data('id');
        Swal.fire({title:'Cancel Pass?',icon:'warning',showCancelButton:true,confirmButtonText:'Yes, Cancel'}).then(r=>{
            if(r.isConfirmed) postAction('/guests/'+id+'/cancel');
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\cards\views\guests\show.php

```php
<div class="row g-4">
    <div class="col-md-5">
        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h5 class="mb-0"><?= e($pass['pass_number']) ?></h5>
                <?= status_badge($pass['status']) ?>
            </div>
            <div class="card-body">
                <table class="table table-sm">
                    <tr><th>Guest Name</th><td><?= e($pass['guest_name']) ?></td></tr>
                    <tr><th>Guest Phone</th><td><?= e($pass['guest_phone'] ?? '-') ?></td></tr>
                    <tr><th>National ID</th><td><?= e($pass['guest_national_id'] ?? '-') ?></td></tr>
                    <tr><th>Gender</th><td><?= e($pass['guest_gender'] ?? '-') ?></td></tr>
                    <tr><th>Pass Type</th><td><?= e($pass['type_name_en']) ?> / <?= e($pass['type_name_ar']) ?></td></tr>
                    <tr><th>Valid Date</th><td><?= e($pass['valid_date']) ?></td></tr>
                    <tr><th>Fee</th><td><?= money($pass['fee_amount']) ?></td></tr>
                    <tr><th>Host Member</th><td><a href="/members/show/<?= $pass['host_member_id'] ?>"><?= e($pass['host_name_en']) ?></a><br><small><?= e($pass['membership_number']) ?></small></td></tr>
                    <tr><th>Check In</th><td><?= e($pass['check_in_time'] ?? '-') ?></td></tr>
                    <tr><th>Check Out</th><td><?= e($pass['check_out_time'] ?? '-') ?></td></tr>
                </table>
                <div class="d-flex gap-2 mt-3">
                    <?php if ($pass['status'] === 'issued'): ?>
                    <form method="POST" action="/guests/<?= $pass['id'] ?>/checkin"><?= csrf_field() ?><button class="btn btn-success btn-sm"><i class="fas fa-sign-in-alt me-1"></i>Check In</button></form>
                    <?php endif; ?>
                    <?php if ($pass['status'] === 'active'): ?>
                    <form method="POST" action="/guests/<?= $pass['id'] ?>/checkout"><?= csrf_field() ?><button class="btn btn-info btn-sm"><i class="fas fa-sign-out-alt me-1"></i>Check Out</button></form>
                    <?php endif; ?>
                    <?php if (in_array($pass['status'], ['issued', 'active'])): ?>
                    <form method="POST" action="/guests/<?= $pass['id'] ?>/cancel"><?= csrf_field() ?><button class="btn btn-danger btn-sm"><i class="fas fa-times me-1"></i>Cancel</button></form>
                    <?php endif; ?>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-7">
        <div class="card"><div class="card-header"><h6 class="mb-0">Visit Log</h6></div><div class="card-body">
            <?php if (empty($visits)): ?>
                <p class="text-muted text-center py-3">No visits recorded</p>
            <?php else: ?>
            <table class="table table-sm"><thead><tr><th>Date</th><th>Check In</th><th>Check Out</th><th>Host Present</th></tr></thead><tbody>
            <?php foreach ($visits as $v): ?>
            <tr><td><?= e($v['visit_date']) ?></td><td><?= e($v['check_in']) ?></td><td><?= e($v['check_out'] ?? '-') ?></td><td><?= $v['host_present'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-danger"></i>' ?></td></tr>
            <?php endforeach; ?>
            </tbody></table>
            <?php endif; ?>
        </div></div>
    </div>
</div>
```

## File: modules\cards\views\widgets\active_guests.php

```php
<?php
$db = Database::getInstance();
$activeGuests = (int)$db->fetchColumn("SELECT COUNT(*) FROM guest_passes WHERE valid_date = CURDATE() AND status IN ('issued','active')");
?>
<div class="card border-info h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('cards.active_guest_passes') ?></h6>
                <h3 class="mb-0 text-info"><?= number_format($activeGuests) ?></h3>
                <small class="text-muted">Active guest passes today</small>
            </div>
            <div class="align-self-center"><i class="fas fa-user-friends fa-2x text-info opacity-50"></i></div>
        </div>
        <a href="/guests" class="stretched-link"></a>
    </div>
</div>
```

## File: modules\cards\views\widgets\expiring_cards.php

```php
<?php
$db = Database::getInstance();
$expCount = (int)$db->fetchColumn("SELECT COUNT(*) FROM member_cards WHERE status='active' AND expiry_date IS NOT NULL AND expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)");
?>
<div class="card border-warning h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('cards.expiring_30') ?></h6>
                <h3 class="mb-0 text-warning"><?= number_format($expCount) ?></h3>
                <small class="text-muted">Cards expiring within 30 days</small>
            </div>
            <div class="align-self-center"><i class="fas fa-id-card fa-2x text-warning opacity-50"></i></div>
        </div>
        <a href="/cards" class="stretched-link"></a>
    </div>
</div>
```

## File: modules\cards\views\widgets\today_access.php

```php
<?php
$db = Database::getInstance();
$todayEntries = (int)$db->fetchColumn("SELECT COUNT(*) FROM member_access_log WHERE DATE(access_time) = CURDATE() AND direction='entry' AND granted=1");
?>
<div class="card border-success h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('cards.today_entries') ?></h6>
                <h3 class="mb-0 text-success"><?= number_format($todayEntries) ?></h3>
                <small class="text-muted">Entries recorded today</small>
            </div>
            <div class="align-self-center"><i class="fas fa-door-open fa-2x text-success opacity-50"></i></div>
        </div>
        <a href="/access/log" class="stretched-link"></a>
    </div>
</div>
```

## File: modules\communications\Controllers\CommunicationsApiController.php

```php
<?php

class CommunicationsApiController extends Controller
{
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  HELPER: Build DataTable response
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    private function datatable(Request $request, string $baseQuery, array $baseParams, array $columns, array $searchCols = []): void
    {
        $db    = $this->db();
        $draw  = (int)$request->get('draw', 1);
        $start = (int)$request->get('start', 0);
        $len   = (int)$request->get('length', 25);
        $searchVal = trim($request->get('search')['value'] ?? '');

        $orderCol = (int)($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtolower($request->get('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

        // Total
        $totalSql = "SELECT COUNT(*) FROM ({$baseQuery}) AS __t";
        $total = (int)$db->fetchColumn($totalSql, $baseParams);

        // Search
        $filterSql = $baseQuery;
        $filterParams = $baseParams;
        if ($searchVal !== '' && !empty($searchCols)) {
            $searchParts = [];
            foreach ($searchCols as $sc) {
                $searchParts[] = "{$sc} LIKE ?";
                $filterParams[] = "%{$searchVal}%";
            }
            $filterSql .= " AND (" . implode(' OR ', $searchParts) . ")";
        }

        $filteredTotal = (int)$db->fetchColumn("SELECT COUNT(*) FROM ({$filterSql}) AS __t", $filterParams);

        // Order
        $colName = $columns[$orderCol] ?? $columns[0];
        $filterSql .= " ORDER BY {$colName} {$orderDir}";

        // Limit
        $filterSql .= " LIMIT ? OFFSET ?";
        $filterParams[] = $len;
        $filterParams[] = $start;

        $rows = $db->fetchAll($filterSql, $filterParams);

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $total,
            'recordsFiltered' => $filteredTotal,
            'data'            => $rows,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  NOTIFICATION TEMPLATES
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function templatesDatatable(Request $request): void
    {
        $sql = "SELECT nt.id, nt.template_code, nt.name_en, nt.name_ar, nt.event_trigger,
                       nc.name_en AS channel_name, nt.is_active, nt.created_at
                FROM notification_templates nt
                LEFT JOIN notification_channels nc ON nt.channel_id = nc.id
                WHERE 1=1";
        $cols = ['nt.id', 'nt.template_code', 'nt.name_en', 'nc.name_en', 'nt.event_trigger', 'nt.is_active', 'nt.created_at'];
        $search = ['nt.template_code', 'nt.name_en', 'nt.name_ar', 'nt.event_trigger'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function templateStore(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'template_code' => 'required|string|max:50',
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'channel_id'    => 'required|integer|exists:notification_channels,id',
            'event_trigger' => 'required|string|max:100',
            'subject_en'    => 'nullable|string|max:500',
            'subject_ar'    => 'nullable|string|max:500',
            'body_en'       => 'required|string',
            'body_ar'       => 'nullable|string',
            'is_active'     => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;

        // Parse variables from body
        preg_match_all('/\{(\w+)\}/', ($data['body_en'] ?? '') . ($data['body_ar'] ?? ''), $matches);
        $vars = array_unique($matches[1] ?? []);
        $data['variables'] = !empty($vars) ? json_encode(array_values($vars)) : null;

        $db = $this->db();
        $id = $db->insert('notification_templates', $data);

        $this->audit('create', 'notification_templates', $id, 'Created notification template: ' . $data['template_code']);
        $this->json(['success' => true, 'message' => lang('comm.template_created'), 'redirect' => '/communications/templates']);
    }

    public function templateUpdate(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM notification_templates WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Template not found'], 404);
            return;
        }

        $v = new Validator($request->all(), [
            'template_code' => 'required|string|max:50',
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'channel_id'    => 'required|integer|exists:notification_channels,id',
            'event_trigger' => 'required|string|max:100',
            'subject_en'    => 'nullable|string|max:500',
            'subject_ar'    => 'nullable|string|max:500',
            'body_en'       => 'required|string',
            'body_ar'       => 'nullable|string',
            'is_active'     => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;

        preg_match_all('/\{(\w+)\}/', ($data['body_en'] ?? '') . ($data['body_ar'] ?? ''), $matches);
        $vars = array_unique($matches[1] ?? []);
        $data['variables'] = !empty($vars) ? json_encode(array_values($vars)) : null;

        $db->update('notification_templates', $data, 'id = ?', [$id]);

        $this->audit('update', 'notification_templates', $id, 'Updated notification template', $old, $data);
        $this->json(['success' => true, 'message' => lang('comm.template_updated'), 'redirect' => '/communications/templates']);
    }

    public function templateDelete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $used = (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE template_id = ?", [$id]);
        if ($used > 0) {
            $db->update('notification_templates', ['is_active' => 0], 'id = ?', [$id]);
            $this->audit('update', 'notification_templates', $id, 'Deactivated template (in use)');
            $this->json(['success' => true, 'message' => lang('comm.template_deactivated')]);
            return;
        }

        $db->delete('notification_templates', 'id = ?', [$id]);
        $this->audit('delete', 'notification_templates', $id, 'Deleted notification template');
        $this->json(['success' => true, 'message' => lang('comm.template_deleted')]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  CHANNELS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function channelsDatatable(Request $request): void
    {
        $sql = "SELECT id, channel_code, name_en, name_ar, channel_type, provider, is_active, created_at
                FROM notification_channels WHERE 1=1";
        $cols = ['id', 'channel_code', 'name_en', 'channel_type', 'provider', 'is_active', 'created_at'];
        $search = ['channel_code', 'name_en', 'name_ar', 'channel_type', 'provider'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function channelStore(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'channel_code' => 'required|string|max:20',
            'name_en'      => 'required|string|max:100',
            'name_ar'      => 'required|string|max:100',
            'channel_type' => 'required|in:email,sms,push,whatsapp,in_app,webhook',
            'provider'     => 'nullable|string|max:50',
            'is_active'    => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;

        $configStr = $request->post('config', '');
        if ($configStr) {
            $decoded = json_decode($configStr, true);
            $data['config'] = $decoded !== null ? $configStr : null;
        }

        $id = $this->db()->insert('notification_channels', $data);
        $this->audit('create', 'notification_channels', $id, 'Created channel: ' . $data['channel_code']);
        $this->json(['success' => true, 'message' => lang('comm.channel_created'), 'redirect' => '/communications/channels']);
    }

    public function channelUpdate(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM notification_channels WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Channel not found'], 404);
            return;
        }

        $v = new Validator($request->all(), [
            'channel_code' => 'required|string|max:20',
            'name_en'      => 'required|string|max:100',
            'name_ar'      => 'required|string|max:100',
            'channel_type' => 'required|in:email,sms,push,whatsapp,in_app,webhook',
            'provider'     => 'nullable|string|max:50',
            'is_active'    => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;

        $configStr = $request->post('config', '');
        if ($configStr) {
            $decoded = json_decode($configStr, true);
            $data['config'] = $decoded !== null ? $configStr : null;
        }

        $db->update('notification_channels', $data, 'id = ?', [$id]);
        $this->audit('update', 'notification_channels', $id, 'Updated channel', $old, $data);
        $this->json(['success' => true, 'message' => lang('comm.channel_updated'), 'redirect' => '/communications/channels']);
    }

    public function channelDelete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $this->db()->update('notification_channels', ['is_active' => 0], 'id = ?', [$id]);
        $this->audit('update', 'notification_channels', $id, 'Deactivated channel');
        $this->json(['success' => true, 'message' => lang('comm.channel_deactivated')]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  TRIGGERS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function triggersDatatable(Request $request): void
    {
        $sql = "SELECT nt.id, nt.trigger_code, nt.trigger_name_en, nt.trigger_name_ar, nt.module, nt.event_type,
                       ntpl.name_en AS template_name, nt.is_active, nt.created_at
                FROM notification_triggers nt
                LEFT JOIN notification_templates ntpl ON nt.template_id = ntpl.id
                WHERE 1=1";
        $cols = ['nt.id', 'nt.trigger_code', 'nt.trigger_name_en', 'nt.module', 'nt.event_type', 'ntpl.name_en', 'nt.is_active', 'nt.created_at'];
        $search = ['nt.trigger_code', 'nt.trigger_name_en', 'nt.trigger_name_ar', 'nt.module', 'nt.event_type'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function triggerStore(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'trigger_code'    => 'required|string|max:50',
            'trigger_name_en' => 'required|string|max:200',
            'trigger_name_ar' => 'required|string|max:200',
            'module'          => 'required|string|max:50',
            'event_type'      => 'required|string|max:50',
            'template_id'     => 'nullable|integer',
            'is_active'       => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;
        $data['template_id'] = $data['template_id'] ?: null;

        $condStr = $request->post('conditions', '');
        $data['conditions'] = ($condStr && json_decode($condStr) !== null) ? $condStr : null;

        $id = $this->db()->insert('notification_triggers', $data);
        $this->audit('create', 'notification_triggers', $id, 'Created trigger: ' . $data['trigger_code']);
        $this->json(['success' => true, 'message' => lang('comm.trigger_created'), 'redirect' => '/communications/triggers']);
    }

    public function triggerUpdate(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM notification_triggers WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Trigger not found'], 404);
            return;
        }

        $v = new Validator($request->all(), [
            'trigger_code'    => 'required|string|max:50',
            'trigger_name_en' => 'required|string|max:200',
            'trigger_name_ar' => 'required|string|max:200',
            'module'          => 'required|string|max:50',
            'event_type'      => 'required|string|max:50',
            'template_id'     => 'nullable|integer',
            'is_active'       => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;
        $data['template_id'] = $data['template_id'] ?: null;

        $condStr = $request->post('conditions', '');
        $data['conditions'] = ($condStr && json_decode($condStr) !== null) ? $condStr : null;

        $db->update('notification_triggers', $data, 'id = ?', [$id]);
        $this->audit('update', 'notification_triggers', $id, 'Updated trigger', $old, $data);
        $this->json(['success' => true, 'message' => lang('comm.trigger_updated'), 'redirect' => '/communications/triggers']);
    }

    public function triggerDelete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $this->db()->update('notification_triggers', ['is_active' => 0], 'id = ?', [$id]);
        $this->audit('update', 'notification_triggers', $id, 'Deactivated trigger');
        $this->json(['success' => true, 'message' => lang('comm.trigger_deactivated')]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  QUEUE
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function queueDatatable(Request $request): void
    {
        $status = $request->get('filter_status', '');
        $sql    = "SELECT nq.id, nq.recipient_type, nq.recipient_address, nq.subject,
                          nq.priority, nq.status, nq.scheduled_at, nq.sent_at, nq.failure_reason,
                          nq.retry_count, nq.max_retries, nq.created_at,
                          nc.name_en AS channel_name, nt.name_en AS template_name
                   FROM notification_queue nq
                   LEFT JOIN notification_channels nc ON nq.channel_id = nc.id
                   LEFT JOIN notification_templates nt ON nq.template_id = nt.id
                   WHERE 1=1";
        $params = [];

        if ($status) {
            $sql .= " AND nq.status = ?";
            $params[] = $status;
        }

        $cols = ['nq.id', 'nc.name_en', 'nq.recipient_address', 'nq.subject', 'nq.status', 'nq.priority', 'nq.created_at'];
        $search = ['nq.recipient_address', 'nq.subject', 'nc.name_en', 'nt.name_en'];

        $this->datatable($request, $sql, $params, $cols, $search);
    }

    public function queueRetry(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $item = $db->fetchOne("SELECT * FROM notification_queue WHERE id = ? AND status = 'failed'", [$id]);

        if (!$item) {
            $this->json(['success' => false, 'message' => 'Queue item not found or not in failed state'], 404);
            return;
        }

        $db->update('notification_queue', [
            'status'      => 'queued',
            'retry_count' => $item['retry_count'] + 1,
            'failure_reason' => null,
            'failed_at'   => null,
        ], 'id = ?', [$id]);

        $this->audit('update', 'notification_queue', $id, 'Retried failed notification');
        $this->json(['success' => true, 'message' => lang('comm.queue_retried')]);
    }

    public function queueCancel(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $db->update('notification_queue', ['status' => 'cancelled'], "id = ? AND status IN ('queued','failed')", [$id]);
        $this->audit('update', 'notification_queue', $id, 'Cancelled queued notification');
        $this->json(['success' => true, 'message' => lang('comm.queue_cancelled')]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  SENT
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function sentDatatable(Request $request): void
    {
        $sql = "SELECT ns.id, nc.name_en AS channel_name, ns.recipient_type, ns.recipient_address,
                       ns.subject, ns.delivery_status, ns.sent_at, ns.delivered_at, ns.read_at,
                       nt.name_en AS template_name
                FROM notifications_sent ns
                LEFT JOIN notification_channels nc ON ns.channel_id = nc.id
                LEFT JOIN notification_templates nt ON ns.template_id = nt.id
                WHERE 1=1";
        $cols = ['ns.id', 'nc.name_en', 'ns.recipient_address', 'ns.subject', 'ns.delivery_status', 'ns.sent_at', 'ns.delivered_at'];
        $search = ['ns.recipient_address', 'ns.subject', 'nc.name_en', 'nt.name_en'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  BULK COMMUNICATIONS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function bulkDatatable(Request $request): void
    {
        $sql = "SELECT bc.id, bc.campaign_name, nc.name_en AS channel_name,
                       bc.total_recipients, bc.sent_count, bc.failed_count,
                       bc.status, bc.scheduled_at, bc.started_at, bc.completed_at, bc.created_at
                FROM bulk_communications bc
                LEFT JOIN notification_channels nc ON bc.channel_id = nc.id
                WHERE 1=1";
        $cols = ['bc.id', 'bc.campaign_name', 'nc.name_en', 'bc.total_recipients', 'bc.status', 'bc.created_at'];
        $search = ['bc.campaign_name', 'nc.name_en'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function bulkStore(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'campaign_name' => 'required|string|max:300',
            'channel_id'    => 'required|integer|exists:notification_channels,id',
            'subject'       => 'nullable|string|max:500',
            'body'          => 'required|string',
            'status'        => 'nullable|in:draft,scheduled',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['status'] = $data['status'] ?? 'draft';
        $data['created_by'] = Auth::id();

        $templateId = $request->post('template_id');
        $data['template_id'] = $templateId ?: null;

        $scheduledAt = $request->post('scheduled_at');
        $data['scheduled_at'] = $scheduledAt ?: null;

        // Target criteria
        $criteria = [];
        $memTypes = $request->post('target_membership_types', []);
        $memStatus = $request->post('target_status', '');
        if (!empty($memTypes)) $criteria['membership_types'] = $memTypes;
        if ($memStatus) $criteria['member_status'] = $memStatus;
        $data['target_criteria'] = !empty($criteria) ? json_encode($criteria) : null;

        $db = $this->db();
        $id = $db->insert('bulk_communications', $data);

        // Build recipients from criteria
        $recipientQuery = "SELECT m.id, m.primary_phone, m.primary_email, m.full_name_en
                           FROM members m WHERE m.is_deleted = 0 AND m.status = 'active'";
        $rParams = [];

        if (!empty($memTypes)) {
            $placeholders = implode(',', array_fill(0, count($memTypes), '?'));
            $recipientQuery .= " AND m.membership_type_id IN ({$placeholders})";
            $rParams = array_merge($rParams, $memTypes);
        }

        $channel = $db->fetchOne("SELECT channel_type FROM notification_channels WHERE id = ?", [$data['channel_id']]);
        $members = $db->fetchAll($recipientQuery, $rParams);

        $recipientCount = 0;
        foreach ($members as $member) {
            $address = '';
            if ($channel && $channel['channel_type'] === 'email') {
                $address = $member['primary_email'] ?? '';
            } elseif ($channel && in_array($channel['channel_type'], ['sms', 'whatsapp'])) {
                $address = $member['primary_phone'] ?? '';
            } else {
                $address = $member['primary_email'] ?? $member['primary_phone'] ?? '';
            }

            if (!$address) continue;

            $db->insert('bulk_communication_recipients', [
                'communication_id' => $id,
                'member_id'        => $member['id'],
                'recipient_address' => $address,
                'status'           => 'pending',
            ]);
            $recipientCount++;
        }

        $db->update('bulk_communications', ['total_recipients' => $recipientCount], 'id = ?', [$id]);

        $this->audit('create', 'bulk_communications', $id, "Created bulk campaign: {$data['campaign_name']} ({$recipientCount} recipients)");
        $this->json(['success' => true, 'message' => lang('comm.bulk_created') . " ({$recipientCount} recipients)", 'redirect' => '/communications/bulk/' . $id]);
    }

    public function bulkSend(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $campaign = $db->fetchOne("SELECT * FROM bulk_communications WHERE id = ?", [$id]);

        if (!$campaign || !in_array($campaign['status'], ['draft', 'scheduled', 'paused'])) {
            $this->json(['success' => false, 'message' => 'Campaign cannot be sent'], 400);
            return;
        }

        $db->update('bulk_communications', [
            'status'     => 'sending',
            'started_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        // Queue each pending recipient
        $recipients = $db->fetchAll(
            "SELECT bcr.id, bcr.member_id, bcr.recipient_address
             FROM bulk_communication_recipients bcr
             WHERE bcr.communication_id = ? AND bcr.status = 'pending'", [$id]
        );

        $sentCount = 0;
        foreach ($recipients as $recip) {
            $db->insert('notification_queue', [
                'channel_id'        => $campaign['channel_id'],
                'template_id'       => $campaign['template_id'],
                'recipient_type'    => 'member',
                'recipient_id'      => $recip['member_id'],
                'recipient_address' => $recip['recipient_address'],
                'subject'           => $campaign['subject'],
                'body'              => $campaign['body'],
                'priority'          => 5,
                'status'            => 'queued',
                'reference_type'    => 'bulk_communication',
                'reference_id'      => $id,
            ]);

            $db->update('bulk_communication_recipients', ['status' => 'sent', 'sent_at' => date('Y-m-d H:i:s')], 'id = ?', [$recip['id']]);
            $sentCount++;
        }

        $db->update('bulk_communications', [
            'status'       => 'completed',
            'sent_count'   => $sentCount,
            'completed_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('execute', 'bulk_communications', $id, "Sent bulk campaign ({$sentCount} queued)");
        $this->json(['success' => true, 'message' => lang('comm.bulk_sent') . " ({$sentCount} queued)"]);
    }

    public function bulkCancel(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $this->db()->update('bulk_communications', ['status' => 'cancelled'], "id = ? AND status IN ('draft','scheduled','paused')", [$id]);
        $this->audit('update', 'bulk_communications', $id, 'Cancelled bulk campaign');
        $this->json(['success' => true, 'message' => lang('comm.bulk_cancelled')]);
    }

    public function bulkRecipients(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $sql = "SELECT bcr.id, bcr.recipient_address, bcr.status, bcr.sent_at, bcr.error_message,
                       m.membership_number, m.full_name_en
                FROM bulk_communication_recipients bcr
                LEFT JOIN members m ON bcr.member_id = m.id
                WHERE bcr.communication_id = ?";
        $cols = ['bcr.id', 'bcr.recipient_address', 'm.full_name_en', 'bcr.status', 'bcr.sent_at'];
        $search = ['bcr.recipient_address', 'm.full_name_en', 'm.membership_number'];

        $this->datatable($request, $sql, [$id], $cols, $search);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  EMAIL & SMS LOGS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function emailLogDatatable(Request $request): void
    {
        $sql = "SELECT id, to_address, from_address, subject, status, sent_at, delivered_at,
                       bounced_at, failure_reason, provider, member_id, created_at
                FROM email_log WHERE 1=1";
        $cols = ['id', 'to_address', 'subject', 'status', 'sent_at', 'created_at'];
        $search = ['to_address', 'from_address', 'subject', 'provider'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function smsLogDatatable(Request $request): void
    {
        $sql = "SELECT id, phone_number, message_text, message_type, provider, status,
                       sent_at, delivered_at, failure_reason, cost, member_id, created_at
                FROM sms_log WHERE 1=1";
        $cols = ['id', 'phone_number', 'message_type', 'status', 'sent_at', 'cost', 'created_at'];
        $search = ['phone_number', 'message_text', 'provider', 'message_type'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  ANNOUNCEMENTS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function announcementsDatatable(Request $request): void
    {
        $sql = "SELECT id, title_en, title_ar, category, priority, target_audience,
                       publish_date, expiry_date, is_published, is_pinned, views_count, created_at
                FROM announcements WHERE 1=1";
        $cols = ['id', 'title_en', 'category', 'priority', 'target_audience', 'is_published', 'is_pinned', 'publish_date', 'created_at'];
        $search = ['title_en', 'title_ar', 'category'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function announcementStore(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'title_en'        => 'required|string|max:500',
            'title_ar'        => 'nullable|string|max:500',
            'body_en'         => 'required|string',
            'body_ar'         => 'nullable|string',
            'category'        => 'required|in:general,maintenance,event,policy,financial,emergency,other',
            'priority'        => 'required|in:low,normal,high,urgent',
            'target_audience' => 'required|in:all_members,specific_types,specific_members,staff,board,public',
            'publish_date'    => 'required|datetime',
            'expiry_date'     => 'nullable|datetime',
            'is_published'    => 'nullable|boolean',
            'is_pinned'       => 'nullable|boolean',
            'send_notification' => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_published']      = !empty($data['is_published']) ? 1 : 0;
        $data['is_pinned']         = !empty($data['is_pinned']) ? 1 : 0;
        $data['send_notification'] = !empty($data['send_notification']) ? 1 : 0;
        $data['created_by']        = Auth::id();

        $targetTypes = $request->post('target_membership_types', []);
        $data['target_membership_types'] = !empty($targetTypes) ? json_encode($targetTypes) : null;

        // Handle image upload
        $imageFile = $request->file('image');
        if ($imageFile && $imageFile['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($imageFile);
            $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'gif', 'webp'])->setMaxSize(5 * 1024 * 1024)->setDestination('uploads/announcements');
            if ($upload->validate()) {
                $data['image_path'] = $upload->save();
            }
        }

        // Handle attachment upload
        $attachFile = $request->file('attachment');
        if ($attachFile && $attachFile['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($attachFile);
            $upload->setAllowedTypes(['pdf', 'doc', 'docx', 'jpg', 'png'])->setMaxSize(10 * 1024 * 1024)->setDestination('uploads/announcements');
            if ($upload->validate()) {
                $data['attachment_path'] = $upload->save();
            }
        }

        $id = $this->db()->insert('announcements', $data);
        $this->audit('create', 'announcements', $id, 'Created announcement: ' . $data['title_en']);
        $this->json(['success' => true, 'message' => lang('comm.announcement_created'), 'redirect' => '/communications/announcements']);
    }

    public function announcementUpdate(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM announcements WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Announcement not found'], 404);
            return;
        }

        $v = new Validator($request->all(), [
            'title_en'        => 'required|string|max:500',
            'title_ar'        => 'nullable|string|max:500',
            'body_en'         => 'required|string',
            'body_ar'         => 'nullable|string',
            'category'        => 'required|in:general,maintenance,event,policy,financial,emergency,other',
            'priority'        => 'required|in:low,normal,high,urgent',
            'target_audience' => 'required|in:all_members,specific_types,specific_members,staff,board,public',
            'publish_date'    => 'required|datetime',
            'expiry_date'     => 'nullable|datetime',
            'is_published'    => 'nullable|boolean',
            'is_pinned'       => 'nullable|boolean',
            'send_notification' => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_published']      = !empty($data['is_published']) ? 1 : 0;
        $data['is_pinned']         = !empty($data['is_pinned']) ? 1 : 0;
        $data['send_notification'] = !empty($data['send_notification']) ? 1 : 0;

        $targetTypes = $request->post('target_membership_types', []);
        $data['target_membership_types'] = !empty($targetTypes) ? json_encode($targetTypes) : null;

        $imageFile = $request->file('image');
        if ($imageFile && $imageFile['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($imageFile);
            $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'gif', 'webp'])->setMaxSize(5 * 1024 * 1024)->setDestination('uploads/announcements');
            if ($upload->validate()) {
                $data['image_path'] = $upload->save();
            }
        }

        $attachFile = $request->file('attachment');
        if ($attachFile && $attachFile['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($attachFile);
            $upload->setAllowedTypes(['pdf', 'doc', 'docx', 'jpg', 'png'])->setMaxSize(10 * 1024 * 1024)->setDestination('uploads/announcements');
            if ($upload->validate()) {
                $data['attachment_path'] = $upload->save();
            }
        }

        $db->update('announcements', $data, 'id = ?', [$id]);
        $this->audit('update', 'announcements', $id, 'Updated announcement', $old, $data);
        $this->json(['success' => true, 'message' => lang('comm.announcement_updated'), 'redirect' => '/communications/announcements']);
    }

    public function announcementDelete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $this->db()->delete('announcements', 'id = ?', [$id]);
        $this->audit('delete', 'announcements', $id, 'Deleted announcement');
        $this->json(['success' => true, 'message' => lang('comm.announcement_deleted')]);
    }

    public function announcementTogglePublish(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $ann = $db->fetchOne("SELECT is_published FROM announcements WHERE id = ?", [$id]);
        if (!$ann) {
            $this->json(['success' => false, 'message' => 'Not found'], 404);
            return;
        }

        $newVal = $ann['is_published'] ? 0 : 1;
        $db->update('announcements', ['is_published' => $newVal], 'id = ?', [$id]);
        $this->audit('update', 'announcements', $id, $newVal ? 'Published announcement' : 'Unpublished announcement');
        $this->json(['success' => true, 'message' => $newVal ? lang('comm.published') : lang('comm.unpublished')]);
    }

    public function announcementTogglePin(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $ann = $db->fetchOne("SELECT is_pinned FROM announcements WHERE id = ?", [$id]);
        if (!$ann) {
            $this->json(['success' => false, 'message' => 'Not found'], 404);
            return;
        }

        $newVal = $ann['is_pinned'] ? 0 : 1;
        $db->update('announcements', ['is_pinned' => $newVal], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => $newVal ? lang('comm.pinned') : lang('comm.unpinned')]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  DOCUMENT TEMPLATES
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function docTemplatesDatatable(Request $request): void
    {
        $sql = "SELECT id, template_code, name_en, name_ar, category, template_type,
                       paper_size, orientation, is_active, version, created_at
                FROM document_templates WHERE 1=1";
        $cols = ['id', 'template_code', 'name_en', 'category', 'template_type', 'is_active', 'version', 'created_at'];
        $search = ['template_code', 'name_en', 'name_ar', 'category', 'template_type'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    public function docTemplateStore(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'template_code'   => 'required|string|max:30',
            'name_en'         => 'required|string|max:200',
            'name_ar'         => 'required|string|max:200',
            'category'        => 'required|string|max:50',
            'template_type'   => 'required|in:letter,certificate,receipt,invoice,contract,form,report,badge,other',
            'template_body_en'=> 'nullable|string',
            'template_body_ar'=> 'nullable|string',
            'paper_size'      => 'required|in:a4,a3,letter,legal,custom',
            'orientation'     => 'required|in:portrait,landscape',
            'header_template' => 'nullable|string',
            'footer_template' => 'nullable|string',
            'is_active'       => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;
        $data['created_by'] = Auth::id();
        $data['version'] = 1;

        // Extract variables from body
        preg_match_all('/\{\{(\w+)\}\}/', ($data['template_body_en'] ?? '') . ($data['template_body_ar'] ?? ''), $matches);
        $vars = array_unique($matches[1] ?? []);
        $data['variables'] = !empty($vars) ? json_encode(array_values($vars)) : null;

        $id = $this->db()->insert('document_templates', $data);
        $this->audit('create', 'document_templates', $id, 'Created document template: ' . $data['template_code']);
        $this->json(['success' => true, 'message' => lang('comm.doc_template_created'), 'redirect' => '/communications/document-templates']);
    }

    public function docTemplateUpdate(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM document_templates WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Template not found'], 404);
            return;
        }

        $v = new Validator($request->all(), [
            'template_code'   => 'required|string|max:30',
            'name_en'         => 'required|string|max:200',
            'name_ar'         => 'required|string|max:200',
            'category'        => 'required|string|max:50',
            'template_type'   => 'required|in:letter,certificate,receipt,invoice,contract,form,report,badge,other',
            'template_body_en'=> 'nullable|string',
            'template_body_ar'=> 'nullable|string',
            'paper_size'      => 'required|in:a4,a3,letter,legal,custom',
            'orientation'     => 'required|in:portrait,landscape',
            'header_template' => 'nullable|string',
            'footer_template' => 'nullable|string',
            'is_active'       => 'nullable|boolean',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;
        $data['version'] = ($old['version'] ?? 1) + 1;

        preg_match_all('/\{\{(\w+)\}\}/', ($data['template_body_en'] ?? '') . ($data['template_body_ar'] ?? ''), $matches);
        $vars = array_unique($matches[1] ?? []);
        $data['variables'] = !empty($vars) ? json_encode(array_values($vars)) : null;

        $db->update('document_templates', $data, 'id = ?', [$id]);
        $this->audit('update', 'document_templates', $id, 'Updated document template', $old, $data);
        $this->json(['success' => true, 'message' => lang('comm.doc_template_updated'), 'redirect' => '/communications/document-templates']);
    }

    public function docTemplateDelete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $this->db()->update('document_templates', ['is_active' => 0], 'id = ?', [$id]);
        $this->audit('update', 'document_templates', $id, 'Deactivated document template');
        $this->json(['success' => true, 'message' => lang('comm.doc_template_deactivated')]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  DOCUMENT GENERATION
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function previewDocument(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $templateId = (int)$request->post('template_id');
        $memberId   = (int)$request->post('member_id', 0);
        $db = $this->db();

        $template = $db->fetchOne("SELECT * FROM document_templates WHERE id = ?", [$templateId]);
        if (!$template) {
            $this->json(['success' => false, 'message' => 'Template not found'], 404);
            return;
        }

        $member = null;
        if ($memberId) {
            $member = $db->fetchOne(
                "SELECT m.*, mt.name_en AS membership_type_en, mt.name_ar AS membership_type_ar
                 FROM members m LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
                 WHERE m.id = ?", [$memberId]
            );
        }

        $html = $this->renderDocumentTemplate($template, $member);
        $this->json(['success' => true, 'html' => $html]);
    }

    public function generateDocumentPost(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $templateId = (int)$request->post('template_id');
        $memberId   = (int)$request->post('member_id', 0);
        $db = $this->db();

        $template = $db->fetchOne("SELECT * FROM document_templates WHERE id = ?", [$templateId]);
        if (!$template) {
            $this->json(['success' => false, 'message' => 'Template not found'], 404);
            return;
        }

        $member = null;
        if ($memberId) {
            $member = $db->fetchOne(
                "SELECT m.*, mt.name_en AS membership_type_en, mt.name_ar AS membership_type_ar
                 FROM members m LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
                 WHERE m.id = ?", [$memberId]
            );
        }

        $html = $this->renderDocumentTemplate($template, $member);

        // Save to file
        $storagePath = ROOT_PATH . '/storage/exports';
        if (!is_dir($storagePath)) @mkdir($storagePath, 0755, true);

        $filename = 'doc_' . $template['template_code'] . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.html';
        $filePath = $storagePath . '/' . $filename;
        file_put_contents($filePath, $html);

        $varsUsed = [];
        if ($member) $varsUsed['member_id'] = $memberId;

        $docId = $db->insert('generated_documents', [
            'template_id'    => $templateId,
            'member_id'      => $memberId ?: null,
            'variables_used' => json_encode($varsUsed),
            'output_path'    => 'storage/exports/' . $filename,
            'output_format'  => 'html',
            'generated_by'   => Auth::id(),
        ]);

        $this->audit('create', 'generated_documents', $docId, "Generated document from template: {$template['template_code']}");
        $this->json([
            'success'  => true,
            'message'  => lang('comm.document_generated'),
            'html'     => $html,
            'doc_id'   => $docId,
        ]);
    }

    public function generatedDatatable(Request $request): void
    {
        $sql = "SELECT gd.id, dt.template_code, dt.name_en AS template_name,
                       m.membership_number, m.full_name_en AS member_name,
                       gd.output_format, gd.print_count, gd.generated_at,
                       u.full_name_en AS generated_by_name
                FROM generated_documents gd
                LEFT JOIN document_templates dt ON gd.template_id = dt.id
                LEFT JOIN members m ON gd.member_id = m.id
                LEFT JOIN auth_users u ON gd.generated_by = u.id
                WHERE 1=1";
        $cols = ['gd.id', 'dt.template_code', 'dt.name_en', 'm.full_name_en', 'gd.output_format', 'gd.generated_at'];
        $search = ['dt.template_code', 'dt.name_en', 'm.full_name_en', 'm.membership_number'];

        $this->datatable($request, $sql, [], $cols, $search);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  PRIVATE: Template renderer
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    private function renderDocumentTemplate(array $template, ?array $member): string
    {
        $isAr = Lang::current() === 'ar';
        $body = $isAr ? ($template['template_body_ar'] ?: $template['template_body_en'] ?? '') : ($template['template_body_en'] ?? '');
        $header = $template['header_template'] ?? '';
        $footer = $template['footer_template'] ?? '';

        // Replace placeholders
        $replacements = [
            'club_name'         => Config::get($isAr ? 'club_name_ar' : 'club_name_en', 'The Club'),
            'club_phone'        => Config::get('club_phone', ''),
            'club_email'        => Config::get('club_email', ''),
            'club_address'      => Config::get($isAr ? 'club_address_ar' : 'club_address_en', ''),
            'current_date'      => date('Y-m-d'),
            'current_datetime'  => date('Y-m-d H:i'),
        ];

        if ($member) {
            $replacements = array_merge($replacements, [
                'member_number'     => $member['membership_number'] ?? '',
                'member_name_en'    => $member['full_name_en'] ?? '',
                'member_name_ar'    => $member['full_name_ar'] ?? '',
                'member_name'       => $isAr ? ($member['full_name_ar'] ?? $member['full_name_en'] ?? '') : ($member['full_name_en'] ?? ''),
                'national_id'       => $member['national_id'] ?? '',
                'primary_phone'     => $member['primary_phone'] ?? '',
                'primary_email'     => $member['primary_email'] ?? '',
                'membership_type'   => $isAr ? ($member['membership_type_ar'] ?? '') : ($member['membership_type_en'] ?? ''),
                'join_date'         => $member['join_date'] ?? '',
                'status'            => $member['status'] ?? '',
                'gender'            => $member['gender'] ?? '',
                'date_of_birth'     => $member['date_of_birth'] ?? '',
                'address'           => $member['address_line1'] ?? '',
            ]);
        }

        foreach ($replacements as $key => $val) {
            $body   = str_replace(['{{' . $key . '}}', '{' . $key . '}'], htmlspecialchars((string)$val, ENT_QUOTES, 'UTF-8'), $body);
            $header = str_replace(['{{' . $key . '}}', '{' . $key . '}'], htmlspecialchars((string)$val, ENT_QUOTES, 'UTF-8'), $header);
            $footer = str_replace(['{{' . $key . '}}', '{' . $key . '}'], htmlspecialchars((string)$val, ENT_QUOTES, 'UTF-8'), $footer);
        }

        $dir = $isAr ? 'rtl' : 'ltr';
        $orientation = $template['orientation'] ?? 'portrait';

        return <<<HTML
<!DOCTYPE html>
<html dir="{$dir}">
<head>
<meta charset="UTF-8">
<title>{$template['name_en']}</title>
<style>
@page { size: {$template['paper_size']} {$orientation}; margin: 20mm; }
body { font-family: 'Cairo', 'Segoe UI', Arial, sans-serif; font-size: 12pt; direction: {$dir}; color: #212121; }
.doc-header { border-bottom: 2px solid #1B2A4A; padding-bottom: 10px; margin-bottom: 20px; }
.doc-footer { border-top: 1px solid #ccc; padding-top: 10px; margin-top: 30px; font-size: 9pt; color: #757575; }
.doc-body { min-height: 500px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: start; }
th { background: #f8f9fa; }
@media print { .no-print { display: none !important; } }
</style>
</head>
<body>
<div class="no-print" style="margin-bottom:15px">
<button onclick="window.print()" style="padding:8px 16px;background:#1B2A4A;color:#fff;border:none;border-radius:4px;cursor:pointer">ðŸ–¨ Print</button>
<button onclick="window.close()" style="padding:8px 16px;background:#999;color:#fff;border:none;border-radius:4px;cursor:pointer;margin-left:5px">âœ• Close</button>
</div>
<div class="doc-header">{$header}</div>
<div class="doc-body">{$body}</div>
<div class="doc-footer">{$footer}</div>
</body>
</html>
HTML;
    }
}
```

## File: modules\communications\Controllers\CommunicationsController.php

```php
<?php

class CommunicationsController extends Controller
{
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  NOTIFICATION TEMPLATES
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function templates(Request $request): void
    {
        Gate::authorize('notifications.view');
        $this->view('templates/index', [
            'title'       => lang('comm.templates'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.templates')],
            ],
        ]);
    }

    public function templateCreate(Request $request): void
    {
        Gate::authorize('notifications.send');
        $db = $this->db();
        $channels = $db->fetchAll("SELECT id, channel_code, name_en, name_ar FROM notification_channels WHERE is_active = 1 ORDER BY id");

        $this->view('templates/form', [
            'title'       => lang('comm.create_template'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.templates'), 'url' => '/communications/templates'],
                ['label' => lang('comm.create_template')],
            ],
            'template'    => null,
            'channels'    => $channels,
        ]);
    }

    public function templateEdit(Request $request): void
    {
        Gate::authorize('notifications.send');
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $template = $db->fetchOne("SELECT * FROM notification_templates WHERE id = ?", [$id]);
        if (!$template) { $this->abort(404, 'Template not found'); }

        $channels = $db->fetchAll("SELECT id, channel_code, name_en, name_ar FROM notification_channels WHERE is_active = 1 ORDER BY id");

        $this->view('templates/form', [
            'title'       => lang('comm.edit_template'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.templates'), 'url' => '/communications/templates'],
                ['label' => lang('comm.edit_template')],
            ],
            'template'    => $template,
            'channels'    => $channels,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  CHANNELS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function channels(Request $request): void
    {
        Gate::authorize('notifications.view');
        $this->view('channels/index', [
            'title'       => lang('comm.channels'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.channels')],
            ],
        ]);
    }

    public function channelCreate(Request $request): void
    {
        Gate::authorize('notifications.send');
        $this->view('channels/form', [
            'title'       => lang('comm.create_channel'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.channels'), 'url' => '/communications/channels'],
                ['label' => lang('comm.create_channel')],
            ],
            'channel'     => null,
        ]);
    }

    public function channelEdit(Request $request): void
    {
        Gate::authorize('notifications.send');
        $id = (int)$request->routeParam('id');
        $channel = $this->db()->fetchOne("SELECT * FROM notification_channels WHERE id = ?", [$id]);
        if (!$channel) { $this->abort(404, 'Channel not found'); }

        $this->view('channels/form', [
            'title'       => lang('comm.edit_channel'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.channels'), 'url' => '/communications/channels'],
                ['label' => lang('comm.edit_channel')],
            ],
            'channel'     => $channel,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  TRIGGERS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function triggers(Request $request): void
    {
        Gate::authorize('notifications.view');
        $this->view('triggers/index', [
            'title'       => lang('comm.triggers'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.triggers')],
            ],
        ]);
    }

    public function triggerCreate(Request $request): void
    {
        Gate::authorize('notifications.send');
        $db = $this->db();
        $templates = $db->fetchAll("SELECT id, template_code, name_en, name_ar FROM notification_templates WHERE is_active = 1 ORDER BY name_en");

        $this->view('triggers/form', [
            'title'       => lang('comm.create_trigger'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.triggers'), 'url' => '/communications/triggers'],
                ['label' => lang('comm.create_trigger')],
            ],
            'trigger'     => null,
            'templates'   => $templates,
        ]);
    }

    public function triggerEdit(Request $request): void
    {
        Gate::authorize('notifications.send');
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $trigger = $db->fetchOne("SELECT * FROM notification_triggers WHERE id = ?", [$id]);
        if (!$trigger) { $this->abort(404, 'Trigger not found'); }

        $templates = $db->fetchAll("SELECT id, template_code, name_en, name_ar FROM notification_templates WHERE is_active = 1 ORDER BY name_en");

        $this->view('triggers/form', [
            'title'       => lang('comm.edit_trigger'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.triggers'), 'url' => '/communications/triggers'],
                ['label' => lang('comm.edit_trigger')],
            ],
            'trigger'     => $trigger,
            'templates'   => $templates,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  QUEUE
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function queue(Request $request): void
    {
        Gate::authorize('notifications.send');
        $db = $this->db();
        $stats = [
            'queued'     => (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE status = 'queued'"),
            'processing' => (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE status = 'processing'"),
            'sent'       => (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE status = 'sent' AND DATE(sent_at) = CURDATE()"),
            'failed'     => (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE status = 'failed'"),
        ];

        $this->view('queue/index', [
            'title'       => lang('comm.notification_queue'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.notification_queue')],
            ],
            'stats'       => $stats,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  SENT
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function sent(Request $request): void
    {
        Gate::authorize('notifications.view');
        $this->view('sent/index', [
            'title'       => lang('comm.sent_notifications'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.sent_notifications')],
            ],
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  BULK COMMUNICATIONS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function bulk(Request $request): void
    {
        Gate::authorize('notifications.bulk');
        $this->view('bulk/index', [
            'title'       => lang('comm.bulk_communications'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.bulk_communications')],
            ],
        ]);
    }

    public function bulkCreate(Request $request): void
    {
        Gate::authorize('notifications.bulk');
        $db = $this->db();
        $channels  = $db->fetchAll("SELECT id, channel_code, name_en, name_ar FROM notification_channels WHERE is_active = 1 ORDER BY id");
        $templates = $db->fetchAll("SELECT id, template_code, name_en, name_ar FROM notification_templates WHERE is_active = 1 ORDER BY name_en");
        $memTypes  = $db->fetchAll("SELECT id, type_code, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('bulk/form', [
            'title'       => lang('comm.create_bulk'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.bulk_communications'), 'url' => '/communications/bulk'],
                ['label' => lang('comm.create_bulk')],
            ],
            'channels'    => $channels,
            'templates'   => $templates,
            'memTypes'    => $memTypes,
        ]);
    }

    public function bulkView(Request $request): void
    {
        Gate::authorize('notifications.bulk');
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $campaign = $db->fetchOne(
            "SELECT bc.*, nc.name_en AS channel_name
             FROM bulk_communications bc
             LEFT JOIN notification_channels nc ON bc.channel_id = nc.id
             WHERE bc.id = ?", [$id]
        );
        if (!$campaign) { $this->abort(404, 'Campaign not found'); }

        $this->view('bulk/view', [
            'title'       => lang('comm.view_campaign'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.bulk_communications'), 'url' => '/communications/bulk'],
                ['label' => $campaign['campaign_name']],
            ],
            'campaign'    => $campaign,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  LOGS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function emailLog(Request $request): void
    {
        Gate::authorize('notifications.view');
        $this->view('logs/email', [
            'title'       => lang('comm.email_log'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.email_log')],
            ],
        ]);
    }

    public function smsLog(Request $request): void
    {
        Gate::authorize('notifications.view');
        $this->view('logs/sms', [
            'title'       => lang('comm.sms_log'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.sms_log')],
            ],
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  ANNOUNCEMENTS
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function announcements(Request $request): void
    {
        Gate::authorize('notifications.send');
        $this->view('announcements/index', [
            'title'       => lang('comm.announcements'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.announcements')],
            ],
        ]);
    }

    public function announcementCreate(Request $request): void
    {
        Gate::authorize('notifications.send');
        $memTypes = $this->db()->fetchAll("SELECT id, type_code, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('announcements/form', [
            'title'        => lang('comm.create_announcement'),
            'breadcrumbs'  => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.announcements'), 'url' => '/communications/announcements'],
                ['label' => lang('comm.create_announcement')],
            ],
            'announcement' => null,
            'memTypes'     => $memTypes,
        ]);
    }

    public function announcementEdit(Request $request): void
    {
        Gate::authorize('notifications.send');
        $id = (int)$request->routeParam('id');
        $announcement = $this->db()->fetchOne("SELECT * FROM announcements WHERE id = ?", [$id]);
        if (!$announcement) { $this->abort(404, 'Announcement not found'); }

        $memTypes = $this->db()->fetchAll("SELECT id, type_code, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('announcements/form', [
            'title'        => lang('comm.edit_announcement'),
            'breadcrumbs'  => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.announcements'), 'url' => '/communications/announcements'],
                ['label' => lang('comm.edit_announcement')],
            ],
            'announcement' => $announcement,
            'memTypes'     => $memTypes,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  DOCUMENT TEMPLATES
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function docTemplates(Request $request): void
    {
        Gate::authorize('documents.view');
        $this->view('documents/templates_index', [
            'title'       => lang('comm.document_templates'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.document_templates')],
            ],
        ]);
    }

    public function docTemplateCreate(Request $request): void
    {
        Gate::authorize('documents.manage');
        $this->view('documents/template_form', [
            'title'       => lang('comm.create_doc_template'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.document_templates'), 'url' => '/communications/document-templates'],
                ['label' => lang('comm.create_doc_template')],
            ],
            'template'    => null,
        ]);
    }

    public function docTemplateEdit(Request $request): void
    {
        Gate::authorize('documents.manage');
        $id = (int)$request->routeParam('id');
        $template = $this->db()->fetchOne("SELECT * FROM document_templates WHERE id = ?", [$id]);
        if (!$template) { $this->abort(404, 'Document template not found'); }

        $this->view('documents/template_form', [
            'title'       => lang('comm.edit_doc_template'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.document_templates'), 'url' => '/communications/document-templates'],
                ['label' => lang('comm.edit_doc_template')],
            ],
            'template'    => $template,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  DOCUMENT GENERATION
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

    public function generateDocument(Request $request): void
    {
        Gate::authorize('documents.manage');
        $db = $this->db();
        $templates = $db->fetchAll("SELECT id, template_code, name_en, name_ar, category, template_type FROM document_templates WHERE is_active = 1 ORDER BY category, name_en");
        $recentDocs = $db->fetchAll(
            "SELECT gd.*, dt.name_en AS template_name, m.membership_number, m.full_name_en AS member_name
             FROM generated_documents gd
             LEFT JOIN document_templates dt ON gd.template_id = dt.id
             LEFT JOIN members m ON gd.member_id = m.id
             ORDER BY gd.generated_at DESC LIMIT 20"
        );

        $this->view('documents/generate', [
            'title'       => lang('comm.generate_document'),
            'breadcrumbs' => [
                ['label' => lang('comm.communications'), 'url' => '/communications/templates'],
                ['label' => lang('comm.generate_document')],
            ],
            'templates'   => $templates,
            'recentDocs'  => $recentDocs,
        ]);
    }
}
```

## File: modules\communications\css\communications.css

```css
/* Communications Module Styles */
.comm-queue-stat { text-align: center; }
.comm-queue-stat h3 { font-size: 2rem; font-weight: 700; }
.comm-badge-channel { font-size: 0.75rem; padding: 3px 8px; }

#doc-preview { max-height: 600px; overflow-y: auto; background: #fff; }
#doc-preview iframe { width: 100%; min-height: 500px; border: none; }

.comm-announcement-body { max-height: 200px; overflow-y: auto; }

.comm-variable-tag {
    display: inline-block;
    background: #e3f2fd;
    color: #1565c0;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 0.8rem;
    margin: 2px;
    cursor: pointer;
}
.comm-variable-tag:hover { background: #bbdefb; }
```

## File: modules\communications\js\communications.js

```js
/**
 * Communications Module JavaScript
 */
(function() {
    'use strict';

    // Auto-init Select2 on module pages
    document.addEventListener('DOMContentLoaded', function() {
        if (typeof $ !== 'undefined' && $.fn.select2) {
            $('.select2').each(function() {
                if (!$(this).data('select2')) {
                    $(this).select2({ theme: 'bootstrap-5', width: '100%', allowClear: true });
                }
            });
        }
    });
})();
```

## File: modules\communications\lang\ar.php

```php
<?php
return [
    'comm.communications'       => 'Ø§Ù„Ø§ØªØµØ§Ù„Ø§Øª',
    'comm.templates'            => 'Ù‚ÙˆØ§Ù„Ø¨ Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª',
    'comm.create_template'      => 'Ø¥Ù†Ø´Ø§Ø¡ Ù‚Ø§Ù„Ø¨',
    'comm.edit_template'        => 'ØªØ¹Ø¯ÙŠÙ„ Ù‚Ø§Ù„Ø¨',
    'comm.template_code'        => 'ÙƒÙˆØ¯ Ø§Ù„Ù‚Ø§Ù„Ø¨',
    'comm.name_en'              => 'Ø§Ù„Ø§Ø³Ù… (EN)',
    'comm.name_ar'              => 'Ø§Ù„Ø§Ø³Ù… (AR)',
    'comm.channel'              => 'Ø§Ù„Ù‚Ù†Ø§Ø©',
    'comm.event_trigger'        => 'Ø­Ø¯Ø« Ø§Ù„ØªØ´ØºÙŠÙ„',
    'comm.subject_en'           => 'Ø§Ù„Ù…ÙˆØ¶ÙˆØ¹ (EN)',
    'comm.subject_ar'           => 'Ø§Ù„Ù…ÙˆØ¶ÙˆØ¹ (AR)',
    'comm.body_en'              => 'Ø§Ù„Ù…Ø­ØªÙˆÙ‰ (EN)',
    'comm.body_ar'              => 'Ø§Ù„Ù…Ø­ØªÙˆÙ‰ (AR)',
    'comm.active'               => 'Ù†Ø´Ø·',
    'comm.template_created'     => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ù‚Ø§Ù„Ø¨ Ø¨Ù†Ø¬Ø§Ø­',
    'comm.template_updated'     => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ù‚Ø§Ù„Ø¨ Ø¨Ù†Ø¬Ø§Ø­',
    'comm.template_deleted'     => 'ØªÙ… Ø­Ø°Ù Ø§Ù„Ù‚Ø§Ù„Ø¨ Ø¨Ù†Ø¬Ø§Ø­',
    'comm.template_deactivated' => 'ØªÙ… ØªØ¹Ø·ÙŠÙ„ Ø§Ù„Ù‚Ø§Ù„Ø¨ (Ù‚ÙŠØ¯ Ø§Ù„Ø§Ø³ØªØ®Ø¯Ø§Ù…)',
    'comm.channels'             => 'Ù‚Ù†ÙˆØ§Øª Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª',
    'comm.create_channel'       => 'Ø¥Ù†Ø´Ø§Ø¡ Ù‚Ù†Ø§Ø©',
    'comm.edit_channel'         => 'ØªØ¹Ø¯ÙŠÙ„ Ù‚Ù†Ø§Ø©',
    'comm.channel_created'      => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ù‚Ù†Ø§Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'comm.channel_updated'      => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ù‚Ù†Ø§Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'comm.channel_deactivated'  => 'ØªÙ… ØªØ¹Ø·ÙŠÙ„ Ø§Ù„Ù‚Ù†Ø§Ø©',
    'comm.triggers'             => 'Ù…Ø´ØºÙ„Ø§Øª Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª',
    'comm.create_trigger'       => 'Ø¥Ù†Ø´Ø§Ø¡ Ù…Ø´ØºÙ„',
    'comm.edit_trigger'         => 'ØªØ¹Ø¯ÙŠÙ„ Ù…Ø´ØºÙ„',
    'comm.trigger_created'      => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ù…Ø´ØºÙ„ Ø¨Ù†Ø¬Ø§Ø­',
    'comm.trigger_updated'      => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ù…Ø´ØºÙ„ Ø¨Ù†Ø¬Ø§Ø­',
    'comm.trigger_deactivated'  => 'ØªÙ… ØªØ¹Ø·ÙŠÙ„ Ø§Ù„Ù…Ø´ØºÙ„',
    'comm.notification_queue'   => 'Ù‚Ø§Ø¦Ù…Ø© Ø§Ù†ØªØ¸Ø§Ø± Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª',
    'comm.queue_retried'        => 'ØªÙ… Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„Ø¥Ø´Ø¹Ø§Ø± Ù„Ù„Ù‚Ø§Ø¦Ù…Ø©',
    'comm.queue_cancelled'      => 'ØªÙ… Ø¥Ù„ØºØ§Ø¡ Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±',
    'comm.sent_notifications'   => 'Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª Ø§Ù„Ù…Ø±Ø³Ù„Ø©',
    'comm.bulk_communications'  => 'Ø§ØªØµØ§Ù„Ø§Øª Ø¬Ù…Ø§Ø¹ÙŠØ©',
    'comm.create_bulk'          => 'Ø¥Ù†Ø´Ø§Ø¡ Ø­Ù…Ù„Ø©',
    'comm.view_campaign'        => 'Ø¹Ø±Ø¶ Ø§Ù„Ø­Ù…Ù„Ø©',
    'comm.bulk_created'         => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø­Ù…Ù„Ø©',
    'comm.bulk_sent'            => 'ØªÙ… Ø¥Ø±Ø³Ø§Ù„ Ø§Ù„Ø­Ù…Ù„Ø©',
    'comm.bulk_cancelled'       => 'ØªÙ… Ø¥Ù„ØºØ§Ø¡ Ø§Ù„Ø­Ù…Ù„Ø©',
    'comm.email_log'            => 'Ø³Ø¬Ù„ Ø§Ù„Ø¨Ø±ÙŠØ¯ Ø§Ù„Ø¥Ù„ÙƒØªØ±ÙˆÙ†ÙŠ',
    'comm.sms_log'              => 'Ø³Ø¬Ù„ Ø§Ù„Ø±Ø³Ø§Ø¦Ù„ Ø§Ù„Ù†ØµÙŠØ©',
    'comm.announcements'        => 'Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†Ø§Øª',
    'comm.create_announcement'  => 'Ø¥Ù†Ø´Ø§Ø¡ Ø¥Ø¹Ù„Ø§Ù†',
    'comm.edit_announcement'    => 'ØªØ¹Ø¯ÙŠÙ„ Ø¥Ø¹Ù„Ø§Ù†',
    'comm.announcement_created' => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.announcement_updated' => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.announcement_deleted' => 'ØªÙ… Ø­Ø°Ù Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.published'            => 'ØªÙ… Ù†Ø´Ø± Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.unpublished'          => 'ØªÙ… Ø¥Ù„ØºØ§Ø¡ Ù†Ø´Ø± Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.pinned'               => 'ØªÙ… ØªØ«Ø¨ÙŠØª Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.unpinned'             => 'ØªÙ… Ø¥Ù„ØºØ§Ø¡ ØªØ«Ø¨ÙŠØª Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†',
    'comm.document_templates'   => 'Ù‚ÙˆØ§Ù„Ø¨ Ø§Ù„Ù…Ø³ØªÙ†Ø¯Ø§Øª',
    'comm.create_doc_template'  => 'Ø¥Ù†Ø´Ø§Ø¡ Ù‚Ø§Ù„Ø¨ Ù…Ø³ØªÙ†Ø¯',
    'comm.edit_doc_template'    => 'ØªØ¹Ø¯ÙŠÙ„ Ù‚Ø§Ù„Ø¨ Ù…Ø³ØªÙ†Ø¯',
    'comm.doc_template_created' => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ù‚Ø§Ù„Ø¨ Ø§Ù„Ù…Ø³ØªÙ†Ø¯',
    'comm.doc_template_updated' => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ù‚Ø§Ù„Ø¨ Ø§Ù„Ù…Ø³ØªÙ†Ø¯',
    'comm.doc_template_deactivated' => 'ØªÙ… ØªØ¹Ø·ÙŠÙ„ Ù‚Ø§Ù„Ø¨ Ø§Ù„Ù…Ø³ØªÙ†Ø¯',
    'comm.generate_document'    => 'Ø¥Ù†Ø´Ø§Ø¡ Ù…Ø³ØªÙ†Ø¯',
    'comm.document_generated'   => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ù…Ø³ØªÙ†Ø¯ Ø¨Ù†Ø¬Ø§Ø­',
    'comm.sent_today'           => 'Ø£ÙØ±Ø³Ù„Øª Ø§Ù„ÙŠÙˆÙ…',
    'comm.failed_notifications' => 'Ø¥Ø´Ø¹Ø§Ø±Ø§Øª ÙØ§Ø´Ù„Ø©',
];
```

## File: modules\communications\lang\en.php

```php
<?php
return [
    'comm.communications'       => 'Communications',
    'comm.templates'            => 'Notification Templates',
    'comm.create_template'      => 'Create Template',
    'comm.edit_template'        => 'Edit Template',
    'comm.template_code'        => 'Template Code',
    'comm.name_en'              => 'Name (EN)',
    'comm.name_ar'              => 'Name (AR)',
    'comm.channel'              => 'Channel',
    'comm.event_trigger'        => 'Event Trigger',
    'comm.subject_en'           => 'Subject (EN)',
    'comm.subject_ar'           => 'Subject (AR)',
    'comm.body_en'              => 'Body (EN)',
    'comm.body_ar'              => 'Body (AR)',
    'comm.active'               => 'Active',
    'comm.template_created'     => 'Template created successfully',
    'comm.template_updated'     => 'Template updated successfully',
    'comm.template_deleted'     => 'Template deleted successfully',
    'comm.template_deactivated' => 'Template deactivated (in use)',
    'comm.channels'             => 'Notification Channels',
    'comm.create_channel'       => 'Create Channel',
    'comm.edit_channel'         => 'Edit Channel',
    'comm.channel_created'      => 'Channel created successfully',
    'comm.channel_updated'      => 'Channel updated successfully',
    'comm.channel_deactivated'  => 'Channel deactivated',
    'comm.triggers'             => 'Notification Triggers',
    'comm.create_trigger'       => 'Create Trigger',
    'comm.edit_trigger'         => 'Edit Trigger',
    'comm.trigger_created'      => 'Trigger created successfully',
    'comm.trigger_updated'      => 'Trigger updated successfully',
    'comm.trigger_deactivated'  => 'Trigger deactivated',
    'comm.notification_queue'   => 'Notification Queue',
    'comm.queue_retried'        => 'Notification re-queued for retry',
    'comm.queue_cancelled'      => 'Notification cancelled',
    'comm.sent_notifications'   => 'Sent Notifications',
    'comm.bulk_communications'  => 'Bulk Communications',
    'comm.create_bulk'          => 'Create Campaign',
    'comm.view_campaign'        => 'View Campaign',
    'comm.bulk_created'         => 'Campaign created',
    'comm.bulk_sent'            => 'Campaign sent',
    'comm.bulk_cancelled'       => 'Campaign cancelled',
    'comm.email_log'            => 'Email Log',
    'comm.sms_log'              => 'SMS Log',
    'comm.announcements'        => 'Announcements',
    'comm.create_announcement'  => 'Create Announcement',
    'comm.edit_announcement'    => 'Edit Announcement',
    'comm.announcement_created' => 'Announcement created',
    'comm.announcement_updated' => 'Announcement updated',
    'comm.announcement_deleted' => 'Announcement deleted',
    'comm.published'            => 'Announcement published',
    'comm.unpublished'          => 'Announcement unpublished',
    'comm.pinned'               => 'Announcement pinned',
    'comm.unpinned'             => 'Announcement unpinned',
    'comm.document_templates'   => 'Document Templates',
    'comm.create_doc_template'  => 'Create Document Template',
    'comm.edit_doc_template'    => 'Edit Document Template',
    'comm.doc_template_created' => 'Document template created',
    'comm.doc_template_updated' => 'Document template updated',
    'comm.doc_template_deactivated' => 'Document template deactivated',
    'comm.generate_document'    => 'Generate Document',
    'comm.document_generated'   => 'Document generated successfully',
    'comm.sent_today'           => 'Sent Today',
    'comm.failed_notifications' => 'Failed Notifications',
];
```

## File: modules\communications\module.json

```json
{
    "name": "communications",
    "label_en": "Communications & Documents",
    "label_ar": "Ø§Ù„Ø§ØªØµØ§Ù„Ø§Øª ÙˆØ§Ù„Ù…Ø³ØªÙ†Ø¯Ø§Øª",
    "version": "1.0.0",
    "description": "Notification templates, bulk communications, announcements, document generation",
    "sort_order": 110,
    "icon": "fas fa-bell",
    "routes_file": "routes.php",
    "css_files": ["css/communications.css"],
    "js_files": ["js/communications.js"],
    "menu": [
        {
            "label_en": "Communications",
            "label_ar": "Ø§Ù„Ø§ØªØµØ§Ù„Ø§Øª",
            "icon": "fas fa-bell",
            "permission": "notifications.view",
            "sort_order": 110,
            "children": [
                {
                    "label_en": "Notification Templates",
                    "label_ar": "Ù‚ÙˆØ§Ù„Ø¨ Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª",
                    "route": "/communications/templates",
                    "permission": "notifications.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Channels",
                    "label_ar": "Ø§Ù„Ù‚Ù†ÙˆØ§Øª",
                    "route": "/communications/channels",
                    "permission": "notifications.view",
                    "sort_order": 2
                },
                {
                    "label_en": "Triggers",
                    "label_ar": "Ø§Ù„Ù…Ø´ØºÙ„Ø§Øª",
                    "route": "/communications/triggers",
                    "permission": "notifications.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Notification Queue",
                    "label_ar": "Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª",
                    "route": "/communications/queue",
                    "permission": "notifications.send",
                    "sort_order": 4
                },
                {
                    "label_en": "Sent Notifications",
                    "label_ar": "Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª Ø§Ù„Ù…Ø±Ø³Ù„Ø©",
                    "route": "/communications/sent",
                    "permission": "notifications.view",
                    "sort_order": 5
                },
                {
                    "label_en": "Bulk Communications",
                    "label_ar": "Ø§ØªØµØ§Ù„Ø§Øª Ø¬Ù…Ø§Ø¹ÙŠØ©",
                    "route": "/communications/bulk",
                    "permission": "notifications.bulk",
                    "sort_order": 6
                },
                {
                    "label_en": "Email Log",
                    "label_ar": "Ø³Ø¬Ù„ Ø§Ù„Ø¨Ø±ÙŠØ¯",
                    "route": "/communications/email-log",
                    "permission": "notifications.view",
                    "sort_order": 7
                },
                {
                    "label_en": "SMS Log",
                    "label_ar": "Ø³Ø¬Ù„ Ø§Ù„Ø±Ø³Ø§Ø¦Ù„",
                    "route": "/communications/sms-log",
                    "permission": "notifications.view",
                    "sort_order": 8
                },
                {
                    "label_en": "Announcements",
                    "label_ar": "Ø§Ù„Ø¥Ø¹Ù„Ø§Ù†Ø§Øª",
                    "route": "/communications/announcements",
                    "permission": "notifications.send",
                    "sort_order": 9
                },
                {
                    "label_en": "Document Templates",
                    "label_ar": "Ù‚ÙˆØ§Ù„Ø¨ Ø§Ù„Ù…Ø³ØªÙ†Ø¯Ø§Øª",
                    "route": "/communications/document-templates",
                    "permission": "documents.view",
                    "sort_order": 10
                },
                {
                    "label_en": "Generate Document",
                    "label_ar": "Ø¥Ù†Ø´Ø§Ø¡ Ù…Ø³ØªÙ†Ø¯",
                    "route": "/communications/documents/generate",
                    "permission": "documents.manage",
                    "sort_order": 11
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "notifications_sent_today",
            "label_en": "Notifications Sent Today",
            "label_ar": "Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª Ø§Ù„Ù…Ø±Ø³Ù„Ø© Ø§Ù„ÙŠÙˆÙ…",
            "permission": "notifications.view",
            "partial": "views/widgets/sent_today.php",
            "size": "col-md-4",
            "sort_order": 110
        },
        {
            "widget_id": "failed_notifications",
            "label_en": "Failed Notifications",
            "label_ar": "Ø§Ù„Ø¥Ø´Ø¹Ø§Ø±Ø§Øª Ø§Ù„ÙØ§Ø´Ù„Ø©",
            "permission": "notifications.view",
            "partial": "views/widgets/failed_notifications.php",
            "size": "col-md-4",
            "sort_order": 111
        }
    ]
}
```

## File: modules\communications\routes.php

```php
<?php
return [
    // â”€â”€ Notification Templates â”€â”€
    ['GET',  '/communications/templates',              'CommunicationsController@templates',       'notifications.view'],
    ['GET',  '/communications/templates/create',       'CommunicationsController@templateCreate',  'notifications.send'],
    ['GET',  '/communications/templates/{id}/edit',    'CommunicationsController@templateEdit',    'notifications.send'],
    ['POST', '/communications/templates',              'CommunicationsApiController@templateStore', 'notifications.send'],
    ['POST', '/communications/templates/{id}',         'CommunicationsApiController@templateUpdate','notifications.send'],
    ['POST', '/communications/templates/{id}/delete',  'CommunicationsApiController@templateDelete','notifications.send'],
    ['GET',  '/communications/api/templates/datatable', 'CommunicationsApiController@templatesDatatable', 'notifications.view'],

    // â”€â”€ Channels â”€â”€
    ['GET',  '/communications/channels',               'CommunicationsController@channels',        'notifications.view'],
    ['GET',  '/communications/channels/create',        'CommunicationsController@channelCreate',   'notifications.send'],
    ['GET',  '/communications/channels/{id}/edit',     'CommunicationsController@channelEdit',     'notifications.send'],
    ['POST', '/communications/channels',               'CommunicationsApiController@channelStore',  'notifications.send'],
    ['POST', '/communications/channels/{id}',          'CommunicationsApiController@channelUpdate', 'notifications.send'],
    ['POST', '/communications/channels/{id}/delete',   'CommunicationsApiController@channelDelete', 'notifications.send'],
    ['GET',  '/communications/api/channels/datatable', 'CommunicationsApiController@channelsDatatable', 'notifications.view'],

    // â”€â”€ Triggers â”€â”€
    ['GET',  '/communications/triggers',               'CommunicationsController@triggers',        'notifications.view'],
    ['GET',  '/communications/triggers/create',        'CommunicationsController@triggerCreate',   'notifications.send'],
    ['GET',  '/communications/triggers/{id}/edit',     'CommunicationsController@triggerEdit',     'notifications.send'],
    ['POST', '/communications/triggers',               'CommunicationsApiController@triggerStore',  'notifications.send'],
    ['POST', '/communications/triggers/{id}',          'CommunicationsApiController@triggerUpdate', 'notifications.send'],
    ['POST', '/communications/triggers/{id}/delete',   'CommunicationsApiController@triggerDelete', 'notifications.send'],
    ['GET',  '/communications/api/triggers/datatable', 'CommunicationsApiController@triggersDatatable', 'notifications.view'],

    // â”€â”€ Queue â”€â”€
    ['GET',  '/communications/queue',                  'CommunicationsController@queue',           'notifications.send'],
    ['GET',  '/communications/api/queue/datatable',    'CommunicationsApiController@queueDatatable', 'notifications.send'],
    ['POST', '/communications/queue/{id}/retry',       'CommunicationsApiController@queueRetry',   'notifications.send'],
    ['POST', '/communications/queue/{id}/cancel',      'CommunicationsApiController@queueCancel',  'notifications.send'],

    // â”€â”€ Sent â”€â”€
    ['GET',  '/communications/sent',                   'CommunicationsController@sent',            'notifications.view'],
    ['GET',  '/communications/api/sent/datatable',     'CommunicationsApiController@sentDatatable', 'notifications.view'],

    // â”€â”€ Bulk â”€â”€
    ['GET',  '/communications/bulk',                   'CommunicationsController@bulk',            'notifications.bulk'],
    ['GET',  '/communications/bulk/create',            'CommunicationsController@bulkCreate',      'notifications.bulk'],
    ['GET',  '/communications/bulk/{id}',              'CommunicationsController@bulkView',        'notifications.bulk'],
    ['POST', '/communications/bulk',                   'CommunicationsApiController@bulkStore',     'notifications.bulk'],
    ['POST', '/communications/bulk/{id}/send',         'CommunicationsApiController@bulkSend',      'notifications.bulk'],
    ['POST', '/communications/bulk/{id}/cancel',       'CommunicationsApiController@bulkCancel',    'notifications.bulk'],
    ['GET',  '/communications/api/bulk/datatable',     'CommunicationsApiController@bulkDatatable', 'notifications.bulk'],
    ['GET',  '/communications/api/bulk/{id}/recipients','CommunicationsApiController@bulkRecipients','notifications.bulk'],

    // â”€â”€ Logs â”€â”€
    ['GET',  '/communications/email-log',              'CommunicationsController@emailLog',        'notifications.view'],
    ['GET',  '/communications/sms-log',                'CommunicationsController@smsLog',          'notifications.view'],
    ['GET',  '/communications/api/email-log/datatable','CommunicationsApiController@emailLogDatatable','notifications.view'],
    ['GET',  '/communications/api/sms-log/datatable',  'CommunicationsApiController@smsLogDatatable', 'notifications.view'],

    // â”€â”€ Announcements â”€â”€
    ['GET',  '/communications/announcements',              'CommunicationsController@announcements',       'notifications.send'],
    ['GET',  '/communications/announcements/create',       'CommunicationsController@announcementCreate',  'notifications.send'],
    ['GET',  '/communications/announcements/{id}/edit',    'CommunicationsController@announcementEdit',    'notifications.send'],
    ['POST', '/communications/announcements',              'CommunicationsApiController@announcementStore', 'notifications.send'],
    ['POST', '/communications/announcements/{id}',         'CommunicationsApiController@announcementUpdate','notifications.send'],
    ['POST', '/communications/announcements/{id}/delete',  'CommunicationsApiController@announcementDelete','notifications.send'],
    ['POST', '/communications/announcements/{id}/toggle-publish','CommunicationsApiController@announcementTogglePublish','notifications.send'],
    ['POST', '/communications/announcements/{id}/toggle-pin',   'CommunicationsApiController@announcementTogglePin','notifications.send'],
    ['GET',  '/communications/api/announcements/datatable','CommunicationsApiController@announcementsDatatable','notifications.send'],

    // â”€â”€ Document Templates â”€â”€
    ['GET',  '/communications/document-templates',              'CommunicationsController@docTemplates',       'documents.view'],
    ['GET',  '/communications/document-templates/create',       'CommunicationsController@docTemplateCreate',  'documents.manage'],
    ['GET',  '/communications/document-templates/{id}/edit',    'CommunicationsController@docTemplateEdit',    'documents.manage'],
    ['POST', '/communications/document-templates',              'CommunicationsApiController@docTemplateStore', 'documents.manage'],
    ['POST', '/communications/document-templates/{id}',         'CommunicationsApiController@docTemplateUpdate','documents.manage'],
    ['POST', '/communications/document-templates/{id}/delete',  'CommunicationsApiController@docTemplateDelete','documents.manage'],
    ['GET',  '/communications/api/document-templates/datatable','CommunicationsApiController@docTemplatesDatatable','documents.view'],

    // â”€â”€ Document Generation â”€â”€
    ['GET',  '/communications/documents/generate',     'CommunicationsController@generateDocument',  'documents.manage'],
    ['POST', '/communications/documents/generate',     'CommunicationsApiController@generateDocumentPost', 'documents.manage'],
    ['POST', '/communications/documents/preview',      'CommunicationsApiController@previewDocument',      'documents.manage'],
    ['GET',  '/communications/api/generated/datatable', 'CommunicationsApiController@generatedDatatable',  'documents.view'],
];
```

## File: modules\communications\views\announcements\form.php

```php
<?php $isEdit = !empty($announcement); $a = $announcement ?? []; ?>
<div class="row justify-content-center"><div class="col-lg-9"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $isEdit ? lang('comm.edit_announcement') : lang('comm.create_announcement') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $isEdit ? '/communications/announcements/' . $a['id'] : '/communications/announcements' ?>" data-ajax="true" enctype="multipart/form-data">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-8">
                    <label class="form-label">Title (EN) <span class="text-danger">*</span></label>
                    <input type="text" name="title_en" class="form-control" value="<?= e($a['title_en'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="title_en"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Category <span class="text-danger">*</span></label>
                    <select name="category" class="form-select" required>
                        <?php foreach (['general','maintenance','event','policy','financial','emergency','other'] as $c): ?>
                        <option value="<?= $c ?>" <?= selected($c, $a['category'] ?? 'general') ?>><?= ucfirst($c) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </div>
            <div class="mt-3">
                <label class="form-label">Title (AR)</label>
                <input type="text" name="title_ar" class="form-control" value="<?= e($a['title_ar'] ?? '') ?>" dir="rtl">
            </div>
            <div class="row g-3 mt-1">
                <div class="col-md-3">
                    <label class="form-label">Priority <span class="text-danger">*</span></label>
                    <select name="priority" class="form-select" required>
                        <?php foreach (['low','normal','high','urgent'] as $p): ?>
                        <option value="<?= $p ?>" <?= selected($p, $a['priority'] ?? 'normal') ?>><?= ucfirst($p) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Audience <span class="text-danger">*</span></label>
                    <select name="target_audience" class="form-select" required>
                        <?php foreach (['all_members','specific_types','staff','board','public'] as $ta): ?>
                        <option value="<?= $ta ?>" <?= selected($ta, $a['target_audience'] ?? 'all_members') ?>><?= ucwords(str_replace('_',' ',$ta)) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Publish Date <span class="text-danger">*</span></label>
                    <input type="text" name="publish_date" class="form-control datetimepicker" value="<?= e($a['publish_date'] ?? date('Y-m-d H:i')) ?>" required>
                    <div class="invalid-feedback" data-field="publish_date"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Expiry Date</label>
                    <input type="text" name="expiry_date" class="form-control datetimepicker" value="<?= e($a['expiry_date'] ?? '') ?>">
                </div>
            </div>
            <div class="mt-3">
                <label class="form-label">Target Membership Types</label>
                <select name="target_membership_types[]" class="form-select select2" multiple>
                    <?php
                    $selectedTypes = json_decode($a['target_membership_types'] ?? '[]', true) ?: [];
                    foreach ($memTypes as $mt): ?>
                    <option value="<?= $mt['id'] ?>" <?= in_array($mt['id'], $selectedTypes) ? 'selected' : '' ?>><?= e($mt['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="mt-3">
                <label class="form-label">Body (EN) <span class="text-danger">*</span></label>
                <textarea name="body_en" class="form-control" rows="6" required><?= e($a['body_en'] ?? '') ?></textarea>
                <div class="invalid-feedback" data-field="body_en"></div>
            </div>
            <div class="mt-3">
                <label class="form-label">Body (AR)</label>
                <textarea name="body_ar" class="form-control" rows="6" dir="rtl"><?= e($a['body_ar'] ?? '') ?></textarea>
            </div>
            <div class="row g-3 mt-1">
                <div class="col-md-6">
                    <label class="form-label">Image</label>
                    <input type="file" name="image" class="form-control" accept="image/*">
                    <?php if (!empty($a['image_path'])): ?><small class="text-muted">Current: <?= e($a['image_path']) ?></small><?php endif; ?>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Attachment</label>
                    <input type="file" name="attachment" class="form-control" accept=".pdf,.doc,.docx,.jpg,.png">
                    <?php if (!empty($a['attachment_path'])): ?><small class="text-muted">Current: <?= e($a['attachment_path']) ?></small><?php endif; ?>
                </div>
            </div>
            <div class="row g-3 mt-2">
                <div class="col-auto">
                    <div class="form-check"><input class="form-check-input" type="checkbox" name="is_published" value="1" id="ann_pub" <?= checked(1, $a['is_published'] ?? 0) ?>><label class="form-check-label" for="ann_pub">Published</label></div>
                </div>
                <div class="col-auto">
                    <div class="form-check"><input class="form-check-input" type="checkbox" name="is_pinned" value="1" id="ann_pin" <?= checked(1, $a['is_pinned'] ?? 0) ?>><label class="form-check-label" for="ann_pin">Pinned</label></div>
                </div>
                <div class="col-auto">
                    <div class="form-check"><input class="form-check-input" type="checkbox" name="send_notification" value="1" id="ann_notif" <?= checked(1, $a['send_notification'] ?? 0) ?>><label class="form-check-label" for="ann_notif">Send Notification</label></div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/communications/announcements" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\communications\views\announcements\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-bullhorn me-2"></i><?= lang('comm.announcements') ?></h4>
    <a href="/communications/announcements/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>
<div class="card"><div class="card-body">
    <table id="announcements-table" class="table table-striped table-hover w-100"
           data-url="/communications/api/announcements/datatable"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"title_en","title"=>"Title"],
               ["data"=>"category","title"=>"Category"],
               ["data"=>"priority","title"=>"Priority"],
               ["data"=>"target_audience","title"=>"Audience"],
               ["data"=>"is_published","title"=>"Published"],
               ["data"=>"is_pinned","title"=>"Pinned"],
               ["data"=>"publish_date","title"=>"Publish Date"],
               ["data"=>"id","title"=>"Actions","orderable"=>false,"searchable"=>false]
           ]) ?>'>
        <thead><tr><th>#</th><th>Title</th><th>Category</th><th>Priority</th><th>Audience</th><th>Published</th><th>Pinned</th><th>Publish</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('announcements-table', {
        columnDefs: [
            { targets: 1, render: function(d){ return d ? d.substring(0,40)+(d.length>40?'...':'') : ''; }},
            { targets: 3, render: function(d){
                var map = {low:'secondary',normal:'primary',high:'warning',urgent:'danger'};
                return '<span class="badge bg-'+(map[d]||'secondary')+'">'+d+'</span>';
            }},
            { targets: 5, render: function(d,t,row){
                var cls = d==1?'btn-success':'btn-outline-secondary';
                var icon = d==1?'fa-eye':'fa-eye-slash';
                return '<button class="btn btn-sm '+cls+'" onclick="togglePublish('+row.id+')"><i class="fas '+icon+'"></i></button>';
            }},
            { targets: 6, render: function(d,t,row){
                var cls = d==1?'btn-warning':'btn-outline-secondary';
                return '<button class="btn btn-sm '+cls+'" onclick="togglePin('+row.id+')"><i class="fas fa-thumbtack"></i></button>';
            }},
            { targets: 7, render: function(d){ return d ? d.substring(0,10) : ''; }},
            { targets: 8, render: function(d){
                return '<div class="btn-group btn-group-sm">'
                    +'<a href="/communications/announcements/'+d+'/edit" class="btn btn-outline-primary"><i class="fas fa-edit"></i></a>'
                    +' <button class="btn btn-outline-danger" onclick="deleteAnn('+d+')"><i class="fas fa-trash"></i></button>'
                    +'</div>';
            }}
        ]
    });
});
function togglePublish(id){$.post('/communications/announcements/'+id+'/toggle-publish',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('announcements-table');}});}
function togglePin(id){$.post('/communications/announcements/'+id+'/toggle-pin',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('announcements-table');}});}
function deleteAnn(id){Swal.fire({title:'Delete?',icon:'warning',showCancelButton:true,confirmButtonColor:'#e53e3e',confirmButtonText:'Delete'}).then(r=>{if(r.isConfirmed)$.post('/communications/announcements/'+id+'/delete',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('announcements-table');}});});}
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\bulk\form.php

```php
<div class="row justify-content-center"><div class="col-lg-9"><div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-bullhorn me-2"></i><?= lang('comm.create_bulk') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/communications/bulk" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6">
                    <label class="form-label">Campaign Name <span class="text-danger">*</span></label>
                    <input type="text" name="campaign_name" class="form-control" required>
                    <div class="invalid-feedback" data-field="campaign_name"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Channel <span class="text-danger">*</span></label>
                    <select name="channel_id" class="form-select" required>
                        <option value="">-- Select --</option>
                        <?php foreach ($channels as $ch): ?>
                        <option value="<?= $ch['id'] ?>"><?= e($ch['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <div class="invalid-feedback" data-field="channel_id"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Template</label>
                    <select name="template_id" class="form-select">
                        <option value="">-- None --</option>
                        <?php foreach ($templates as $t): ?>
                        <option value="<?= $t['id'] ?>"><?= e($t['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </div>
            <div class="mt-3">
                <label class="form-label">Target Membership Types</label>
                <select name="target_membership_types[]" class="form-select select2" multiple>
                    <?php foreach ($memTypes as $mt): ?>
                    <option value="<?= $mt['id'] ?>"><?= e($mt['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
                <small class="text-muted">Leave empty to target all active members</small>
            </div>
            <div class="mt-3">
                <label class="form-label">Subject</label>
                <input type="text" name="subject" class="form-control">
            </div>
            <div class="mt-3">
                <label class="form-label">Message Body <span class="text-danger">*</span></label>
                <textarea name="body" class="form-control" rows="8" required placeholder="Type your message here..."></textarea>
                <div class="invalid-feedback" data-field="body"></div>
            </div>
            <div class="row g-3 mt-1">
                <div class="col-md-4">
                    <label class="form-label">Schedule At</label>
                    <input type="text" name="scheduled_at" class="form-control datetimepicker" placeholder="Leave empty to send immediately">
                </div>
                <div class="col-md-4">
                    <label class="form-label">Status</label>
                    <select name="status" class="form-select">
                        <option value="draft">Draft (save only)</option>
                        <option value="scheduled">Scheduled</option>
                    </select>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Create Campaign</button>
                <a href="/communications/bulk" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\communications\views\bulk\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-bullhorn me-2"></i><?= lang('comm.bulk_communications') ?></h4>
    <?php if (Gate::can('notifications.bulk')): ?>
    <a href="/communications/bulk/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('comm.create_bulk') ?></a>
    <?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="bulk-table" class="table table-striped table-hover w-100"
           data-url="/communications/api/bulk/datatable"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"campaign_name","title"=>"Campaign"],
               ["data"=>"channel_name","title"=>"Channel"],
               ["data"=>"total_recipients","title"=>"Recipients"],
               ["data"=>"status","title"=>"Status"],
               ["data"=>"created_at","title"=>"Created"],
               ["data"=>"id","title"=>"Actions","orderable"=>false,"searchable"=>false]
           ]) ?>'>
        <thead><tr><th>#</th><th>Campaign</th><th>Channel</th><th>Recipients</th><th>Status</th><th>Created</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('bulk-table', {
        columnDefs: [
            { targets: 3, render: function(d){ return '<span class="badge bg-primary">'+d+'</span>'; }},
            { targets: 4, render: function(d){
                var map = {draft:'secondary',scheduled:'info',sending:'warning',completed:'success',paused:'warning',cancelled:'danger'};
                return '<span class="badge bg-'+(map[d]||'secondary')+'">'+d+'</span>';
            }},
            { targets: 5, render: function(d){ return d ? new Date(d).toLocaleString() : ''; }},
            { targets: 6, render: function(d){ return '<a href="/communications/bulk/'+d+'" class="btn btn-outline-primary btn-sm"><i class="fas fa-eye"></i></a>'; }}
        ]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\bulk\view.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= e($campaign['campaign_name']) ?></h4>
        <span class="text-muted"><?= e($campaign['channel_name']) ?> | <?= status_badge($campaign['status']) ?></span>
    </div>
    <div class="d-flex gap-2">
        <?php if (in_array($campaign['status'], ['draft', 'scheduled', 'paused'])): ?>
        <button class="btn btn-success" onclick="sendCampaign(<?= $campaign['id'] ?>)"><i class="fas fa-paper-plane me-1"></i>Send Now</button>
        <button class="btn btn-outline-danger" onclick="cancelCampaign(<?= $campaign['id'] ?>)"><i class="fas fa-ban me-1"></i>Cancel</button>
        <?php endif; ?>
        <a href="/communications/bulk" class="btn btn-secondary"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-3 mb-4">
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body text-center"><h6 class="text-muted">Total</h6><h2><?= number_format($campaign['total_recipients']) ?></h2></div></div></div>
    <div class="col-md-3"><div class="card border-success h-100"><div class="card-body text-center"><h6 class="text-muted">Sent</h6><h2><?= number_format($campaign['sent_count']) ?></h2></div></div></div>
    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body text-center"><h6 class="text-muted">Failed</h6><h2><?= number_format($campaign['failed_count']) ?></h2></div></div></div>
    <div class="col-md-3"><div class="card h-100"><div class="card-body text-center"><h6 class="text-muted">Created</h6><p class="mb-0"><?= format_datetime($campaign['created_at']) ?></p></div></div></div>
</div>

<div class="card mb-4">
    <div class="card-header"><h6 class="mb-0">Message</h6></div>
    <div class="card-body">
        <?php if ($campaign['subject']): ?><p><strong>Subject:</strong> <?= e($campaign['subject']) ?></p><?php endif; ?>
        <div class="border p-3 rounded bg-light"><?= nl2br(e($campaign['body'])) ?></div>
    </div>
</div>

<div class="card">
    <div class="card-header"><h6 class="mb-0">Recipients</h6></div>
    <div class="card-body">
        <table id="recipients-table" class="table table-striped table-hover w-100"
               data-url="/communications/api/bulk/<?= $campaign['id'] ?>/recipients"
               data-columns='<?= json_encode([
                   ["data"=>"id","title"=>"#","width"=>"50px"],
                   ["data"=>"recipient_address","title"=>"Address"],
                   ["data"=>"full_name_en","title"=>"Member"],
                   ["data"=>"status","title"=>"Status"],
                   ["data"=>"sent_at","title"=>"Sent At"]
               ]) ?>'>
            <thead><tr><th>#</th><th>Address</th><th>Member</th><th>Status</th><th>Sent At</th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('recipients-table', {
        columnDefs: [
            { targets: 3, render: function(d){
                var map = {pending:'warning',sent:'success',failed:'danger',bounced:'danger'};
                return '<span class="badge bg-'+(map[d]||'secondary')+'">'+d+'</span>';
            }},
            { targets: 4, render: function(d){ return d ? new Date(d).toLocaleString() : '-'; }}
        ]
    });
});
function sendCampaign(id){
    Swal.fire({title:'Send Campaign?',text:'This will queue all pending recipients.',icon:'question',showCancelButton:true,confirmButtonText:'Send'}).then(r=>{
        if(r.isConfirmed) $.post('/communications/bulk/'+id+'/send',{_token:'<?= csrf_token() ?>'},function(r){
            if(r.success){FormHelper.showToast('success',r.message);setTimeout(()=>location.reload(),1000);}
            else FormHelper.showToast('error',r.message);
        });
    });
}
function cancelCampaign(id){
    Swal.fire({title:'Cancel?',icon:'warning',showCancelButton:true,confirmButtonColor:'#e53e3e',confirmButtonText:'Yes, Cancel'}).then(r=>{
        if(r.isConfirmed) $.post('/communications/bulk/'+id+'/cancel',{_token:'<?= csrf_token() ?>'},function(r){
            if(r.success){FormHelper.showToast('success',r.message);setTimeout(()=>location.reload(),1000);}
            else FormHelper.showToast('error',r.message);
        });
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\channels\form.php

```php
<?php $isEdit = !empty($channel); ?>
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= $isEdit ? lang('comm.edit_channel') : lang('comm.create_channel') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= $isEdit ? '/communications/channels/' . $channel['id'] : '/communications/channels' ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-4">
                            <label class="form-label">Channel Code <span class="text-danger">*</span></label>
                            <input type="text" name="channel_code" class="form-control" value="<?= e($channel['channel_code'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="channel_code"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                            <input type="text" name="name_en" class="form-control" value="<?= e($channel['name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="name_en"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                            <input type="text" name="name_ar" class="form-control" value="<?= e($channel['name_ar'] ?? '') ?>" required dir="rtl">
                            <div class="invalid-feedback" data-field="name_ar"></div>
                        </div>
                    </div>
                    <div class="row g-3 mt-1">
                        <div class="col-md-4">
                            <label class="form-label">Channel Type <span class="text-danger">*</span></label>
                            <select name="channel_type" class="form-select" required>
                                <?php foreach (['email','sms','push','whatsapp','in_app','webhook'] as $t): ?>
                                <option value="<?= $t ?>" <?= selected($t, $channel['channel_type'] ?? '') ?>><?= ucfirst($t) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Provider</label>
                            <input type="text" name="provider" class="form-control" value="<?= e($channel['provider'] ?? '') ?>">
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">&nbsp;</label>
                            <div class="form-check mt-2">
                                <input class="form-check-input" type="checkbox" name="is_active" value="1" id="ch_active" <?= checked(1, $channel['is_active'] ?? 1) ?>>
                                <label class="form-check-label" for="ch_active">Active</label>
                            </div>
                        </div>
                    </div>
                    <div class="mt-3">
                        <label class="form-label">Configuration (JSON)</label>
                        <textarea name="config" class="form-control font-monospace" rows="5" placeholder='{"api_key": "...", "sender": "..."}'><?= e($channel['config'] ?? '') ?></textarea>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/communications/channels" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\communications\views\channels\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-broadcast-tower me-2"></i><?= lang('comm.channels') ?></h4>
    <?php if (Gate::can('notifications.send')): ?>
    <a href="/communications/channels/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="channels-table" class="table table-striped table-hover w-100"
               data-url="/communications/api/channels/datatable"
               data-columns='<?= json_encode([
                   ["data" => "id", "title" => "#", "width" => "50px"],
                   ["data" => "channel_code", "title" => "Code"],
                   ["data" => "name_en", "title" => "Name"],
                   ["data" => "channel_type", "title" => "Type"],
                   ["data" => "provider", "title" => "Provider"],
                   ["data" => "is_active", "title" => "Active"],
                   ["data" => "id", "title" => "Actions", "orderable" => false, "searchable" => false]
               ]) ?>'>
            <thead><tr><th>#</th><th>Code</th><th>Name</th><th>Type</th><th>Provider</th><th>Active</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('channels-table', {
        columnDefs: [
            { targets: 3, render: function(d){ return '<span class="badge bg-info">'+d+'</span>'; }},
            { targets: 5, render: function(d){ return d == 1 ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>'; }},
            { targets: 6, render: function(d){
                return '<a href="/communications/channels/'+d+'/edit" class="btn btn-outline-primary btn-sm"><i class="fas fa-edit"></i></a>';
            }}
        ]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\documents\generate.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-file-export me-2"></i><?= lang('comm.generate_document') ?></h4>
</div>

<div class="row">
    <div class="col-lg-5">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Generate</h6></div>
            <div class="card-body">
                <form id="generate-form">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label">Template <span class="text-danger">*</span></label>
                        <select name="template_id" id="gen-template" class="form-select select2" required>
                            <option value="">-- Select Template --</option>
                            <?php foreach ($templates as $t): ?>
                            <option value="<?= $t['id'] ?>"><?= e($t['name_en']) ?> (<?= e($t['template_type']) ?>)</option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Member (optional)</label>
                        <select name="member_id" id="gen-member" class="form-select select2" data-url="/api/members/search">
                            <option value="">-- No Member --</option>
                        </select>
                        <small class="text-muted">Search by name, number, or phone</small>
                    </div>
                    <div class="d-flex gap-2">
                        <button type="button" class="btn btn-outline-primary" onclick="previewDoc()"><i class="fas fa-eye me-1"></i>Preview</button>
                        <button type="button" class="btn btn-primary" onclick="generateDoc()"><i class="fas fa-file-export me-1"></i>Generate</button>
                    </div>
                </form>
            </div>
        </div>

        <div class="card mt-4">
            <div class="card-header"><h6 class="mb-0">Recently Generated</h6></div>
            <div class="card-body p-0">
                <div class="table-responsive">
                    <table class="table table-sm mb-0">
                        <thead><tr><th>Template</th><th>Member</th><th>Date</th></tr></thead>
                        <tbody>
                        <?php foreach ($recentDocs as $doc): ?>
                        <tr>
                            <td><?= e($doc['template_name'] ?? '-') ?></td>
                            <td><?= e($doc['member_name'] ?? '-') ?></td>
                            <td><?= format_datetime($doc['generated_at']) ?></td>
                        </tr>
                        <?php endforeach; ?>
                        <?php if (empty($recentDocs)): ?>
                        <tr><td colspan="3" class="text-muted text-center py-3">No documents generated yet</td></tr>
                        <?php endif; ?>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <div class="col-lg-7">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Preview</h6></div>
            <div class="card-body">
                <div id="doc-preview" class="border rounded p-3 bg-white" style="min-height:400px">
                    <div class="text-center text-muted py-5"><i class="fas fa-file fa-3x mb-3"></i><p>Select a template and click Preview</p></div>
                </div>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$('#gen-member').select2({
    theme: 'bootstrap-5', width: '100%', allowClear: true, placeholder: '-- No Member --',
    ajax: {
        url: '/api/members/search', dataType: 'json', delay: 300,
        data: function(p){ return {q: p.term}; },
        processResults: function(d){ return {results: d.data || d}; }
    }, minimumInputLength: 2
});

function previewDoc(){
    var tid = $('#gen-template').val();
    if(!tid){FormHelper.showToast('error','Select a template');return;}
    $.post('/communications/documents/preview',{_token:'<?= csrf_token() ?>',template_id:tid,member_id:$('#gen-member').val()||0},function(r){
        if(r.success) $('#doc-preview').html(r.html);
        else FormHelper.showToast('error',r.message);
    });
}
function generateDoc(){
    var tid = $('#gen-template').val();
    if(!tid){FormHelper.showToast('error','Select a template');return;}
    $.post('/communications/documents/generate',{_token:'<?= csrf_token() ?>',template_id:tid,member_id:$('#gen-member').val()||0},function(r){
        if(r.success){
            FormHelper.showToast('success',r.message);
            var win = window.open('','_blank');
            win.document.write(r.html);
            win.document.close();
        } else FormHelper.showToast('error',r.message);
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\documents\template_form.php

```php
<?php $isEdit = !empty($template); $t = $template ?? []; ?>
<div class="row justify-content-center"><div class="col-lg-10"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $isEdit ? lang('comm.edit_doc_template') : lang('comm.create_doc_template') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $isEdit ? '/communications/document-templates/' . $t['id'] : '/communications/document-templates' ?>" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-3">
                    <label class="form-label">Code <span class="text-danger">*</span></label>
                    <input type="text" name="template_code" class="form-control" value="<?= e($t['template_code'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="template_code"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                    <input type="text" name="name_en" class="form-control" value="<?= e($t['name_en'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="name_en"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                    <input type="text" name="name_ar" class="form-control" value="<?= e($t['name_ar'] ?? '') ?>" required dir="rtl">
                    <div class="invalid-feedback" data-field="name_ar"></div>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Category <span class="text-danger">*</span></label>
                    <input type="text" name="category" class="form-control" value="<?= e($t['category'] ?? '') ?>" required placeholder="e.g. membership, finance">
                    <div class="invalid-feedback" data-field="category"></div>
                </div>
            </div>
            <div class="row g-3 mt-1">
                <div class="col-md-3">
                    <label class="form-label">Type <span class="text-danger">*</span></label>
                    <select name="template_type" class="form-select" required>
                        <?php foreach (['letter','certificate','receipt','invoice','contract','form','report','badge','other'] as $tt): ?>
                        <option value="<?= $tt ?>" <?= selected($tt, $t['template_type'] ?? '') ?>><?= ucfirst($tt) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Paper Size <span class="text-danger">*</span></label>
                    <select name="paper_size" class="form-select" required>
                        <?php foreach (['a4','a3','letter','legal','custom'] as $ps): ?>
                        <option value="<?= $ps ?>" <?= selected($ps, $t['paper_size'] ?? 'a4') ?>><?= strtoupper($ps) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label">Orientation <span class="text-danger">*</span></label>
                    <select name="orientation" class="form-select" required>
                        <option value="portrait" <?= selected('portrait', $t['orientation'] ?? 'portrait') ?>>Portrait</option>
                        <option value="landscape" <?= selected('landscape', $t['orientation'] ?? '') ?>>Landscape</option>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label">&nbsp;</label>
                    <div class="form-check mt-2">
                        <input class="form-check-input" type="checkbox" name="is_active" value="1" id="dt_active" <?= checked(1, $t['is_active'] ?? 1) ?>>
                        <label class="form-check-label" for="dt_active">Active</label>
                    </div>
                </div>
            </div>
            <div class="mt-3">
                <label class="form-label">Header Template (HTML)</label>
                <textarea name="header_template" class="form-control font-monospace" rows="4"><?= e($t['header_template'] ?? '') ?></textarea>
            </div>
            <div class="mt-3">
                <label class="form-label">Body (EN) â€” Use <code>{{variable_name}}</code> for placeholders</label>
                <textarea name="template_body_en" class="form-control font-monospace" rows="12"><?= e($t['template_body_en'] ?? '') ?></textarea>
                <small class="text-muted">Variables: {{member_name}}, {{member_number}}, {{national_id}}, {{membership_type}}, {{join_date}}, {{club_name}}, {{current_date}}</small>
            </div>
            <div class="mt-3">
                <label class="form-label">Body (AR)</label>
                <textarea name="template_body_ar" class="form-control font-monospace" rows="12" dir="rtl"><?= e($t['template_body_ar'] ?? '') ?></textarea>
            </div>
            <div class="mt-3">
                <label class="form-label">Footer Template (HTML)</label>
                <textarea name="footer_template" class="form-control font-monospace" rows="3"><?= e($t['footer_template'] ?? '') ?></textarea>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/communications/document-templates" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\communications\views\documents\templates_index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-file-alt me-2"></i><?= lang('comm.document_templates') ?></h4>
    <?php if (Gate::can('documents.manage')): ?>
    <a href="/communications/document-templates/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="doctemplates-table" class="table table-striped table-hover w-100"
           data-url="/communications/api/document-templates/datatable"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"template_code","title"=>"Code"],
               ["data"=>"name_en","title"=>"Name"],
               ["data"=>"category","title"=>"Category"],
               ["data"=>"template_type","title"=>"Type"],
               ["data"=>"is_active","title"=>"Active"],
               ["data"=>"version","title"=>"Ver"],
               ["data"=>"id","title"=>"Actions","orderable"=>false,"searchable"=>false]
           ]) ?>'>
        <thead><tr><th>#</th><th>Code</th><th>Name</th><th>Category</th><th>Type</th><th>Active</th><th>Ver</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('doctemplates-table', {
        columnDefs: [
            { targets: 5, render: function(d){ return d==1?'<span class="badge bg-success">Active</span>':'<span class="badge bg-secondary">Inactive</span>'; }},
            { targets: 7, render: function(d){
                return '<div class="btn-group btn-group-sm">'
                    +'<a href="/communications/document-templates/'+d+'/edit" class="btn btn-outline-primary"><i class="fas fa-edit"></i></a>'
                    +' <button class="btn btn-outline-danger" onclick="deleteDocTpl('+d+')"><i class="fas fa-trash"></i></button>'
                    +'</div>';
            }}
        ]
    });
});
function deleteDocTpl(id){Swal.fire({title:'Deactivate?',icon:'warning',showCancelButton:true,confirmButtonColor:'#e53e3e',confirmButtonText:'Yes'}).then(r=>{if(r.isConfirmed)$.post('/communications/document-templates/'+id+'/delete',{_token:'<?= csrf_token() ?>'},function(r){if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('doctemplates-table');}});});}
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\queue\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-list-ol me-2"></i><?= lang('comm.notification_queue') ?></h4>
</div>

<div class="row g-3 mb-4">
    <div class="col-md-3">
        <div class="card border-warning h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Queued</h6><h3 class="mb-0"><?= number_format($stats['queued']) ?></h3></div>
            <div class="align-self-center"><i class="fas fa-clock fa-2x text-warning opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-info h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Processing</h6><h3 class="mb-0"><?= number_format($stats['processing']) ?></h3></div>
            <div class="align-self-center"><i class="fas fa-spinner fa-2x text-info opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-success h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Sent Today</h6><h3 class="mb-0"><?= number_format($stats['sent']) ?></h3></div>
            <div class="align-self-center"><i class="fas fa-check-circle fa-2x text-success opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Failed</h6><h3 class="mb-0"><?= number_format($stats['failed']) ?></h3></div>
            <div class="align-self-center"><i class="fas fa-exclamation-circle fa-2x text-danger opacity-50"></i></div></div>
        </div></div>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <div class="row g-2 align-items-end" data-filter-table="queue-table">
            <div class="col-md-3">
                <label class="form-label form-label-sm mb-1">Status</label>
                <select name="filter_status" class="form-select form-select-sm" id="filter-status">
                    <option value="">All</option>
                    <option value="queued">Queued</option>
                    <option value="processing">Processing</option>
                    <option value="sent">Sent</option>
                    <option value="failed">Failed</option>
                    <option value="cancelled">Cancelled</option>
                </select>
            </div>
            <div class="col-auto"><button class="btn btn-sm btn-outline-primary" onclick="DataTableHelper.reload('queue-table')"><i class="fas fa-filter me-1"></i>Filter</button></div>
        </div>
    </div>
</div>

<div class="card"><div class="card-body">
    <table id="queue-table" class="table table-striped table-hover w-100"
           data-url="/communications/api/queue/datatable"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"channel_name","title"=>"Channel"],
               ["data"=>"recipient_address","title"=>"Recipient"],
               ["data"=>"subject","title"=>"Subject"],
               ["data"=>"status","title"=>"Status"],
               ["data"=>"priority","title"=>"Priority"],
               ["data"=>"created_at","title"=>"Created"],
               ["data"=>"id","title"=>"Actions","orderable"=>false,"searchable"=>false]
           ]) ?>'>
        <thead><tr><th>#</th><th>Channel</th><th>Recipient</th><th>Subject</th><th>Status</th><th>Priority</th><th>Created</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('queue-table', {
        columnDefs: [
            { targets: 3, render: function(d){ return d ? d.substring(0,50)+(d.length>50?'...':'') : '-'; }},
            { targets: 4, render: function(d){
                var map = {queued:'warning',processing:'info',sent:'success',failed:'danger',cancelled:'secondary'};
                return '<span class="badge bg-'+(map[d]||'secondary')+'">'+d+'</span>';
            }},
            { targets: 6, render: function(d){ return d ? new Date(d).toLocaleString() : ''; }},
            { targets: 7, render: function(d, t, row){
                var html = '';
                if(row.status==='failed') html += '<button class="btn btn-outline-warning btn-sm me-1" onclick="retryQueue('+d+')"><i class="fas fa-redo"></i></button>';
                if(row.status==='queued'||row.status==='failed') html += '<button class="btn btn-outline-danger btn-sm" onclick="cancelQueue('+d+')"><i class="fas fa-ban"></i></button>';
                return html || '-';
            }}
        ]
    });
});
function retryQueue(id){
    $.post('/communications/queue/'+id+'/retry',{_token:'<?= csrf_token() ?>'},function(r){
        if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('queue-table');}
        else FormHelper.showToast('error',r.message);
    });
}
function cancelQueue(id){
    $.post('/communications/queue/'+id+'/cancel',{_token:'<?= csrf_token() ?>'},function(r){
        if(r.success){FormHelper.showToast('success',r.message);DataTableHelper.reload('queue-table');}
        else FormHelper.showToast('error',r.message);
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\sent\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-paper-plane me-2"></i><?= lang('comm.sent_notifications') ?></h4>
</div>
<div class="card"><div class="card-body">
    <table id="sent-table" class="table table-striped table-hover w-100"
           data-url="/communications/api/sent/datatable"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"channel_name","title"=>"Channel"],
               ["data"=>"recipient_address","title"=>"Recipient"],
               ["data"=>"subject","title"=>"Subject"],
               ["data"=>"delivery_status","title"=>"Delivery"],
               ["data"=>"sent_at","title"=>"Sent At"],
               ["data"=>"delivered_at","title"=>"Delivered"]
           ]) ?>'>
        <thead><tr><th>#</th><th>Channel</th><th>Recipient</th><th>Subject</th><th>Delivery</th><th>Sent</th><th>Delivered</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('sent-table', {
        columnDefs: [
            { targets: 3, render: function(d){ return d ? d.substring(0,40)+(d.length>40?'...':'') : '-'; }},
            { targets: 4, render: function(d){
                var map = {sent:'info',delivered:'success',read:'primary',bounced:'danger',failed:'danger'};
                return '<span class="badge bg-'+(map[d]||'secondary')+'">'+d+'</span>';
            }},
            { targets: [5,6], render: function(d){ return d ? new Date(d).toLocaleString() : '-'; }}
        ]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\templates\form.php

```php
<?php $isEdit = !empty($template); ?>

<div class="row justify-content-center">
    <div class="col-lg-9">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= $isEdit ? lang('comm.edit_template') : lang('comm.create_template') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= $isEdit ? '/communications/templates/' . $template['id'] : '/communications/templates' ?>" data-ajax="true">
                    <?= csrf_field() ?>

                    <div class="row g-3">
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('comm.template_code') ?> <span class="text-danger">*</span></label>
                            <input type="text" name="template_code" class="form-control" value="<?= e($template['template_code'] ?? '') ?>" required maxlength="50" placeholder="e.g. member_activated">
                            <div class="invalid-feedback" data-field="template_code"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('comm.name_en') ?> <span class="text-danger">*</span></label>
                            <input type="text" name="name_en" class="form-control" value="<?= e($template['name_en'] ?? '') ?>" required>
                            <div class="invalid-feedback" data-field="name_en"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('comm.name_ar') ?> <span class="text-danger">*</span></label>
                            <input type="text" name="name_ar" class="form-control" value="<?= e($template['name_ar'] ?? '') ?>" required dir="rtl">
                            <div class="invalid-feedback" data-field="name_ar"></div>
                        </div>
                    </div>

                    <div class="row g-3 mt-1">
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('comm.channel') ?> <span class="text-danger">*</span></label>
                            <select name="channel_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($channels as $ch): ?>
                                <option value="<?= $ch['id'] ?>" <?= selected($ch['id'], $template['channel_id'] ?? '') ?>><?= e($ch['name_en']) ?> (<?= e($ch['channel_code']) ?>)</option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="channel_id"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('comm.event_trigger') ?> <span class="text-danger">*</span></label>
                            <input type="text" name="event_trigger" class="form-control" value="<?= e($template['event_trigger'] ?? '') ?>" required placeholder="e.g. member.activated">
                            <div class="invalid-feedback" data-field="event_trigger"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">&nbsp;</label>
                            <div class="form-check mt-2">
                                <input class="form-check-input" type="checkbox" name="is_active" value="1" id="is_active" <?= checked(1, $template['is_active'] ?? 1) ?>>
                                <label class="form-check-label" for="is_active"><?= lang('comm.active') ?></label>
                            </div>
                        </div>
                    </div>

                    <div class="row g-3 mt-1">
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('comm.subject_en') ?></label>
                            <input type="text" name="subject_en" class="form-control" value="<?= e($template['subject_en'] ?? '') ?>">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('comm.subject_ar') ?></label>
                            <input type="text" name="subject_ar" class="form-control" value="<?= e($template['subject_ar'] ?? '') ?>" dir="rtl">
                        </div>
                    </div>

                    <div class="mt-3">
                        <label class="form-label"><?= lang('comm.body_en') ?> <span class="text-danger">*</span></label>
                        <textarea name="body_en" class="form-control" rows="8" required placeholder="Use {variable_name} for placeholders"><?= e($template['body_en'] ?? '') ?></textarea>
                        <div class="invalid-feedback" data-field="body_en"></div>
                        <small class="text-muted">Available variables: {member_name}, {member_number}, {club_name}, {amount}, {date}, {status}</small>
                    </div>

                    <div class="mt-3">
                        <label class="form-label"><?= lang('comm.body_ar') ?></label>
                        <textarea name="body_ar" class="form-control" rows="8" dir="rtl"><?= e($template['body_ar'] ?? '') ?></textarea>
                    </div>

                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/communications/templates" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\communications\views\templates\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-envelope-open-text me-2"></i><?= lang('comm.templates') ?></h4>
    <?php if (Gate::can('notifications.send')): ?>
    <a href="/communications/templates/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="templates-table" class="table table-striped table-hover w-100"
               data-url="/communications/api/templates/datatable"
               data-columns='<?= json_encode([
                   ["data" => "id", "title" => "#", "width" => "50px"],
                   ["data" => "template_code", "title" => "Code"],
                   ["data" => "name_en", "title" => "Name (EN)"],
                   ["data" => "channel_name", "title" => "Channel"],
                   ["data" => "event_trigger", "title" => "Event Trigger"],
                   ["data" => "is_active", "title" => "Active", "orderable" => false],
                   ["data" => "created_at", "title" => "Created"],
                   ["data" => "id", "title" => "Actions", "orderable" => false, "searchable" => false]
               ]) ?>'>
            <thead><tr>
                <th>#</th><th>Code</th><th>Name</th><th>Channel</th><th>Trigger</th><th>Active</th><th>Created</th><th>Actions</th>
            </tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('templates-table', {
        columnDefs: [
            { targets: 5, render: function(d){ return d == 1 ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>'; }},
            { targets: 6, render: function(d){ return d ? new Date(d).toLocaleDateString() : ''; }},
            { targets: 7, render: function(d, t, row){
                let html = '<div class="btn-group btn-group-sm">';
                html += '<a href="/communications/templates/'+d+'/edit" class="btn btn-outline-primary btn-sm" title="Edit"><i class="fas fa-edit"></i></a>';
                html += ' <button class="btn btn-outline-danger btn-sm" onclick="deleteTemplate('+d+')" title="Delete"><i class="fas fa-trash"></i></button>';
                html += '</div>';
                return html;
            }}
        ]
    });
});
function deleteTemplate(id){
    Swal.fire({title:'Delete Template?',text:'This may deactivate the template if in use.',icon:'warning',showCancelButton:true,confirmButtonColor:'#e53e3e',confirmButtonText:'Delete'}).then(r=>{
        if(r.isConfirmed){
            $.ajax({url:'/communications/templates/'+id+'/delete',type:'POST',data:{_token:'<?= csrf_token() ?>'},success:function(res){
                if(res.success){FormHelper.showToast('success',res.message);DataTableHelper.reload('templates-table');}
                else{FormHelper.showToast('error',res.message);}
            }});
        }
    });
}
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\triggers\form.php

```php
<?php $isEdit = !empty($trigger); ?>
<div class="row justify-content-center"><div class="col-lg-8"><div class="card">
    <div class="card-header"><h5 class="mb-0"><?= $isEdit ? lang('comm.edit_trigger') : lang('comm.create_trigger') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="<?= $isEdit ? '/communications/triggers/' . $trigger['id'] : '/communications/triggers' ?>" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6">
                    <label class="form-label">Trigger Code <span class="text-danger">*</span></label>
                    <input type="text" name="trigger_code" class="form-control" value="<?= e($trigger['trigger_code'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="trigger_code"></div>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Module <span class="text-danger">*</span></label>
                    <input type="text" name="module" class="form-control" value="<?= e($trigger['module'] ?? '') ?>" required placeholder="e.g. members, finance">
                    <div class="invalid-feedback" data-field="module"></div>
                </div>
            </div>
            <div class="row g-3 mt-1">
                <div class="col-md-6">
                    <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                    <input type="text" name="trigger_name_en" class="form-control" value="<?= e($trigger['trigger_name_en'] ?? '') ?>" required>
                    <div class="invalid-feedback" data-field="trigger_name_en"></div>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                    <input type="text" name="trigger_name_ar" class="form-control" value="<?= e($trigger['trigger_name_ar'] ?? '') ?>" required dir="rtl">
                    <div class="invalid-feedback" data-field="trigger_name_ar"></div>
                </div>
            </div>
            <div class="row g-3 mt-1">
                <div class="col-md-4">
                    <label class="form-label">Event Type <span class="text-danger">*</span></label>
                    <input type="text" name="event_type" class="form-control" value="<?= e($trigger['event_type'] ?? '') ?>" required placeholder="e.g. status_change">
                    <div class="invalid-feedback" data-field="event_type"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Template</label>
                    <select name="template_id" class="form-select select2">
                        <option value="">-- None --</option>
                        <?php foreach ($templates as $t): ?>
                        <option value="<?= $t['id'] ?>" <?= selected($t['id'], $trigger['template_id'] ?? '') ?>><?= e($t['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-4">
                    <label class="form-label">&nbsp;</label>
                    <div class="form-check mt-2">
                        <input class="form-check-input" type="checkbox" name="is_active" value="1" id="trg_active" <?= checked(1, $trigger['is_active'] ?? 1) ?>>
                        <label class="form-check-label" for="trg_active">Active</label>
                    </div>
                </div>
            </div>
            <div class="mt-3">
                <label class="form-label">Conditions (JSON)</label>
                <textarea name="conditions" class="form-control font-monospace" rows="4"><?= e($trigger['conditions'] ?? '') ?></textarea>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/communications/triggers" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\communications\views\triggers\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-bolt me-2"></i><?= lang('comm.triggers') ?></h4>
    <?php if (Gate::can('notifications.send')): ?>
    <a href="/communications/triggers/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="triggers-table" class="table table-striped table-hover w-100"
           data-url="/communications/api/triggers/datatable"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"trigger_code","title"=>"Code"],
               ["data"=>"trigger_name_en","title"=>"Name"],
               ["data"=>"module","title"=>"Module"],
               ["data"=>"event_type","title"=>"Event"],
               ["data"=>"template_name","title"=>"Template"],
               ["data"=>"is_active","title"=>"Active"],
               ["data"=>"id","title"=>"Actions","orderable"=>false,"searchable"=>false]
           ]) ?>'>
        <thead><tr><th>#</th><th>Code</th><th>Name</th><th>Module</th><th>Event</th><th>Template</th><th>Active</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('triggers-table', {
        columnDefs: [
            { targets: 6, render: function(d){ return d==1?'<span class="badge bg-success">Active</span>':'<span class="badge bg-secondary">Inactive</span>'; }},
            { targets: 7, render: function(d){ return '<a href="/communications/triggers/'+d+'/edit" class="btn btn-outline-primary btn-sm"><i class="fas fa-edit"></i></a>'; }}
        ]
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\communications\views\widgets\failed_notifications.php

```php
<?php
$db = Database::getInstance();
$failedCount = (int)$db->fetchColumn("SELECT COUNT(*) FROM notification_queue WHERE status = 'failed'");
$recentFailed = $db->fetchAll("SELECT recipient_address, failure_reason, created_at FROM notification_queue WHERE status = 'failed' ORDER BY created_at DESC LIMIT 3");
?>
<div class="card border-danger h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('comm.failed_notifications') ?></h6>
                <h3 class="mb-0"><?= number_format($failedCount) ?></h3>
            </div>
            <div class="align-self-center"><i class="fas fa-exclamation-triangle fa-2x text-danger opacity-50"></i></div>
        </div>
        <?php if (!empty($recentFailed)): ?>
        <hr class="my-2">
        <?php foreach ($recentFailed as $f): ?>
        <small class="d-block text-truncate text-muted" title="<?= e($f['failure_reason'] ?? '') ?>"><?= e($f['recipient_address']) ?>: <?= e(str_limit($f['failure_reason'] ?? 'Unknown', 30)) ?></small>
        <?php endforeach; ?>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\communications\views\widgets\sent_today.php

```php
<?php
$db = Database::getInstance();
$sentToday = (int)$db->fetchColumn("SELECT COUNT(*) FROM notifications_sent WHERE DATE(sent_at) = CURDATE()");
$byChannel = $db->fetchAll("SELECT nc.name_en, COUNT(*) AS cnt FROM notifications_sent ns LEFT JOIN notification_channels nc ON ns.channel_id = nc.id WHERE DATE(ns.sent_at) = CURDATE() GROUP BY nc.name_en ORDER BY cnt DESC LIMIT 5");
?>
<div class="card border-success h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('comm.sent_today') ?></h6>
                <h3 class="mb-0"><?= number_format($sentToday) ?></h3>
            </div>
            <div class="align-self-center"><i class="fas fa-paper-plane fa-2x text-success opacity-50"></i></div>
        </div>
        <?php if (!empty($byChannel)): ?>
        <hr class="my-2">
        <small class="text-muted">
            <?php foreach ($byChannel as $ch): ?>
            <?= e($ch['name_en']) ?>: <strong><?= $ch['cnt'] ?></strong><?= $ch !== end($byChannel) ? ' | ' : '' ?>
            <?php endforeach; ?>
        </small>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\facilities\Controllers\BookingController.php

```php
<?php

class BookingController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $facilities = $db->fetchAll("SELECT id, name_en, name_ar, facility_code FROM facilities WHERE is_active = 1 AND is_bookable = 1 ORDER BY name_en");

        $todayCount     = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_bookings WHERE booking_date = CURDATE() AND status NOT IN ('cancelled','no_show')");
        $pendingCount   = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_bookings WHERE status = 'pending'");
        $confirmedCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_bookings WHERE status = 'confirmed' AND booking_date >= CURDATE()");

        $this->view('bookings/index', [
            'title'       => lang('facilities.bookings'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.bookings')],
            ],
            'facilities'     => $facilities,
            'todayCount'     => $todayCount,
            'pendingCount'   => $pendingCount,
            'confirmedCount' => $confirmedCount,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $facilities = $db->fetchAll(
            "SELECT f.id, f.name_en, f.name_ar, f.facility_code, f.booking_fee_per_hour, f.booking_fee_members,
                    f.min_booking_minutes, f.max_booking_minutes, f.advance_booking_days
             FROM facilities f WHERE f.is_active = 1 AND f.is_bookable = 1 ORDER BY f.name_en"
        );

        $this->view('bookings/create', [
            'title'       => lang('facilities.create_booking'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.bookings'), 'url' => '/bookings'],
                ['label' => lang('facilities.create_booking')],
            ],
            'facilities' => $facilities,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'facility_id'  => 'required|integer',
            'member_id'    => 'required|integer',
            'booking_date' => 'required|date',
            'start_time'   => 'required|string',
            'end_time'     => 'required|string',
            'booking_type' => 'nullable|in:regular,recurring,tournament,event,training,private',
            'notes'        => 'nullable|string|max:500',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $db = $this->db();

        // Check availability via stored procedure
        $db->execute(
            "CALL sp_check_booking_availability(?, ?, ?, ?, @p_avail, @p_conflicts)",
            [$data['facility_id'], $data['booking_date'], $data['start_time'], $data['end_time']]
        );
        $availability = $db->fetchOne("SELECT @p_avail AS is_available, @p_conflicts AS conflict_count");

        if (!$availability || !$availability['is_available']) {
            $this->json(['success' => false, 'message' => lang('facilities.slot_not_available')], 422);
            return;
        }

        // Calculate duration and fee
        $startTs = strtotime($data['start_time']);
        $endTs   = strtotime($data['end_time']);
        $durationMinutes = ($endTs - $startTs) / 60;

        $facility = $db->fetchOne("SELECT booking_fee_members, min_booking_minutes, max_booking_minutes, advance_booking_days FROM facilities WHERE id = ?", [$data['facility_id']]);
        if ($facility) {
            if ($facility['min_booking_minutes'] && $durationMinutes < $facility['min_booking_minutes']) {
                $this->json(['success' => false, 'message' => "Minimum booking is {$facility['min_booking_minutes']} minutes"], 422);
                return;
            }
            if ($facility['max_booking_minutes'] && $durationMinutes > $facility['max_booking_minutes']) {
                $this->json(['success' => false, 'message' => "Maximum booking is {$facility['max_booking_minutes']} minutes"], 422);
                return;
            }
            $advDays = $facility['advance_booking_days'] ?: 30;
            $maxDate = date('Y-m-d', strtotime("+{$advDays} days"));
            if ($data['booking_date'] > $maxDate) {
                $this->json(['success' => false, 'message' => "Cannot book more than {$advDays} days in advance"], 422);
                return;
            }
        }

        $feePerHour = (float) ($facility['booking_fee_members'] ?? 0);
        $feeAmount  = round($feePerHour * ($durationMinutes / 60), 4);

        $bookingNumber = SequenceGenerator::next('booking_number');

        $insertData = [
            'booking_number'    => $bookingNumber,
            'facility_id'      => $data['facility_id'],
            'member_id'        => $data['member_id'],
            'booking_date'     => $data['booking_date'],
            'start_time'       => $data['start_time'],
            'end_time'         => $data['end_time'],
            'duration_minutes' => (int) $durationMinutes,
            'booking_type'     => $data['booking_type'] ?? 'regular',
            'status'           => 'pending',
            'fee_amount'       => $feeAmount,
            'notes'            => $data['notes'] ?? null,
            'created_at'       => date('Y-m-d H:i:s'),
            'created_by'       => Auth::id(),
        ];

        $id = $db->insert('facility_bookings', $insertData);
        $this->audit('create', 'facility_bookings', $id, "Created booking {$bookingNumber}");

        $this->json(['success' => true, 'message' => lang('facilities.booking_created') . ': ' . $bookingNumber, 'redirect' => '/bookings']);
    }

    public function calendar(Request $request): void
    {
        $facilityId = (int) $request->routeParam('id');
        $db = $this->db();

        $facility = $db->fetchOne("SELECT * FROM facilities WHERE id = ? AND is_bookable = 1", [$facilityId]);
        if (!$facility) {
            $this->abort(404, 'Bookable facility not found');
        }

        $this->view('bookings/calendar', [
            'title'       => lang('facilities.booking_calendar') . ': ' . $facility['name_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.bookings'), 'url' => '/bookings'],
                ['label' => lang('facilities.booking_calendar')],
            ],
            'facility' => $facility,
        ]);
    }

    public function updateStatus(Request $request): void
    {
        $id = (int) $request->routeParam('id');

        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $newStatus = $request->post('status');
        $reason    = $request->post('reason', '');

        $allowed = ['confirmed', 'checked_in', 'completed', 'cancelled', 'no_show'];
        if (!in_array($newStatus, $allowed)) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 422);
            return;
        }

        $db  = $this->db();
        $old = $db->fetchOne("SELECT * FROM facility_bookings WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Booking not found'], 404);
            return;
        }

        $updateData = ['status' => $newStatus, 'updated_at' => date('Y-m-d H:i:s')];

        if ($newStatus === 'confirmed') {
            $updateData['confirmed_at'] = date('Y-m-d H:i:s');
        } elseif ($newStatus === 'checked_in') {
            $updateData['checked_in_at'] = date('Y-m-d H:i:s');
            $updateData['checked_in_by'] = Auth::id();
        } elseif ($newStatus === 'completed') {
            $updateData['completed_at'] = date('Y-m-d H:i:s');
        } elseif ($newStatus === 'cancelled') {
            $updateData['cancelled_at']     = date('Y-m-d H:i:s');
            $updateData['cancelled_by']     = Auth::id();
            $updateData['cancellation_reason'] = $reason;
        }

        $db->update('facility_bookings', $updateData, 'id = ?', [$id]);
        $this->audit('update', 'facility_bookings', $id, "Booking status changed to {$newStatus}", $old, $updateData);

        $this->json(['success' => true, 'message' => lang('facilities.booking_updated')]);
    }
}
```

## File: modules\facilities\Controllers\EventController.php

```php
<?php

class EventController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $planned  = (int) $db->fetchColumn("SELECT COUNT(*) FROM club_events WHERE status IN ('planned','registration_open')");
        $ongoing  = (int) $db->fetchColumn("SELECT COUNT(*) FROM club_events WHERE status = 'ongoing'");
        $completed = (int) $db->fetchColumn("SELECT COUNT(*) FROM club_events WHERE status = 'completed'");

        $this->view('events/index', [
            'title'       => lang('facilities.events'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.events')],
            ],
            'planned'   => $planned,
            'ongoing'   => $ongoing,
            'completed' => $completed,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $facilities = $db->fetchAll("SELECT id, name_en, name_ar FROM facilities WHERE is_active = 1 ORDER BY name_en");

        $this->view('events/create', [
            'title'       => lang('facilities.create_event'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.events'), 'url' => '/events'],
                ['label' => lang('facilities.create_event')],
            ],
            'facilities' => $facilities,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'title_en'           => 'required|string|max:500',
            'title_ar'           => 'nullable|string|max:500',
            'description_en'     => 'nullable|string',
            'description_ar'     => 'nullable|string',
            'event_type'         => 'required|in:social,sport,cultural,charity,celebration,workshop,competition,other',
            'facility_id'        => 'nullable|integer',
            'location'           => 'nullable|string|max:300',
            'start_datetime'     => 'required|string',
            'end_datetime'       => 'nullable|string',
            'registration_start' => 'nullable|string',
            'registration_end'   => 'nullable|string',
            'max_attendees'      => 'nullable|integer',
            'member_fee'         => 'nullable|decimal',
            'guest_fee'          => 'nullable|decimal',
            'allow_guests'       => 'nullable|boolean',
            'max_guests_per_member' => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['event_code']    = SequenceGenerator::next('event_code');
        $data['allow_guests']  = isset($data['allow_guests']) ? 1 : 0;
        $data['fee']           = $data['member_fee'] ?? 0;
        $data['status']        = 'planned';
        $data['created_at']    = date('Y-m-d H:i:s');
        $data['created_by']    = Auth::id();

        $db = $this->db();
        $id = $db->insert('club_events', $data);
        $this->audit('create', 'club_events', $id, "Created event: {$data['title_en']}");

        $this->json(['success' => true, 'message' => lang('facilities.event_created'), 'redirect' => "/events/{$id}"]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $event = $db->fetchOne(
            "SELECT e.*, f.name_en AS facility_name_en, f.name_ar AS facility_name_ar
             FROM club_events e
             LEFT JOIN facilities f ON e.facility_id = f.id
             WHERE e.id = ?",
            [$id]
        );
        if (!$event) {
            $this->abort(404, 'Event not found');
        }

        $registrations = $db->fetchAll(
            "SELECT r.*, m.full_name_en AS member_name, m.membership_number
             FROM club_event_registrations r
             LEFT JOIN members m ON r.member_id = m.id
             WHERE r.event_id = ?
             ORDER BY r.registration_date DESC",
            [$id]
        );

        $this->view('events/show', [
            'title'       => $event['title_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.events'), 'url' => '/events'],
                ['label' => $event['title_en']],
            ],
            'event'         => $event,
            'registrations' => $registrations,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $event = $db->fetchOne("SELECT * FROM club_events WHERE id = ?", [$id]);
        if (!$event) {
            $this->abort(404);
        }
        $facilities = $db->fetchAll("SELECT id, name_en, name_ar FROM facilities WHERE is_active = 1 ORDER BY name_en");

        $this->view('events/edit', [
            'title'       => lang('common.edit') . ': ' . $event['title_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.events'), 'url' => '/events'],
                ['label' => $event['title_en'], 'url' => "/events/{$id}"],
                ['label' => lang('common.edit')],
            ],
            'event'      => $event,
            'facilities' => $facilities,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db  = $this->db();
        $old = $db->fetchOne("SELECT * FROM club_events WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Event not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'title_en'           => 'required|string|max:500',
            'title_ar'           => 'nullable|string|max:500',
            'description_en'     => 'nullable|string',
            'description_ar'     => 'nullable|string',
            'event_type'         => 'required|in:social,sport,cultural,charity,celebration,workshop,competition,other',
            'facility_id'        => 'nullable|integer',
            'location'           => 'nullable|string|max:300',
            'start_datetime'     => 'required|string',
            'end_datetime'       => 'nullable|string',
            'max_attendees'      => 'nullable|integer',
            'member_fee'         => 'nullable|decimal',
            'guest_fee'          => 'nullable|decimal',
            'allow_guests'       => 'nullable|boolean',
            'status'             => 'nullable|in:planned,registration_open,registration_closed,ongoing,completed,cancelled,postponed',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['allow_guests'] = isset($data['allow_guests']) ? 1 : 0;
        $data['fee']          = $data['member_fee'] ?? $old['fee'];
        $data['updated_at']   = date('Y-m-d H:i:s');

        $db->update('club_events', $data, 'id = ?', [$id]);
        $this->audit('update', 'club_events', $id, "Updated event: {$data['title_en']}", $old, $data);

        $this->json(['success' => true, 'message' => lang('facilities.event_updated'), 'redirect' => "/events/{$id}"]);
    }

    public function register(Request $request): void
    {
        $eventId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $memberId = (int) $request->post('member_id');
        if (!$memberId) {
            $this->json(['success' => false, 'message' => 'Member is required'], 422);
            return;
        }

        $db    = $this->db();
        $event = $db->fetchOne("SELECT * FROM club_events WHERE id = ?", [$eventId]);
        if (!$event) {
            $this->json(['success' => false, 'message' => 'Event not found'], 404);
            return;
        }

        if ($event['max_attendees'] && $event['current_attendees'] >= $event['max_attendees']) {
            $this->json(['success' => false, 'message' => lang('facilities.event_full')], 422);
            return;
        }

        $existing = $db->fetchColumn(
            "SELECT COUNT(*) FROM club_event_registrations WHERE event_id = ? AND member_id = ? AND status NOT IN ('cancelled')",
            [$eventId, $memberId]
        );
        if ($existing > 0) {
            $this->json(['success' => false, 'message' => lang('facilities.already_registered')], 422);
            return;
        }

        $totalGuests = (int) $request->post('guest_count', 0);
        $totalAttendees = 1 + $totalGuests;
        $totalFee = (float) $event['member_fee'] + ($totalGuests * (float) $event['guest_fee']);

        $regData = [
            'event_id'          => $eventId,
            'member_id'         => $memberId,
            'total_attendees'   => $totalAttendees,
            'total_fee'         => $totalFee,
            'registration_date' => date('Y-m-d H:i:s'),
            'status'            => 'registered',
            'notes'             => $request->post('notes', ''),
        ];

        $regId = $db->insert('club_event_registrations', $regData);

        $db->execute("UPDATE club_events SET current_attendees = current_attendees + ? WHERE id = ?", [$totalAttendees, $eventId]);

        $this->audit('create', 'club_event_registrations', $regId, "Registered member #{$memberId} for event #{$eventId}");

        $this->json(['success' => true, 'message' => lang('facilities.registration_success')]);
    }

    public function checkin(Request $request): void
    {
        $eventId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $registrationId = (int) $request->post('registration_id');
        $db = $this->db();

        $reg = $db->fetchOne("SELECT * FROM club_event_registrations WHERE id = ? AND event_id = ?", [$registrationId, $eventId]);
        if (!$reg) {
            $this->json(['success' => false, 'message' => 'Registration not found'], 404);
            return;
        }

        $db->update('club_event_registrations', [
            'status'      => 'attended',
            'check_in_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$registrationId]);

        $this->audit('update', 'club_event_registrations', $registrationId, "Checked in registration #{$registrationId}");
        $this->json(['success' => true, 'message' => lang('facilities.checkin_success')]);
    }
}
```

## File: modules\facilities\Controllers\FacilityApiController.php

```php
<?php

class FacilityApiController extends Controller
{
    public function list(Request $request): void
    {
        $db = $this->db();

        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status', '');
        $typeFilter   = $request->get('facility_type_id', '');

        $where  = "WHERE f.is_active = 1";
        $params = [];

        if ($statusFilter) {
            $where .= " AND f.status = ?";
            $params[] = $statusFilter;
        }
        if ($typeFilter) {
            $where .= " AND f.facility_type_id = ?";
            $params[] = $typeFilter;
        }
        if ($search) {
            $where .= " AND (f.name_en LIKE ? OR f.name_ar LIKE ? OR f.facility_code LIKE ? OR ft.name_en LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $totalAll  = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities f WHERE f.is_active = 1");
        $totalFilt = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM facilities f LEFT JOIN facility_types ft ON f.facility_type_id = ft.id {$where}",
            $params
        );

        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtoupper($request->get('order')[0]['dir'] ?? 'ASC') === 'DESC' ? 'DESC' : 'ASC';
        $colMap   = ['f.id', 'f.facility_code', 'f.name_en', 'ft.name_en', 'f.capacity', 'f.status', 'f.is_bookable'];
        $orderClause = ($colMap[$orderCol] ?? 'f.id') . ' ' . $orderDir;

        $rows = $db->fetchAll(
            "SELECT f.id, f.facility_code, f.name_en, f.name_ar, f.capacity, f.status, f.is_bookable,
                    ft.name_en AS type_name_en, ft.name_ar AS type_name_ar
             FROM facilities f
             LEFT JOIN facility_types ft ON f.facility_type_id = ft.id
             {$where}
             ORDER BY {$orderClause}
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '<a href="/facilities/' . $r['id'] . '" class="btn btn-sm btn-info" title="View"><i class="fas fa-eye"></i></a> ';
            if (Gate::can('facilities.manage')) {
                $actions .= '<a href="/facilities/' . $r['id'] . '/edit" class="btn btn-sm btn-warning" title="Edit"><i class="fas fa-edit"></i></a> ';
            }
            if ($r['is_bookable'] && Gate::can('bookings.view')) {
                $actions .= '<a href="/bookings/calendar/' . $r['id'] . '" class="btn btn-sm btn-primary" title="Calendar"><i class="fas fa-calendar"></i></a>';
            }
            $data[] = [
                'id'        => $r['id'],
                'code'      => e($r['facility_code']),
                'name'      => e($r['name_en']),
                'name_ar'   => e($r['name_ar']),
                'type'      => e($r['type_name_en'] ?? ''),
                'capacity'  => $r['capacity'] ? number_format($r['capacity']) : '-',
                'bookable'  => $r['is_bookable'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-muted"></i>',
                'status'    => status_badge($r['status']),
                'actions'   => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalFilt,
            'data'            => $data,
        ]);
    }

    public function typesList(Request $request): void
    {
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where  = "WHERE 1=1";
        $params = [];
        if ($search) {
            $where .= " AND (name_en LIKE ? OR name_ar LIKE ? OR type_code LIKE ?)";
            $s = "%{$search}%";
            $params = [$s, $s, $s];
        }

        $total     = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_types");
        $totalFilt = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_types {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT * FROM facility_types {$where} ORDER BY name_en LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $facilityCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE facility_type_id = ?", [$r['id']]);
            $actions = '<a href="/facilities/types/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>';
            $data[] = [
                'id'         => $r['id'],
                'code'       => e($r['type_code']),
                'name_en'    => e($r['name_en']),
                'name_ar'    => e($r['name_ar']),
                'bookable'   => $r['is_bookable'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-muted"></i>',
                'active'     => $r['is_active'] ? status_badge('active') : status_badge('expired'),
                'facilities' => $facilityCount,
                'actions'    => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $totalFilt, 'data' => $data]);
    }

    public function bookingsList(Request $request): void
    {
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $statusFilter   = $request->get('status', '');
        $facilityFilter = $request->get('facility_id', '');
        $dateFrom       = $request->get('date_from', '');
        $dateTo         = $request->get('date_to', '');

        $where  = "WHERE 1=1";
        $params = [];

        if ($statusFilter) {
            $where .= " AND fb.status = ?";
            $params[] = $statusFilter;
        }
        if ($facilityFilter) {
            $where .= " AND fb.facility_id = ?";
            $params[] = $facilityFilter;
        }
        if ($dateFrom) {
            $where .= " AND fb.booking_date >= ?";
            $params[] = $dateFrom;
        }
        if ($dateTo) {
            $where .= " AND fb.booking_date <= ?";
            $params[] = $dateTo;
        }
        if ($search) {
            $where .= " AND (fb.booking_number LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ? OR f.name_en LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $baseJoin = "FROM facility_bookings fb
                     LEFT JOIN members m ON fb.member_id = m.id
                     LEFT JOIN facilities f ON fb.facility_id = f.id";

        $total     = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_bookings");
        $totalFilt = (int) $db->fetchColumn("SELECT COUNT(*) {$baseJoin} {$where}", $params);

        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtoupper($request->get('order')[0]['dir'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
        $colMap = ['fb.id', 'fb.booking_number', 'f.name_en', 'm.full_name_en', 'fb.booking_date', 'fb.start_time', 'fb.fee_amount', 'fb.status'];
        $orderClause = ($colMap[$orderCol] ?? 'fb.id') . ' ' . $orderDir;

        $rows = $db->fetchAll(
            "SELECT fb.*, m.full_name_en AS member_name, m.membership_number, f.name_en AS facility_name
             {$baseJoin} {$where}
             ORDER BY {$orderClause}
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '';
            if (Gate::can('bookings.manage')) {
                if ($r['status'] === 'pending') {
                    $actions .= '<button class="btn btn-sm btn-success btn-status" data-id="' . $r['id'] . '" data-status="confirmed" title="Confirm"><i class="fas fa-check"></i></button> ';
                }
                if (in_array($r['status'], ['confirmed', 'pending'])) {
                    $actions .= '<button class="btn btn-sm btn-info btn-status" data-id="' . $r['id'] . '" data-status="checked_in" title="Check In"><i class="fas fa-sign-in-alt"></i></button> ';
                    $actions .= '<button class="btn btn-sm btn-danger btn-status" data-id="' . $r['id'] . '" data-status="cancelled" title="Cancel"><i class="fas fa-times"></i></button> ';
                }
                if ($r['status'] === 'checked_in') {
                    $actions .= '<button class="btn btn-sm btn-secondary btn-status" data-id="' . $r['id'] . '" data-status="completed" title="Complete"><i class="fas fa-flag-checkered"></i></button>';
                }
            }

            $data[] = [
                'id'       => $r['id'],
                'number'   => e($r['booking_number']),
                'facility' => e($r['facility_name'] ?? ''),
                'member'   => e($r['member_name'] ?? '') . '<br><small class="text-muted">' . e($r['membership_number'] ?? '') . '</small>',
                'date'     => e($r['booking_date']),
                'time'     => e($r['start_time']) . ' - ' . e($r['end_time']),
                'fee'      => format_money($r['fee_amount']),
                'status'   => status_badge($r['status']),
                'actions'  => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $totalFilt, 'data' => $data]);
    }

    public function calendarData(Request $request): void
    {
        $facilityId = (int) $request->routeParam('id');
        $startDate  = $request->get('start', date('Y-m-d'));
        $endDate    = $request->get('end', date('Y-m-d', strtotime('+7 days')));

        $db = $this->db();
        $bookings = $db->fetchAll(
            "SELECT fb.id, fb.booking_number, fb.booking_date, fb.start_time, fb.end_time, fb.status, fb.booking_type,
                    m.full_name_en AS member_name, m.membership_number
             FROM facility_bookings fb
             LEFT JOIN members m ON fb.member_id = m.id
             WHERE fb.facility_id = ? AND fb.booking_date BETWEEN ? AND ? AND fb.status NOT IN ('cancelled')
             ORDER BY fb.booking_date, fb.start_time",
            [$facilityId, $startDate, $endDate]
        );

        $overrides = $db->fetchAll(
            "SELECT * FROM facility_slot_overrides WHERE facility_id = ? AND override_date BETWEEN ? AND ?",
            [$facilityId, $startDate, $endDate]
        );

        $events = [];
        foreach ($bookings as $b) {
            $colorMap = [
                'pending'    => '#ffc107',
                'confirmed'  => '#28a745',
                'checked_in' => '#17a2b8',
                'completed'  => '#6c757d',
            ];
            $events[] = [
                'id'              => $b['id'],
                'title'           => $b['member_name'] . ' (' . $b['booking_number'] . ')',
                'start'           => $b['booking_date'] . 'T' . $b['start_time'],
                'end'             => $b['booking_date'] . 'T' . $b['end_time'],
                'backgroundColor' => $colorMap[$b['status']] ?? '#007bff',
                'borderColor'     => $colorMap[$b['status']] ?? '#007bff',
                'extendedProps'   => [
                    'booking_number' => $b['booking_number'],
                    'member'         => $b['member_name'],
                    'membership_no'  => $b['membership_number'],
                    'status'         => $b['status'],
                    'type'           => $b['booking_type'],
                ],
            ];
        }

        foreach ($overrides as $ov) {
            $events[] = [
                'id'              => 'ov-' . $ov['id'],
                'title'           => 'â›” ' . ucfirst($ov['override_type']) . ($ov['reason'] ? ': ' . $ov['reason'] : ''),
                'start'           => $ov['override_date'] . ($ov['start_time'] ? 'T' . $ov['start_time'] : ''),
                'end'             => $ov['override_date'] . ($ov['end_time'] ? 'T' . $ov['end_time'] : ''),
                'backgroundColor' => '#dc3545',
                'borderColor'     => '#dc3545',
                'display'         => $ov['start_time'] ? 'auto' : 'background',
            ];
        }

        $this->json($events);
    }

    public function checkAvailability(Request $request): void
    {
        $facilityId = (int) $request->get('facility_id');
        $date       = $request->get('date');
        $startTime  = $request->get('start_time');
        $endTime    = $request->get('end_time');

        if (!$facilityId || !$date || !$startTime || !$endTime) {
            $this->json(['available' => false, 'message' => 'Missing parameters']);
            return;
        }

        $db = $this->db();
        $db->execute(
            "CALL sp_check_booking_availability(?, ?, ?, ?, @p_avail, @p_conflicts)",
            [$facilityId, $date, $startTime, $endTime]
        );
        $result = $db->fetchOne("SELECT @p_avail AS is_available, @p_conflicts AS conflict_count");

        $this->json([
            'available'     => (bool) ($result['is_available'] ?? 0),
            'conflict_count'=> (int) ($result['conflict_count'] ?? 0),
        ]);
    }

    public function eventsList(Request $request): void
    {
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $statusFilter = $request->get('status', '');

        $where  = "WHERE 1=1";
        $params = [];
        if ($statusFilter) {
            $where .= " AND e.status = ?";
            $params[] = $statusFilter;
        }
        if ($search) {
            $where .= " AND (e.title_en LIKE ? OR e.title_ar LIKE ? OR e.event_code LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s]);
        }

        $total     = (int) $db->fetchColumn("SELECT COUNT(*) FROM club_events");
        $totalFilt = (int) $db->fetchColumn("SELECT COUNT(*) FROM club_events e LEFT JOIN facilities f ON e.facility_id = f.id {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT e.*, f.name_en AS facility_name
             FROM club_events e LEFT JOIN facilities f ON e.facility_id = f.id
             {$where} ORDER BY e.start_datetime DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '<a href="/events/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a> ';
            if (Gate::can('events.manage')) {
                $actions .= '<a href="/events/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>';
            }
            $data[] = [
                'id'        => $r['id'],
                'code'      => e($r['event_code']),
                'title'     => e($r['title_en']),
                'type'      => '<span class="badge bg-secondary">' . e(ucfirst($r['event_type'])) . '</span>',
                'venue'     => e($r['facility_name'] ?? $r['location'] ?? '-'),
                'date'      => format_datetime($r['start_datetime']),
                'attendees' => $r['current_attendees'] . ($r['max_attendees'] ? '/' . $r['max_attendees'] : ''),
                'status'    => status_badge($r['status']),
                'actions'   => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $totalFilt, 'data' => $data]);
    }

    public function eventRegistrations(Request $request): void
    {
        $eventId = (int) $request->routeParam('id');
        $db = $this->db();

        $rows = $db->fetchAll(
            "SELECT r.*, m.full_name_en AS member_name, m.membership_number, m.primary_phone
             FROM club_event_registrations r
             LEFT JOIN members m ON r.member_id = m.id
             WHERE r.event_id = ?
             ORDER BY r.registration_date DESC",
            [$eventId]
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'            => $r['id'],
                'member'        => e($r['member_name'] ?? ''),
                'membership_no' => e($r['membership_number'] ?? ''),
                'phone'         => e($r['primary_phone'] ?? ''),
                'attendees'     => $r['total_attendees'],
                'fee'           => format_money($r['total_fee']),
                'date'          => format_datetime($r['registration_date']),
                'status'        => status_badge($r['status']),
            ];
        }

        $this->json(['draw' => 1, 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function maintenanceList(Request $request): void
    {
        $facilityId = (int) $request->routeParam('id');
        $db = $this->db();

        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_maintenance WHERE facility_id = ?", [$facilityId]);
        $rows = $db->fetchAll(
            "SELECT * FROM facility_maintenance WHERE facility_id = ? ORDER BY start_date DESC LIMIT " . (int)$length . " OFFSET " . (int)$start,
            [$facilityId]
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'        => $r['id'],
                'type'      => '<span class="badge bg-secondary">' . e(ucfirst($r['maintenance_type'])) . '</span>',
                'title'     => e($r['title']),
                'start'     => e($r['start_date']),
                'end'       => e($r['end_date'] ?? '-'),
                'cost'      => $r['estimated_cost'] ? format_money($r['estimated_cost']) : '-',
                'priority'  => status_badge($r['priority']),
                'status'    => status_badge($r['status']),
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function poolReadings(Request $request): void
    {
        $poolId = (int) $request->routeParam('id');
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM swimming_pool_readings WHERE pool_id = ?", [$poolId]);
        $rows = $db->fetchAll(
            "SELECT * FROM swimming_pool_readings WHERE pool_id = ? ORDER BY reading_date DESC, reading_time DESC LIMIT " . (int)$length . " OFFSET " . (int)$start,
            [$poolId]
        );

        $data = [];
        foreach ($rows as $r) {
            $compliance = $r['is_compliant']
                ? '<span class="badge bg-success">Compliant</span>'
                : '<span class="badge bg-danger">Non-Compliant</span>';
            $data[] = [
                'id'          => $r['id'],
                'date'        => e($r['reading_date']),
                'time'        => e($r['reading_time']),
                'ph'          => $r['ph_level'] ?? '-',
                'chlorine'    => $r['chlorine_level'] ?? '-',
                'temperature' => $r['temperature_celsius'] ? $r['temperature_celsius'] . 'Â°C' : '-',
                'compliant'   => $compliance,
                'reason'      => e($r['non_compliance_reason'] ?? ''),
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function permitsList(Request $request): void
    {
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where  = "WHERE 1=1";
        $params = [];
        if ($search) {
            $where .= " AND (pp.permit_number LIKE ? OR pp.vehicle_plate LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = [$s, $s, $s, $s];
        }

        $total     = (int) $db->fetchColumn("SELECT COUNT(*) FROM parking_permits");
        $totalFilt = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM parking_permits pp LEFT JOIN members m ON pp.member_id = m.id LEFT JOIN parking_zones pz ON pp.zone_id = pz.id {$where}",
            $params
        );

        $rows = $db->fetchAll(
            "SELECT pp.*, m.full_name_en AS member_name, m.membership_number, pz.name_en AS zone_name
             FROM parking_permits pp
             LEFT JOIN members m ON pp.member_id = m.id
             LEFT JOIN parking_zones pz ON pp.zone_id = pz.id
             {$where} ORDER BY pp.created_at DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'       => $r['id'],
                'number'   => e($r['permit_number']),
                'member'   => e($r['member_name'] ?? ''),
                'plate'    => e($r['vehicle_plate']),
                'vehicle'  => e(trim(($r['vehicle_make'] ?? '') . ' ' . ($r['vehicle_model'] ?? ''))),
                'zone'     => e($r['zone_name'] ?? '-'),
                'type'     => '<span class="badge bg-secondary">' . e(ucfirst($r['permit_type'])) . '</span>',
                'dates'    => e($r['start_date']) . ' â†’ ' . e($r['end_date'] ?? 'âˆž'),
                'status'   => status_badge($r['status']),
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $totalFilt, 'data' => $data]);
    }

    public function parkingLog(Request $request): void
    {
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);

        $dateFilter = $request->get('date', date('Y-m-d'));

        $total = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM parking_log WHERE DATE(timestamp) = ?",
            [$dateFilter]
        );

        $rows = $db->fetchAll(
            "SELECT pl.*, m.full_name_en AS member_name, pz.name_en AS zone_name
             FROM parking_log pl
             LEFT JOIN members m ON pl.member_id = m.id
             LEFT JOIN parking_zones pz ON pl.zone_id = pz.id
             WHERE DATE(pl.timestamp) = ?
             ORDER BY pl.timestamp DESC
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            [$dateFilter]
        );

        $data = [];
        foreach ($rows as $r) {
            $dirIcon = $r['direction'] === 'entry'
                ? '<span class="text-success"><i class="fas fa-arrow-right"></i> Entry</span>'
                : '<span class="text-danger"><i class="fas fa-arrow-left"></i> Exit</span>';
            $data[] = [
                'id'        => $r['id'],
                'time'      => format_datetime($r['timestamp']),
                'direction' => $dirIcon,
                'plate'     => e($r['vehicle_plate']),
                'member'    => e($r['member_name'] ?? '-'),
                'zone'      => e($r['zone_name'] ?? '-'),
                'authorized'=> $r['is_authorized'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-danger"></i>',
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }
}
```

## File: modules\facilities\Controllers\FacilityController.php

```php
<?php

class FacilityController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();

        $totalActive = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE status = 'active' AND is_active = 1");
        $totalMaintenance = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE status = 'under_maintenance'");
        $totalClosed = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE status = 'closed'");
        $totalAll = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE is_active = 1");

        $facilityTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM facility_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('index', [
            'title'            => lang('facilities.title'),
            'breadcrumbs'      => [['label' => lang('facilities.title')]],
            'totalActive'      => $totalActive,
            'totalMaintenance' => $totalMaintenance,
            'totalClosed'      => $totalClosed,
            'totalAll'         => $totalAll,
            'facilityTypes'    => $facilityTypes,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $facility = $db->fetchOne(
            "SELECT f.*, ft.name_en AS type_name_en, ft.name_ar AS type_name_ar
             FROM facilities f
             LEFT JOIN facility_types ft ON f.facility_type_id = ft.id
             WHERE f.id = ?",
            [$id]
        );

        if (!$facility) {
            $this->abort(404, 'Facility not found');
        }

        $amenities = $db->fetchAll("SELECT * FROM facility_amenities WHERE facility_id = ? ORDER BY amenity_name_en", [$id]);
        $timeSlots = $db->fetchAll("SELECT * FROM facility_time_slots WHERE facility_id = ? AND is_active = 1 ORDER BY day_of_week, start_time", [$id]);
        $recentBookings = $db->fetchAll(
            "SELECT fb.*, m.full_name_en AS member_name, m.membership_number
             FROM facility_bookings fb
             LEFT JOIN members m ON fb.member_id = m.id
             WHERE fb.facility_id = ? AND fb.booking_date >= CURDATE()
             ORDER BY fb.booking_date, fb.start_time LIMIT 20",
            [$id]
        );
        $maintenanceRecords = $db->fetchAll(
            "SELECT * FROM facility_maintenance WHERE facility_id = ? ORDER BY start_date DESC LIMIT 10",
            [$id]
        );

        $this->view('show', [
            'title'       => $facility['name_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => $facility['name_en']],
            ],
            'facility'           => $facility,
            'amenities'          => $amenities,
            'timeSlots'          => $timeSlots,
            'recentBookings'     => $recentBookings,
            'maintenanceRecords' => $maintenanceRecords,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $facilityTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM facility_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('create', [
            'title'       => lang('facilities.create'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.create')],
            ],
            'facilityTypes' => $facilityTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'facility_code'    => 'required|string|max:20',
            'facility_type_id' => 'required|integer',
            'name_en'          => 'required|string|max:200',
            'name_ar'          => 'required|string|max:200',
            'location'         => 'nullable|string|max:300',
            'capacity'         => 'nullable|integer',
            'area_sqm'         => 'nullable|decimal',
            'is_indoor'        => 'nullable|boolean',
            'is_bookable'      => 'nullable|boolean',
            'booking_fee_per_hour' => 'nullable|decimal',
            'booking_fee_members'  => 'nullable|decimal',
            'booking_fee_guests'   => 'nullable|decimal',
            'min_booking_minutes'  => 'nullable|integer',
            'max_booking_minutes'  => 'nullable|integer',
            'advance_booking_days' => 'nullable|integer',
            'operating_hours_start'=> 'nullable|string',
            'operating_hours_end'  => 'nullable|string',
            'status'               => 'nullable|in:active,under_maintenance,closed,reserved',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_indoor']   = isset($data['is_indoor']) ? 1 : 0;
        $data['is_bookable'] = isset($data['is_bookable']) ? 1 : 0;
        $data['is_active']   = 1;
        $data['created_at']  = date('Y-m-d H:i:s');

        $db = $this->db();

        $existingCode = $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE facility_code = ?", [$data['facility_code']]);
        if ($existingCode > 0) {
            $this->json(['success' => false, 'errors' => ['facility_code' => ['Facility code already exists']]], 422);
            return;
        }

        $id = $db->insert('facilities', $data);
        $this->audit('create', 'facilities', $id, "Created facility: {$data['name_en']}");

        $this->json(['success' => true, 'message' => lang('facilities.created'), 'redirect' => "/facilities/{$id}"]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $facility = $db->fetchOne("SELECT * FROM facilities WHERE id = ?", [$id]);
        if (!$facility) {
            $this->abort(404, 'Facility not found');
        }

        $facilityTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM facility_types WHERE is_active = 1 ORDER BY name_en");

        $this->view('edit', [
            'title'       => lang('facilities.edit') . ': ' . $facility['name_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => $facility['name_en'], 'url' => "/facilities/{$id}"],
                ['label' => lang('common.edit')],
            ],
            'facility'      => $facility,
            'facilityTypes' => $facilityTypes,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');

        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM facilities WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Facility not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'facility_type_id'     => 'required|integer',
            'name_en'              => 'required|string|max:200',
            'name_ar'              => 'required|string|max:200',
            'location'             => 'nullable|string|max:300',
            'capacity'             => 'nullable|integer',
            'area_sqm'             => 'nullable|decimal',
            'is_indoor'            => 'nullable|boolean',
            'is_bookable'          => 'nullable|boolean',
            'booking_fee_per_hour' => 'nullable|decimal',
            'booking_fee_members'  => 'nullable|decimal',
            'booking_fee_guests'   => 'nullable|decimal',
            'min_booking_minutes'  => 'nullable|integer',
            'max_booking_minutes'  => 'nullable|integer',
            'advance_booking_days' => 'nullable|integer',
            'operating_hours_start'=> 'nullable|string',
            'operating_hours_end'  => 'nullable|string',
            'status'               => 'nullable|in:active,under_maintenance,closed,reserved',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_indoor']   = isset($data['is_indoor']) ? 1 : 0;
        $data['is_bookable'] = isset($data['is_bookable']) ? 1 : 0;
        $data['updated_at']  = date('Y-m-d H:i:s');

        $db->update('facilities', $data, 'id = ?', [$id]);
        $this->audit('update', 'facilities', $id, "Updated facility: {$data['name_en']}", $old, $data);

        $this->json(['success' => true, 'message' => lang('facilities.updated'), 'redirect' => "/facilities/{$id}"]);
    }

    // â”€â”€â”€ Facility Types â”€â”€â”€

    public function types(Request $request): void
    {
        $this->view('types/index', [
            'title'       => lang('facilities.types'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.types')],
            ],
        ]);
    }

    public function createType(Request $request): void
    {
        $this->view('types/form', [
            'title'       => lang('facilities.create_type'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.types'), 'url' => '/facilities/types'],
                ['label' => lang('facilities.create_type')],
            ],
            'type' => null,
        ]);
    }

    public function storeType(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'type_code'  => 'required|string|max:20',
            'name_en'    => 'required|string|max:200',
            'name_ar'    => 'required|string|max:200',
            'icon'       => 'nullable|string|max:100',
            'is_bookable'=> 'nullable|boolean',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_bookable'] = isset($data['is_bookable']) ? 1 : 0;
        $data['is_active']   = 1;
        $data['created_at']  = date('Y-m-d H:i:s');

        $db = $this->db();
        $exists = $db->fetchColumn("SELECT COUNT(*) FROM facility_types WHERE type_code = ?", [$data['type_code']]);
        if ($exists > 0) {
            $this->json(['success' => false, 'errors' => ['type_code' => ['Type code already exists']]], 422);
            return;
        }

        $id = $db->insert('facility_types', $data);
        $this->audit('create', 'facility_types', $id, "Created facility type: {$data['name_en']}");

        $this->json(['success' => true, 'message' => lang('facilities.type_created'), 'redirect' => '/facilities/types']);
    }

    public function editType(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $type = $db->fetchOne("SELECT * FROM facility_types WHERE id = ?", [$id]);
        if (!$type) {
            $this->abort(404);
        }

        $this->view('types/form', [
            'title'       => lang('facilities.edit_type') . ': ' . $type['name_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.types'), 'url' => '/facilities/types'],
                ['label' => lang('common.edit')],
            ],
            'type' => $type,
        ]);
    }

    public function updateType(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $db = $this->db();
        $old = $db->fetchOne("SELECT * FROM facility_types WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Type not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'name_en'     => 'required|string|max:200',
            'name_ar'     => 'required|string|max:200',
            'icon'        => 'nullable|string|max:100',
            'is_bookable' => 'nullable|boolean',
            'is_active'   => 'nullable|boolean',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_bookable'] = isset($data['is_bookable']) ? 1 : 0;
        $data['is_active']   = isset($data['is_active']) ? 1 : 0;

        $db->update('facility_types', $data, 'id = ?', [$id]);
        $this->audit('update', 'facility_types', $id, "Updated facility type: {$data['name_en']}", $old, $data);

        $this->json(['success' => true, 'message' => lang('facilities.type_updated'), 'redirect' => '/facilities/types']);
    }

    // â”€â”€â”€ Maintenance â”€â”€â”€

    public function maintenance(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $facility = $db->fetchOne("SELECT id, name_en, name_ar, facility_code FROM facilities WHERE id = ?", [$id]);
        if (!$facility) {
            $this->abort(404);
        }

        $this->view('maintenance/index', [
            'title'       => lang('facilities.maintenance') . ': ' . $facility['name_en'],
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => $facility['name_en'], 'url' => "/facilities/{$id}"],
                ['label' => lang('facilities.maintenance')],
            ],
            'facility' => $facility,
        ]);
    }

    public function storeMaintenance(Request $request): void
    {
        $facilityId = (int) $request->routeParam('id');

        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'maintenance_type' => 'required|in:scheduled,emergency,inspection,upgrade,repair',
            'title'            => 'required|string|max:300',
            'description'      => 'nullable|string',
            'start_date'       => 'required|date',
            'end_date'         => 'nullable|date',
            'estimated_cost'   => 'nullable|decimal',
            'priority'         => 'required|in:low,medium,high,critical',
            'assigned_to'      => 'nullable|string|max:200',
            'vendor'           => 'nullable|string|max:200',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['facility_id'] = $facilityId;
        $data['status']      = 'planned';
        $data['created_at']  = date('Y-m-d H:i:s');
        $data['created_by']  = Auth::id();

        $db = $this->db();
        $id = $db->insert('facility_maintenance', $data);
        $this->audit('create', 'facility_maintenance', $id, "Created maintenance record for facility #{$facilityId}");

        $this->json(['success' => true, 'message' => lang('facilities.maintenance_created')]);
    }

    // â”€â”€â”€ Swimming Pools â”€â”€â”€

    public function pools(Request $request): void
    {
        $db = $this->db();
        $pools = $db->fetchAll(
            "SELECT sp.*, f.name_en AS facility_name_en, f.name_ar AS facility_name_ar, f.facility_code
             FROM swimming_pools sp
             JOIN facilities f ON sp.facility_id = f.id
             WHERE sp.is_active = 1
             ORDER BY f.name_en"
        );

        $this->view('pools/index', [
            'title'       => lang('facilities.pools'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.pools')],
            ],
            'pools' => $pools,
        ]);
    }

    public function storeReading(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'pool_id'           => 'required|integer',
            'reading_date'      => 'required|date',
            'reading_time'      => 'required|string',
            'ph_level'          => 'nullable|decimal',
            'chlorine_level'    => 'nullable|decimal',
            'temperature_celsius'=> 'nullable|decimal',
            'turbidity'         => 'nullable|decimal',
            'action_taken'      => 'nullable|string|max:500',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();

        $isCompliant = 1;
        $reasons = [];
        if ($data['ph_level'] !== null && ($data['ph_level'] < 7.2 || $data['ph_level'] > 7.8)) {
            $isCompliant = 0;
            $reasons[] = 'pH level out of range (7.2-7.8)';
        }
        if ($data['chlorine_level'] !== null && ($data['chlorine_level'] < 1.0 || $data['chlorine_level'] > 3.0)) {
            $isCompliant = 0;
            $reasons[] = 'Chlorine level out of range (1.0-3.0)';
        }
        if ($data['temperature_celsius'] !== null && ($data['temperature_celsius'] < 24 || $data['temperature_celsius'] > 30)) {
            $isCompliant = 0;
            $reasons[] = 'Temperature out of range (24-30Â°C)';
        }

        $data['is_compliant']         = $isCompliant;
        $data['non_compliance_reason'] = $isCompliant ? null : implode('; ', $reasons);
        $data['recorded_by']          = Auth::id();
        $data['created_at']           = date('Y-m-d H:i:s');

        $db = $this->db();
        $id = $db->insert('swimming_pool_readings', $data);
        $this->audit('create', 'swimming_pool_readings', $id, "Recorded pool reading for pool #{$data['pool_id']}");

        $this->json(['success' => true, 'message' => lang('facilities.reading_recorded')]);
    }

    // â”€â”€â”€ Parking â”€â”€â”€

    public function parking(Request $request): void
    {
        $db = $this->db();
        $zones = $db->fetchAll("SELECT * FROM parking_zones WHERE is_active = 1 ORDER BY zone_code");
        $membershipTypes = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('parking/index', [
            'title'       => lang('facilities.parking'),
            'breadcrumbs' => [
                ['label' => lang('facilities.title'), 'url' => '/facilities'],
                ['label' => lang('facilities.parking')],
            ],
            'zones'           => $zones,
            'membershipTypes' => $membershipTypes,
        ]);
    }

    public function storePermit(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'member_id'      => 'required|integer',
            'zone_id'        => 'nullable|integer',
            'vehicle_plate'  => 'required|string|max:30',
            'vehicle_make'   => 'nullable|string|max:100',
            'vehicle_model'  => 'nullable|string|max:100',
            'vehicle_color'  => 'nullable|string|max:50',
            'permit_type'    => 'required|in:annual,seasonal,monthly,daily,vip,disabled',
            'start_date'     => 'required|date',
            'end_date'       => 'nullable|date',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['permit_number'] = SequenceGenerator::next('parking_permit_number');
        $data['status']        = 'active';
        $data['created_at']    = date('Y-m-d H:i:s');

        $db = $this->db();
        $id = $db->insert('parking_permits', $data);
        $this->audit('create', 'parking_permits', $id, "Issued parking permit: {$data['permit_number']}");

        $this->json(['success' => true, 'message' => lang('facilities.permit_issued') . ': ' . $data['permit_number']]);
    }
}
```

## File: modules\facilities\css\facilities.css

```css
/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   Facilities Module Styles
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

.fc .fc-toolbar-title { font-size: 1.1rem; }
.fc .fc-button { font-size: 0.8rem; padding: 4px 10px; }
.fc .fc-timegrid-slot { height: 2.5em; }
.fc .fc-event { border-radius: 4px; font-size: 0.75rem; padding: 2px 4px; cursor: pointer; }
.fc .fc-bg-event { opacity: 0.15; }

.pool-card .reading-value { font-size: 1.5rem; font-weight: 600; }
.pool-card .reading-label { font-size: 0.75rem; color: #6c757d; }

.parking-spot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); gap: 8px; }
.parking-spot { width: 60px; height: 40px; border: 2px solid #dee2e6; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; font-weight: 600; }
.parking-spot.occupied { background: #ffeeba; border-color: #ffc107; }
.parking-spot.available { background: #d4edda; border-color: #28a745; }
.parking-spot.vip { background: #cce5ff; border-color: #007bff; }

.event-timeline { position: relative; padding-left: 30px; }
.event-timeline::before { content: ''; position: absolute; left: 10px; top: 0; bottom: 0; width: 2px; background: #dee2e6; }
.event-timeline-item { position: relative; margin-bottom: 20px; }
.event-timeline-item::before { content: ''; position: absolute; left: -24px; top: 4px; width: 12px; height: 12px; border-radius: 50%; background: #007bff; border: 2px solid #fff; }

.maintenance-priority-critical { border-left: 4px solid #dc3545 !important; }
.maintenance-priority-high { border-left: 4px solid #fd7e14 !important; }
.maintenance-priority-medium { border-left: 4px solid #ffc107 !important; }
.maintenance-priority-low { border-left: 4px solid #28a745 !important; }
```

## File: modules\facilities\js\facilities.js

```js
/**
 * Facilities Module JavaScript
 */
(function() {
    'use strict';

    // Auto-init Select2 on tab show for modals
    document.addEventListener('shown.bs.tab', function() {
        if ($.fn.select2) {
            $('.select2:not(.select2-hidden-accessible)').select2({
                theme: 'bootstrap-5',
                width: '100%'
            });
        }
    });

    // Auto-reload DataTables after successful AJAX form submission in modals
    document.addEventListener('submit', function(e) {
        var form = e.target;
        if (form.closest('.modal') && form.getAttribute('data-ajax') === 'true') {
            var modal = form.closest('.modal');
            // Listen for the success response to reload tables
            var origSuccess = null;
            setTimeout(function() {
                // After form submit processes, check for success and reload
                var tables = document.querySelectorAll('.tab-pane.active table[id]');
                tables.forEach(function(tbl) {
                    if (DataTableHelper.instances && DataTableHelper.instances[tbl.id]) {
                        setTimeout(function() {
                            DataTableHelper.reload(tbl.id);
                        }, 1500);
                    }
                });
            }, 500);
        }
    });

})();
```

## File: modules\facilities\lang\ar.php

```php
<?php
return [
    'facilities.title'              => 'Ø§Ù„Ù…Ø±Ø§ÙÙ‚',
    'facilities.create'             => 'Ø¥Ù†Ø´Ø§Ø¡ Ù…Ø±ÙÙ‚',
    'facilities.edit'               => 'ØªØ¹Ø¯ÙŠÙ„ Ù…Ø±ÙÙ‚',
    'facilities.created'            => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ù…Ø±ÙÙ‚ Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.updated'            => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ù…Ø±ÙÙ‚ Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.types'              => 'Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ù…Ø±Ø§ÙÙ‚',
    'facilities.create_type'        => 'Ø¥Ù†Ø´Ø§Ø¡ Ù†ÙˆØ¹ Ù…Ø±ÙÙ‚',
    'facilities.edit_type'          => 'ØªØ¹Ø¯ÙŠÙ„ Ù†ÙˆØ¹ Ø§Ù„Ù…Ø±ÙÙ‚',
    'facilities.type_created'       => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ù†ÙˆØ¹ Ø§Ù„Ù…Ø±ÙÙ‚ Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.type_updated'       => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ù†ÙˆØ¹ Ø§Ù„Ù…Ø±ÙÙ‚ Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.code'               => 'Ø±Ù…Ø² Ø§Ù„Ù…Ø±ÙÙ‚',
    'facilities.name'               => 'Ø§Ù„Ø§Ø³Ù…',
    'facilities.type'               => 'Ø§Ù„Ù†ÙˆØ¹',
    'facilities.capacity'           => 'Ø§Ù„Ø³Ø¹Ø©',
    'facilities.area_sqm'           => 'Ø§Ù„Ù…Ø³Ø§Ø­Ø© (Ù…Â²)',
    'facilities.location'           => 'Ø§Ù„Ù…ÙˆÙ‚Ø¹',
    'facilities.indoor'             => 'Ø¯Ø§Ø®Ù„ÙŠ',
    'facilities.bookable'           => 'Ù‚Ø§Ø¨Ù„ Ù„Ù„Ø­Ø¬Ø²',
    'facilities.active'             => 'Ù†Ø´Ø·',
    'facilities.closed'             => 'Ù…ØºÙ„Ù‚',
    'facilities.reserved'           => 'Ù…Ø­Ø¬ÙˆØ²',
    'facilities.under_maintenance'  => 'ØªØ­Øª Ø§Ù„ØµÙŠØ§Ù†Ø©',
    'facilities.total'              => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ù…Ø±Ø§ÙÙ‚',
    'facilities.booking_settings'   => 'Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Ø§Ù„Ø­Ø¬Ø²',
    'facilities.fee_per_hour'       => 'Ø§Ù„Ø±Ø³ÙˆÙ… / Ø³Ø§Ø¹Ø©',
    'facilities.fee_members'        => 'Ø±Ø³ÙˆÙ… Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ / Ø³Ø§Ø¹Ø©',
    'facilities.fee_guests'         => 'Ø±Ø³ÙˆÙ… Ø§Ù„Ø¶ÙŠÙˆÙ / Ø³Ø§Ø¹Ø©',
    'facilities.advance_days'       => 'Ø£ÙŠØ§Ù… Ø§Ù„Ø­Ø¬Ø² Ø§Ù„Ù…Ø³Ø¨Ù‚',
    'facilities.min_minutes'        => 'Ø§Ù„Ø­Ø¯ Ø§Ù„Ø£Ø¯Ù†Ù‰ (Ø¯Ù‚ÙŠÙ‚Ø©)',
    'facilities.max_minutes'        => 'Ø§Ù„Ø­Ø¯ Ø§Ù„Ø£Ù‚ØµÙ‰ (Ø¯Ù‚ÙŠÙ‚Ø©)',
    'facilities.operating_start'    => 'Ø¨Ø¯Ø§ÙŠØ© Ø³Ø§Ø¹Ø§Øª Ø§Ù„Ø¹Ù…Ù„',
    'facilities.operating_end'      => 'Ù†Ù‡Ø§ÙŠØ© Ø³Ø§Ø¹Ø§Øª Ø§Ù„Ø¹Ù…Ù„',
    'facilities.amenities'          => 'Ø§Ù„Ù…Ø±Ø§ÙÙ‚ ÙˆØ§Ù„Ø®Ø¯Ù…Ø§Øª',
    'facilities.time_slots'         => 'Ø§Ù„ÙØªØ±Ø§Øª Ø§Ù„Ø²Ù…Ù†ÙŠØ©',
    'facilities.upcoming_bookings'  => 'Ø§Ù„Ø­Ø¬ÙˆØ²Ø§Øª Ø§Ù„Ù‚Ø§Ø¯Ù…Ø©',
    'facilities.recent_maintenance' => 'Ø§Ù„ØµÙŠØ§Ù†Ø© Ø§Ù„Ø£Ø®ÙŠØ±Ø©',
    'facilities.bookings'           => 'Ø§Ù„Ø­Ø¬ÙˆØ²Ø§Øª',
    'facilities.today_bookings'     => 'Ø­Ø¬ÙˆØ²Ø§Øª Ø§Ù„ÙŠÙˆÙ…',
    'facilities.pending_bookings'   => 'Ø­Ø¬ÙˆØ²Ø§Øª Ù…Ø¹Ù„Ù‚Ø©',
    'facilities.confirmed_bookings' => 'Ø­Ø¬ÙˆØ²Ø§Øª Ù…Ø¤ÙƒØ¯Ø©',
    'facilities.create_booking'     => 'Ø¥Ù†Ø´Ø§Ø¡ Ø­Ø¬Ø²',
    'facilities.booking_calendar'   => 'ØªÙ‚ÙˆÙŠÙ… Ø§Ù„Ø­Ø¬ÙˆØ²Ø§Øª',
    'facilities.booking_created'    => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø­Ø¬Ø² Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.booking_updated'    => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø­Ø§Ù„Ø© Ø§Ù„Ø­Ø¬Ø²',
    'facilities.slot_not_available' => 'Ø§Ù„ÙØªØ±Ø© Ø§Ù„Ø²Ù…Ù†ÙŠØ© Ø§Ù„Ù…Ø­Ø¯Ø¯Ø© ØºÙŠØ± Ù…ØªØ§Ø­Ø©',
    'facilities.facility'           => 'Ø§Ù„Ù…Ø±ÙÙ‚',
    'facilities.maintenance'        => 'Ø§Ù„ØµÙŠØ§Ù†Ø©',
    'facilities.maintenance_created'=> 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø³Ø¬Ù„ Ø§Ù„ØµÙŠØ§Ù†Ø©',
    'facilities.events'             => 'Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ§Øª',
    'facilities.create_event'       => 'Ø¥Ù†Ø´Ø§Ø¡ ÙØ¹Ø§Ù„ÙŠØ©',
    'facilities.event_created'      => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ© Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.event_updated'      => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ© Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.upcoming_events'    => 'Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ§Øª Ø§Ù„Ù‚Ø§Ø¯Ù…Ø©',
    'facilities.event_full'         => 'Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ© Ù…Ù…ØªÙ„Ø¦Ø©. Ù„Ø§ ÙŠÙ…ÙƒÙ† Ø§Ù„ØªØ³Ø¬ÙŠÙ„.',
    'facilities.already_registered' => 'Ø§Ù„Ø¹Ø¶Ùˆ Ù…Ø³Ø¬Ù„ Ø¨Ø§Ù„ÙØ¹Ù„ ÙÙŠ Ù‡Ø°Ù‡ Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ©',
    'facilities.registration_success'=> 'ØªÙ… Ø§Ù„ØªØ³Ø¬ÙŠÙ„ Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.checkin_success'    => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø­Ø¶ÙˆØ± Ø¨Ù†Ø¬Ø§Ø­',
    'facilities.pools'              => 'Ø­Ù…Ø§Ù…Ø§Øª Ø§Ù„Ø³Ø¨Ø§Ø­Ø©',
    'facilities.reading_recorded'   => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ù‚Ø±Ø§Ø¡Ø©',
    'facilities.parking'            => 'Ù…ÙˆØ§Ù‚Ù Ø§Ù„Ø³ÙŠØ§Ø±Ø§Øª',
    'facilities.permit_issued'      => 'ØªÙ… Ø¥ØµØ¯Ø§Ø± ØªØµØ±ÙŠØ­ Ø§Ù„Ù…ÙˆÙ‚Ù',
    'facilities.facility_status_title' => 'Ø­Ø§Ù„Ø© Ø§Ù„Ù…Ø±Ø§ÙÙ‚',
];
```

## File: modules\facilities\lang\en.php

```php
<?php
return [
    'facilities.title'              => 'Facilities',
    'facilities.create'             => 'Create Facility',
    'facilities.edit'               => 'Edit Facility',
    'facilities.created'            => 'Facility created successfully',
    'facilities.updated'            => 'Facility updated successfully',
    'facilities.types'              => 'Facility Types',
    'facilities.create_type'        => 'Create Facility Type',
    'facilities.edit_type'          => 'Edit Facility Type',
    'facilities.type_created'       => 'Facility type created successfully',
    'facilities.type_updated'       => 'Facility type updated successfully',
    'facilities.code'               => 'Facility Code',
    'facilities.name'               => 'Name',
    'facilities.type'               => 'Type',
    'facilities.capacity'           => 'Capacity',
    'facilities.area_sqm'           => 'Area (mÂ²)',
    'facilities.location'           => 'Location',
    'facilities.indoor'             => 'Indoor',
    'facilities.bookable'           => 'Bookable',
    'facilities.active'             => 'Active',
    'facilities.closed'             => 'Closed',
    'facilities.reserved'           => 'Reserved',
    'facilities.under_maintenance'  => 'Under Maintenance',
    'facilities.total'              => 'Total Facilities',
    'facilities.booking_settings'   => 'Booking Settings',
    'facilities.fee_per_hour'       => 'Fee / Hour',
    'facilities.fee_members'        => 'Member Fee / Hour',
    'facilities.fee_guests'         => 'Guest Fee / Hour',
    'facilities.advance_days'       => 'Advance Booking (days)',
    'facilities.min_minutes'        => 'Min Duration (min)',
    'facilities.max_minutes'        => 'Max Duration (min)',
    'facilities.operating_start'    => 'Operating Hours Start',
    'facilities.operating_end'      => 'Operating Hours End',
    'facilities.amenities'          => 'Amenities',
    'facilities.time_slots'         => 'Time Slots',
    'facilities.upcoming_bookings'  => 'Upcoming Bookings',
    'facilities.recent_maintenance' => 'Recent Maintenance',
    'facilities.bookings'           => 'Bookings',
    'facilities.today_bookings'     => "Today's Bookings",
    'facilities.pending_bookings'   => 'Pending Bookings',
    'facilities.confirmed_bookings' => 'Confirmed Bookings',
    'facilities.create_booking'     => 'Create Booking',
    'facilities.booking_calendar'   => 'Booking Calendar',
    'facilities.booking_created'    => 'Booking created successfully',
    'facilities.booking_updated'    => 'Booking status updated',
    'facilities.slot_not_available' => 'The selected time slot is not available',
    'facilities.facility'           => 'Facility',
    'facilities.maintenance'        => 'Maintenance',
    'facilities.maintenance_created'=> 'Maintenance record created',
    'facilities.events'             => 'Events',
    'facilities.create_event'       => 'Create Event',
    'facilities.event_created'      => 'Event created successfully',
    'facilities.event_updated'      => 'Event updated successfully',
    'facilities.upcoming_events'    => 'Upcoming Events',
    'facilities.event_full'         => 'Event is full. No more registrations allowed.',
    'facilities.already_registered' => 'Member is already registered for this event',
    'facilities.registration_success'=> 'Registration successful',
    'facilities.checkin_success'    => 'Check-in successful',
    'facilities.pools'              => 'Swimming Pools',
    'facilities.reading_recorded'   => 'Pool reading recorded',
    'facilities.parking'            => 'Parking',
    'facilities.permit_issued'      => 'Parking permit issued',
    'facilities.facility_status_title' => 'Facility Status',
];
```

## File: modules\facilities\module.json

```json
{
    "name": "facilities",
    "label_en": "Facilities & Events",
    "label_ar": "Ø§Ù„Ù…Ø±Ø§ÙÙ‚ ÙˆØ§Ù„ÙØ¹Ø§Ù„ÙŠØ§Øª",
    "version": "1.0.0",
    "description": "Facility management, bookings, events, swimming pools, and parking",
    "sort_order": 80,
    "icon": "fas fa-building",
    "routes_file": "routes.php",
    "css_files": ["css/facilities.css"],
    "js_files": ["js/facilities.js"],
    "menu": [
        {
            "label_en": "Facilities",
            "label_ar": "Ø§Ù„Ù…Ø±Ø§ÙÙ‚",
            "icon": "fas fa-building",
            "permission": "facilities.view",
            "sort_order": 80,
            "children": [
                {
                    "label_en": "All Facilities",
                    "label_ar": "Ø¬Ù…ÙŠØ¹ Ø§Ù„Ù…Ø±Ø§ÙÙ‚",
                    "route": "/facilities",
                    "permission": "facilities.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Facility Types",
                    "label_ar": "Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ù…Ø±Ø§ÙÙ‚",
                    "route": "/facilities/types",
                    "permission": "facilities.manage",
                    "sort_order": 2
                },
                {
                    "label_en": "Bookings",
                    "label_ar": "Ø§Ù„Ø­Ø¬ÙˆØ²Ø§Øª",
                    "route": "/bookings",
                    "permission": "bookings.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Events",
                    "label_ar": "Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ§Øª",
                    "route": "/events",
                    "permission": "events.view",
                    "sort_order": 4
                },
                {
                    "label_en": "Swimming Pools",
                    "label_ar": "Ø­Ù…Ø§Ù…Ø§Øª Ø§Ù„Ø³Ø¨Ø§Ø­Ø©",
                    "route": "/facilities/pools",
                    "permission": "facilities.view",
                    "sort_order": 5
                },
                {
                    "label_en": "Parking",
                    "label_ar": "Ù…ÙˆØ§Ù‚Ù Ø§Ù„Ø³ÙŠØ§Ø±Ø§Øª",
                    "route": "/facilities/parking",
                    "permission": "facilities.view",
                    "sort_order": 6
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "facilities_todays_bookings",
            "label_en": "Today's Bookings",
            "label_ar": "Ø­Ø¬ÙˆØ²Ø§Øª Ø§Ù„ÙŠÙˆÙ…",
            "permission": "bookings.view",
            "partial": "views/widgets/todays_bookings.php",
            "size": "col-md-4",
            "sort_order": 80
        },
        {
            "widget_id": "facilities_upcoming_events",
            "label_en": "Upcoming Events",
            "label_ar": "Ø§Ù„ÙØ¹Ø§Ù„ÙŠØ§Øª Ø§Ù„Ù‚Ø§Ø¯Ù…Ø©",
            "permission": "events.view",
            "partial": "views/widgets/upcoming_events.php",
            "size": "col-md-4",
            "sort_order": 81
        },
        {
            "widget_id": "facilities_status",
            "label_en": "Facility Status",
            "label_ar": "Ø­Ø§Ù„Ø© Ø§Ù„Ù…Ø±Ø§ÙÙ‚",
            "permission": "facilities.view",
            "partial": "views/widgets/facility_status.php",
            "size": "col-md-4",
            "sort_order": 82
        }
    ]
}
```

## File: modules\facilities\routes.php

```php
<?php
return [
    // â”€â”€â”€ Facilities â”€â”€â”€
    ['GET',  '/facilities',                    'FacilityController@index',       'facilities.view'],
    ['GET',  '/facilities/create',             'FacilityController@create',      'facilities.manage'],
    ['POST', '/facilities',                    'FacilityController@store',       'facilities.manage'],
    ['GET',  '/facilities/types',              'FacilityController@types',       'facilities.manage'],
    ['GET',  '/facilities/types/create',       'FacilityController@createType',  'facilities.manage'],
    ['POST', '/facilities/types',              'FacilityController@storeType',   'facilities.manage'],
    ['GET',  '/facilities/types/{id}/edit',    'FacilityController@editType',    'facilities.manage'],
    ['POST', '/facilities/types/{id}',         'FacilityController@updateType',  'facilities.manage'],
    ['GET',  '/facilities/pools',              'FacilityController@pools',       'facilities.view'],
    ['POST', '/facilities/pools/readings',     'FacilityController@storeReading','facilities.manage'],
    ['GET',  '/facilities/parking',            'FacilityController@parking',     'facilities.view'],
    ['POST', '/facilities/parking/permits',    'FacilityController@storePermit', 'facilities.manage'],
    ['GET',  '/facilities/{id}',               'FacilityController@show',        'facilities.view'],
    ['GET',  '/facilities/{id}/edit',          'FacilityController@edit',        'facilities.manage'],
    ['POST', '/facilities/{id}',               'FacilityController@update',      'facilities.manage'],
    ['GET',  '/facilities/{id}/maintenance',   'FacilityController@maintenance', 'facilities.manage'],
    ['POST', '/facilities/{id}/maintenance',   'FacilityController@storeMaintenance', 'facilities.manage'],

    // â”€â”€â”€ Bookings â”€â”€â”€
    ['GET',  '/bookings',                      'BookingController@index',        'bookings.view'],
    ['GET',  '/bookings/create',               'BookingController@create',       'bookings.create'],
    ['POST', '/bookings',                      'BookingController@store',        'bookings.create'],
    ['GET',  '/bookings/calendar/{id}',        'BookingController@calendar',     'bookings.view'],
    ['POST', '/bookings/{id}/status',          'BookingController@updateStatus', 'bookings.manage'],

    // â”€â”€â”€ Events â”€â”€â”€
    ['GET',  '/events',                        'EventController@index',          'events.view'],
    ['GET',  '/events/create',                 'EventController@create',         'events.manage'],
    ['POST', '/events',                        'EventController@store',          'events.manage'],
    ['GET',  '/events/{id}',                   'EventController@show',           'events.view'],
    ['GET',  '/events/{id}/edit',              'EventController@edit',           'events.manage'],
    ['POST', '/events/{id}',                   'EventController@update',         'events.manage'],
    ['POST', '/events/{id}/register',          'EventController@register',       'events.manage'],
    ['POST', '/events/{id}/checkin',           'EventController@checkin',        'events.manage'],

    // â”€â”€â”€ API Endpoints â”€â”€â”€
    ['GET',  '/api/facilities/list',           'FacilityApiController@list',             'facilities.view'],
    ['GET',  '/api/facilities/types/list',     'FacilityApiController@typesList',         'facilities.manage'],
    ['GET',  '/api/bookings/list',             'FacilityApiController@bookingsList',      'bookings.view'],
    ['GET',  '/api/bookings/calendar/{id}',    'FacilityApiController@calendarData',      'bookings.view'],
    ['GET',  '/api/bookings/availability',     'FacilityApiController@checkAvailability', 'bookings.create'],
    ['GET',  '/api/events/list',               'FacilityApiController@eventsList',        'events.view'],
    ['GET',  '/api/events/{id}/registrations', 'FacilityApiController@eventRegistrations','events.view'],
    ['GET',  '/api/facilities/maintenance/{id}','FacilityApiController@maintenanceList',  'facilities.manage'],
    ['GET',  '/api/facilities/pools/readings/{id}','FacilityApiController@poolReadings',  'facilities.view'],
    ['GET',  '/api/facilities/parking/permits', 'FacilityApiController@permitsList',      'facilities.view'],
    ['GET',  '/api/facilities/parking/log',     'FacilityApiController@parkingLog',       'facilities.view'],
];
```

## File: modules\facilities\views\bookings\calendar.php

```php
<div class="card">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h5 class="mb-0"><i class="fas fa-calendar me-2"></i><?= lang('facilities.booking_calendar') ?>: <?= e($facility['name_en']) ?></h5>
        <?php if (auth_can('bookings.create')): ?>
        <a href="/bookings/create" class="btn btn-sm btn-primary"><i class="fas fa-plus me-1"></i>New Booking</a>
        <?php endif; ?>
    </div>
    <div class="card-body">
        <div id="booking-calendar" style="min-height: 600px;"></div>
    </div>
</div>

<?php $__view->push('styles') ?>
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css" rel="stylesheet">
<?php $__view->endPush() ?>

<?php $__view->push('scripts') ?>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
    var calEl = document.getElementById('booking-calendar');
    var calendar = new FullCalendar.Calendar(calEl, {
        initialView: 'timeGridWeek',
        headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay' },
        slotMinTime: '<?= e($facility['operating_hours_start'] ?? '06:00:00') ?>',
        slotMaxTime: '<?= e($facility['operating_hours_end'] ?? '23:00:00') ?>',
        slotDuration: '00:30:00',
        allDaySlot: false,
        direction: '<?= is_rtl() ? 'rtl' : 'ltr' ?>',
        events: {
            url: '/api/bookings/calendar/<?= $facility['id'] ?>',
            method: 'GET',
            failure: function() { Swal.fire('Error', 'Failed to load bookings', 'error'); }
        },
        eventClick: function(info) {
            var props = info.event.extendedProps;
            Swal.fire({
                title: info.event.title,
                html: '<table class="table table-sm text-start">' +
                    '<tr><td><strong>Booking #</strong></td><td>' + (props.booking_number||'-') + '</td></tr>' +
                    '<tr><td><strong>Member</strong></td><td>' + (props.member||'-') + '</td></tr>' +
                    '<tr><td><strong>Status</strong></td><td>' + (props.status||'-') + '</td></tr>' +
                    '<tr><td><strong>Type</strong></td><td>' + (props.type||'-') + '</td></tr>' +
                    '</table>',
                confirmButtonText: 'Close'
            });
        },
        height: 'auto',
    });
    calendar.render();
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\bookings\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-calendar-plus me-2"></i><?= lang('facilities.create_booking') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="/bookings" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Member <span class="text-danger">*</span></label>
                            <select name="member_id" class="form-select select2" id="member-search" required data-placeholder="Search member..."></select>
                            <div class="invalid-feedback" data-field="member_id"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Facility <span class="text-danger">*</span></label>
                            <select name="facility_id" class="form-select" id="facility-select" required>
                                <option value="">Select facility...</option>
                                <?php foreach ($facilities as $f): ?>
                                <option value="<?= $f['id'] ?>" data-fee="<?= $f['booking_fee_members'] ?>" data-min="<?= $f['min_booking_minutes'] ?>" data-max="<?= $f['max_booking_minutes'] ?>" data-advance="<?= $f['advance_booking_days'] ?>">
                                    <?= e($f['name_en']) ?> (<?= e($f['facility_code']) ?>)
                                </option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="facility_id"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Date <span class="text-danger">*</span></label>
                            <input type="date" name="booking_date" class="form-control" id="booking-date" required min="<?= date('Y-m-d') ?>">
                            <div class="invalid-feedback" data-field="booking_date"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Start Time <span class="text-danger">*</span></label>
                            <input type="time" name="start_time" class="form-control" id="start-time" required step="900">
                            <div class="invalid-feedback" data-field="start_time"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">End Time <span class="text-danger">*</span></label>
                            <input type="time" name="end_time" class="form-control" id="end-time" required step="900">
                            <div class="invalid-feedback" data-field="end_time"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Booking Type</label>
                            <select name="booking_type" class="form-select">
                                <option value="regular">Regular</option>
                                <option value="training">Training</option>
                                <option value="tournament">Tournament</option>
                                <option value="event">Event</option>
                                <option value="private">Private</option>
                            </select>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Estimated Fee</label>
                            <input type="text" class="form-control" id="estimated-fee" readonly value="-">
                        </div>
                        <div class="col-12" id="availability-result" style="display:none"></div>
                        <div class="col-12">
                            <label class="form-label">Notes</label>
                            <textarea name="notes" class="form-control" rows="2"></textarea>
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="button" class="btn btn-outline-info" id="check-availability-btn"><i class="fas fa-search me-1"></i>Check Availability</button>
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/bookings" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    $('#member-search').select2({
        theme: 'bootstrap-5', width: '100%', minimumInputLength: 2,
        ajax: {
            url: '/api/members/search',
            dataType: 'json', delay: 300,
            data: function(params) { return { q: params.term }; },
            processResults: function(data) { return { results: data.map(m => ({ id: m.id, text: m.membership_number + ' - ' + m.text })) }; }
        }
    });

    function calcFee() {
        const opt = document.querySelector('#facility-select option:checked');
        const fee = parseFloat(opt?.dataset?.fee || 0);
        const s = document.getElementById('start-time').value;
        const e = document.getElementById('end-time').value;
        if (s && e && fee) {
            const mins = (new Date('2000-01-01T' + e) - new Date('2000-01-01T' + s)) / 60000;
            if (mins > 0) { document.getElementById('estimated-fee').value = (fee * (mins / 60)).toFixed(2) + ' EGP'; }
        }
    }
    document.getElementById('start-time').addEventListener('change', calcFee);
    document.getElementById('end-time').addEventListener('change', calcFee);
    document.getElementById('facility-select').addEventListener('change', calcFee);

    document.getElementById('check-availability-btn').addEventListener('click', function() {
        const fac = document.getElementById('facility-select').value;
        const dt = document.getElementById('booking-date').value;
        const st = document.getElementById('start-time').value;
        const et = document.getElementById('end-time').value;
        if (!fac || !dt || !st || !et) { FormHelper.showToast('warning', 'Fill in facility, date, and time first'); return; }
        $.get('/api/bookings/availability', { facility_id: fac, date: dt, start_time: st, end_time: et }, function(res) {
            const el = document.getElementById('availability-result');
            el.style.display = 'block';
            el.innerHTML = res.available
                ? '<div class="alert alert-success mb-0"><i class="fas fa-check-circle me-1"></i> Slot is available!</div>'
                : '<div class="alert alert-danger mb-0"><i class="fas fa-times-circle me-1"></i> Slot is NOT available (' + res.conflict_count + ' conflicts)</div>';
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\bookings\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-4">
        <div class="card border-primary h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1"><?= lang('facilities.today_bookings') ?></h6><h3 class="mb-0"><?= $todayCount ?></h3></div>
            <div class="align-self-center"><i class="fas fa-calendar-day fa-2x text-primary opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-4">
        <div class="card border-warning h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1"><?= lang('facilities.pending_bookings') ?></h6><h3 class="mb-0"><?= $pendingCount ?></h3></div>
            <div class="align-self-center"><i class="fas fa-clock fa-2x text-warning opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-4">
        <div class="card border-success h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1"><?= lang('facilities.confirmed_bookings') ?></h6><h3 class="mb-0"><?= $confirmedCount ?></h3></div>
            <div class="align-self-center"><i class="fas fa-check-circle fa-2x text-success opacity-50"></i></div></div>
        </div></div>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <form class="row g-2 align-items-end" id="booking-filters" data-filter-table="bookings-table">
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('common.status') ?></label>
                <select name="status" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="pending">Pending</option>
                    <option value="confirmed">Confirmed</option>
                    <option value="checked_in">Checked In</option>
                    <option value="completed">Completed</option>
                    <option value="cancelled">Cancelled</option>
                    <option value="no_show">No Show</option>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('facilities.facility') ?></label>
                <select name="facility_id" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <?php foreach ($facilities as $f): ?>
                    <option value="<?= $f['id'] ?>"><?= e($f['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm">From</label>
                <input type="date" name="date_from" class="form-control form-control-sm">
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm">To</label>
                <input type="date" name="date_to" class="form-control form-control-sm">
            </div>
            <div class="col-md-2">
                <button type="button" class="btn btn-sm btn-outline-primary" onclick="DataTableHelper.reload('bookings-table')"><?= lang('common.filter') ?></button>
            </div>
            <?php if (auth_can('bookings.create')): ?>
            <div class="col-md-2 text-end">
                <a href="/bookings/create" class="btn btn-sm btn-primary"><i class="fas fa-plus me-1"></i><?= lang('facilities.create_booking') ?></a>
            </div>
            <?php endif; ?>
        </form>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="bookings-table" class="table table-striped table-hover w-100"
               data-url="/api/bookings/list"
               data-columns='<?= json_encode([
                   ["data"=>"id","title"=>"#","width"=>"50px"],
                   ["data"=>"number","title"=>"Booking #"],
                   ["data"=>"facility","title"=>"Facility"],
                   ["data"=>"member","title"=>"Member"],
                   ["data"=>"date","title"=>"Date"],
                   ["data"=>"time","title"=>"Time"],
                   ["data"=>"fee","title"=>"Fee"],
                   ["data"=>"status","title"=>"Status"],
                   ["data"=>"actions","title"=>"Actions","orderable"=>false,"searchable"=>false],
               ]) ?>'>
            <thead><tr><th>#</th><th>Booking #</th><th>Facility</th><th>Member</th><th>Date</th><th>Time</th><th>Fee</th><th>Status</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('bookings-table', { order: [[0, 'desc']] });

    document.addEventListener('click', function(e) {
        const btn = e.target.closest('.btn-status');
        if (!btn) return;
        const bookingId = btn.dataset.id;
        const newStatus = btn.dataset.status;
        const needsReason = newStatus === 'cancelled';

        if (needsReason) {
            Swal.fire({
                title: 'Cancel Booking',
                input: 'textarea',
                inputLabel: 'Reason for cancellation',
                showCancelButton: true,
                confirmButtonText: 'Confirm Cancel',
                confirmButtonColor: '#dc3545',
            }).then((result) => {
                if (result.isConfirmed) {
                    updateBookingStatus(bookingId, newStatus, result.value || '');
                }
            });
        } else {
            Swal.fire({
                title: 'Confirm',
                text: 'Change booking status to ' + newStatus + '?',
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: 'Yes',
            }).then((result) => {
                if (result.isConfirmed) {
                    updateBookingStatus(bookingId, newStatus, '');
                }
            });
        }
    });

    function updateBookingStatus(id, status, reason) {
        const formData = new FormData();
        formData.append('_token', document.querySelector('meta[name="csrf-token"]').content);
        formData.append('status', status);
        formData.append('reason', reason);

        $.ajax({
            url: '/bookings/' + id + '/status',
            type: 'POST',
            data: formData,
            processData: false,
            contentType: false,
            success: function(res) {
                if (res.success) {
                    FormHelper.showToast('success', res.message);
                    DataTableHelper.reload('bookings-table');
                } else {
                    FormHelper.showToast('error', res.message);
                }
            }
        });
    }
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-plus me-2"></i><?= lang('facilities.create') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="/facilities" data-ajax="true">
                    <?= csrf_field() ?>
                    <?php include __DIR__ . '/partials/_facility_form.php'; ?>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/facilities" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\facilities\views\edit.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-edit me-2"></i><?= lang('facilities.edit') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="/facilities/<?= $facility['id'] ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <?php include __DIR__ . '/partials/_facility_form.php'; ?>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/facilities/<?= $facility['id'] ?>" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\facilities\views\events\create.php

```php
<div class="row justify-content-center"><div class="col-lg-10"><div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-calendar-plus me-2"></i><?= lang('facilities.create_event') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/events" data-ajax="true">
            <?= csrf_field() ?>
            <?php $ev = []; include __DIR__ . '/partials/_event_form.php'; ?>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/events" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\facilities\views\events\edit.php

```php
<div class="row justify-content-center"><div class="col-lg-10"><div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-edit me-2"></i><?= lang('common.edit') ?>: <?= e($event['title_en']) ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/events/<?= $event['id'] ?>" data-ajax="true">
            <?= csrf_field() ?>
            <?php $ev = $event; include __DIR__ . '/partials/_event_form.php'; ?>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/events/<?= $event['id'] ?>" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div></div></div>
```

## File: modules\facilities\views\events\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-4"><div class="card border-info h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Planned / Open</h6><h3 class="mb-0"><?= $planned ?></h3></div><div class="align-self-center"><i class="fas fa-calendar-alt fa-2x text-info opacity-50"></i></div></div></div></div></div>
    <div class="col-md-4"><div class="card border-primary h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Ongoing</h6><h3 class="mb-0"><?= $ongoing ?></h3></div><div class="align-self-center"><i class="fas fa-play-circle fa-2x text-primary opacity-50"></i></div></div></div></div></div>
    <div class="col-md-4"><div class="card border-success h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Completed</h6><h3 class="mb-0"><?= $completed ?></h3></div><div class="align-self-center"><i class="fas fa-check-double fa-2x text-success opacity-50"></i></div></div></div></div></div>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <form class="row g-2 align-items-end" data-filter-table="events-table">
            <div class="col-md-3">
                <select name="status" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="planned">Planned</option>
                    <option value="registration_open">Registration Open</option>
                    <option value="ongoing">Ongoing</option>
                    <option value="completed">Completed</option>
                    <option value="cancelled">Cancelled</option>
                </select>
            </div>
            <div class="col-md-2"><button type="button" class="btn btn-sm btn-outline-primary" onclick="DataTableHelper.reload('events-table')">Filter</button></div>
            <?php if (auth_can('events.manage')): ?>
            <div class="col text-end"><a href="/events/create" class="btn btn-sm btn-primary"><i class="fas fa-plus me-1"></i><?= lang('facilities.create_event') ?></a></div>
            <?php endif; ?>
        </form>
    </div>
</div>

<div class="card"><div class="card-body">
    <table id="events-table" class="table table-striped table-hover w-100"
           data-url="/api/events/list"
           data-columns='<?= json_encode([
               ["data"=>"id","title"=>"#","width"=>"50px"],
               ["data"=>"code","title"=>"Code"],
               ["data"=>"title","title"=>"Title"],
               ["data"=>"type","title"=>"Type"],
               ["data"=>"venue","title"=>"Venue"],
               ["data"=>"date","title"=>"Date"],
               ["data"=>"attendees","title"=>"Attendees"],
               ["data"=>"status","title"=>"Status"],
               ["data"=>"actions","title"=>"Actions","orderable"=>false,"searchable"=>false],
           ]) ?>'>
        <thead><tr><th>#</th><th>Code</th><th>Title</th><th>Type</th><th>Venue</th><th>Date</th><th>Attendees</th><th>Status</th><th>Actions</th></tr></thead>
    </table>
</div></div>

<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('events-table', { order: [[0, 'desc']] }); });</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\events\partials\_event_form.php

```php
<?php $ev = $ev ?? []; ?>
<div class="row g-3">
    <div class="col-md-6">
        <label class="form-label">Title (EN) <span class="text-danger">*</span></label>
        <input type="text" name="title_en" class="form-control" value="<?= e($ev['title_en'] ?? '') ?>" required>
        <div class="invalid-feedback" data-field="title_en"></div>
    </div>
    <div class="col-md-6">
        <label class="form-label">Title (AR)</label>
        <input type="text" name="title_ar" class="form-control" value="<?= e($ev['title_ar'] ?? '') ?>" dir="rtl">
    </div>
    <div class="col-md-4">
        <label class="form-label">Event Type <span class="text-danger">*</span></label>
        <select name="event_type" class="form-select" required>
            <?php foreach (['social','sport','cultural','charity','celebration','workshop','competition','other'] as $t): ?>
            <option value="<?= $t ?>" <?= selected($t, $ev['event_type'] ?? '') ?>><?= ucfirst($t) ?></option>
            <?php endforeach; ?>
        </select>
    </div>
    <div class="col-md-4">
        <label class="form-label">Venue (Facility)</label>
        <select name="facility_id" class="form-select">
            <option value="">No facility</option>
            <?php foreach ($facilities as $f): ?>
            <option value="<?= $f['id'] ?>" <?= selected($f['id'], $ev['facility_id'] ?? '') ?>><?= e($f['name_en']) ?></option>
            <?php endforeach; ?>
        </select>
    </div>
    <div class="col-md-4">
        <label class="form-label">Location</label>
        <input type="text" name="location" class="form-control" value="<?= e($ev['location'] ?? '') ?>">
    </div>
    <div class="col-md-3"><label class="form-label">Start DateTime <span class="text-danger">*</span></label><input type="datetime-local" name="start_datetime" class="form-control" value="<?= e(str_replace(' ', 'T', $ev['start_datetime'] ?? '')) ?>" required></div>
    <div class="col-md-3"><label class="form-label">End DateTime</label><input type="datetime-local" name="end_datetime" class="form-control" value="<?= e(str_replace(' ', 'T', $ev['end_datetime'] ?? '')) ?>"></div>
    <div class="col-md-3"><label class="form-label">Registration Start</label><input type="datetime-local" name="registration_start" class="form-control" value="<?= e(str_replace(' ', 'T', $ev['registration_start'] ?? '')) ?>"></div>
    <div class="col-md-3"><label class="form-label">Registration End</label><input type="datetime-local" name="registration_end" class="form-control" value="<?= e(str_replace(' ', 'T', $ev['registration_end'] ?? '')) ?>"></div>
    <div class="col-md-3"><label class="form-label">Max Attendees</label><input type="number" name="max_attendees" class="form-control" value="<?= e($ev['max_attendees'] ?? '') ?>" min="0"></div>
    <div class="col-md-3"><label class="form-label">Member Fee</label><input type="number" name="member_fee" class="form-control" value="<?= e($ev['member_fee'] ?? '0') ?>" step="0.01" min="0"></div>
    <div class="col-md-3"><label class="form-label">Guest Fee</label><input type="number" name="guest_fee" class="form-control" value="<?= e($ev['guest_fee'] ?? '0') ?>" step="0.01" min="0"></div>
    <div class="col-md-3">
        <div class="form-check form-switch mt-4">
            <input type="hidden" name="allow_guests" value="0">
            <input class="form-check-input" type="checkbox" name="allow_guests" value="1" id="allow_guests" <?= checked(1, $ev['allow_guests'] ?? 0) ?>>
            <label class="form-check-label" for="allow_guests">Allow Guests</label>
        </div>
    </div>
    <?php if (!empty($ev)): ?>
    <div class="col-md-4">
        <label class="form-label">Status</label>
        <select name="status" class="form-select">
            <?php foreach (['planned','registration_open','registration_closed','ongoing','completed','cancelled','postponed'] as $s): ?>
            <option value="<?= $s ?>" <?= selected($s, $ev['status'] ?? '') ?>><?= ucfirst(str_replace('_', ' ', $s)) ?></option>
            <?php endforeach; ?>
        </select>
    </div>
    <?php endif; ?>
    <div class="col-md-6"><label class="form-label">Description (EN)</label><textarea name="description_en" class="form-control" rows="3"><?= e($ev['description_en'] ?? '') ?></textarea></div>
    <div class="col-md-6"><label class="form-label">Description (AR)</label><textarea name="description_ar" class="form-control" rows="3" dir="rtl"><?= e($ev['description_ar'] ?? '') ?></textarea></div>
</div>
```

## File: modules\facilities\views\events\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h3 class="mb-1"><?= e($event['title_en']) ?> <small class="text-muted">(<?= e($event['event_code']) ?>)</small></h3>
        <?php if ($event['title_ar']): ?><p class="text-muted mb-0" dir="rtl"><?= e($event['title_ar']) ?></p><?php endif; ?>
    </div>
    <div class="d-flex gap-2">
        <?= status_badge($event['status']) ?>
        <?php if (auth_can('events.manage')): ?>
        <a href="/events/<?= $event['id'] ?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a>
        <?php endif; ?>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-8">
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Event Details</h6></div>
            <div class="card-body">
                <div class="row g-2">
                    <div class="col-md-4"><strong>Type:</strong> <span class="badge bg-secondary"><?= e(ucfirst($event['event_type'])) ?></span></div>
                    <div class="col-md-4"><strong>Venue:</strong> <?= e($event['facility_name_en'] ?? $event['location'] ?? '-') ?></div>
                    <div class="col-md-4"><strong>Attendees:</strong> <?= $event['current_attendees'] ?><?= $event['max_attendees'] ? '/' . $event['max_attendees'] : '' ?></div>
                    <div class="col-md-6"><strong>Start:</strong> <?= format_datetime($event['start_datetime']) ?></div>
                    <div class="col-md-6"><strong>End:</strong> <?= $event['end_datetime'] ? format_datetime($event['end_datetime']) : '-' ?></div>
                    <div class="col-md-4"><strong>Member Fee:</strong> <?= format_money($event['member_fee']) ?></div>
                    <div class="col-md-4"><strong>Guest Fee:</strong> <?= format_money($event['guest_fee']) ?></div>
                    <div class="col-md-4"><strong>Guests Allowed:</strong> <?= $event['allow_guests'] ? 'Yes' : 'No' ?></div>
                    <?php if ($event['description_en']): ?><div class="col-12 mt-2"><strong>Description:</strong><p><?= nl2br(e($event['description_en'])) ?></p></div><?php endif; ?>
                </div>
            </div>
        </div>

        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h6 class="mb-0">Registrations (<?= count($registrations) ?>)</h6>
                <?php if (auth_can('events.manage') && in_array($event['status'], ['planned', 'registration_open'])): ?>
                <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#registerModal"><i class="fas fa-user-plus me-1"></i>Register Member</button>
                <?php endif; ?>
            </div>
            <div class="card-body p-0">
                <?php if (empty($registrations)): ?>
                <div class="text-center text-muted py-4">No registrations yet.</div>
                <?php else: ?>
                <div class="table-responsive">
                    <table class="table table-sm table-hover mb-0">
                        <thead><tr><th>Member</th><th>Attendees</th><th>Fee</th><th>Date</th><th>Status</th><th>Actions</th></tr></thead>
                        <tbody>
                        <?php foreach ($registrations as $r): ?>
                        <tr>
                            <td><?= e($r['member_name'] ?? '-') ?> <small class="text-muted"><?= e($r['membership_number'] ?? '') ?></small></td>
                            <td><?= $r['total_attendees'] ?></td>
                            <td><?= format_money($r['total_fee']) ?></td>
                            <td><?= format_datetime($r['registration_date']) ?></td>
                            <td><?= status_badge($r['status']) ?></td>
                            <td>
                                <?php if ($r['status'] !== 'attended' && auth_can('events.manage')): ?>
                                <form method="POST" action="/events/<?= $event['id'] ?>/checkin" class="d-inline" data-ajax="true">
                                    <?= csrf_field() ?>
                                    <input type="hidden" name="registration_id" value="<?= $r['id'] ?>">
                                    <button type="submit" class="btn btn-sm btn-success" title="Check In"><i class="fas fa-check"></i></button>
                                </form>
                                <?php endif; ?>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="card"><div class="card-body text-center">
            <i class="fas fa-calendar-alt fa-3x text-primary mb-2"></i>
            <h5><?= date('M d', strtotime($event['start_datetime'])) ?></h5>
            <p class="text-muted"><?= date('l, Y', strtotime($event['start_datetime'])) ?></p>
            <?php if ($event['start_datetime']): ?>
            <p><i class="fas fa-clock me-1"></i><?= date('H:i', strtotime($event['start_datetime'])) ?><?= $event['end_datetime'] ? ' - ' . date('H:i', strtotime($event['end_datetime'])) : '' ?></p>
            <?php endif; ?>
        </div></div>
    </div>
</div>

<!-- Register Modal -->
<div class="modal fade" id="registerModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Register Member</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form method="POST" action="/events/<?= $event['id'] ?>/register" data-ajax="true" id="register-form">
            <?= csrf_field() ?>
            <div class="mb-3">
                <label class="form-label">Member <span class="text-danger">*</span></label>
                <select name="member_id" class="form-select select2" data-placeholder="Search member..." required></select>
            </div>
            <?php if ($event['allow_guests']): ?>
            <div class="mb-3">
                <label class="form-label">Number of Guests</label>
                <input type="number" name="guest_count" class="form-control" value="0" min="0" max="<?= $event['max_guests_per_member'] ?? 10 ?>">
            </div>
            <?php endif; ?>
            <div class="mb-3"><label class="form-label">Notes</label><textarea name="notes" class="form-control" rows="2"></textarea></div>
            <button type="submit" class="btn btn-primary"><i class="fas fa-user-plus me-1"></i>Register</button>
        </form>
    </div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    $('#registerModal select.select2').select2({
        theme: 'bootstrap-5', width: '100%', dropdownParent: $('#registerModal'), minimumInputLength: 2,
        ajax: {
            url: '/api/members/search', dataType: 'json', delay: 300,
            data: function(p) { return { q: p.term }; },
            processResults: function(d) { return { results: d.map(m => ({ id: m.id, text: m.membership_number + ' - ' + m.text })) }; }
        }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-3">
        <div class="card border-primary h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div><h6 class="text-muted mb-1"><?= lang('facilities.total') ?></h6><h3 class="mb-0"><?= number_format($totalAll) ?></h3></div>
                    <div class="align-self-center"><i class="fas fa-building fa-2x text-primary opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-success h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div><h6 class="text-muted mb-1"><?= lang('facilities.active') ?></h6><h3 class="mb-0"><?= number_format($totalActive) ?></h3></div>
                    <div class="align-self-center"><i class="fas fa-check-circle fa-2x text-success opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-warning h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div><h6 class="text-muted mb-1"><?= lang('facilities.under_maintenance') ?></h6><h3 class="mb-0"><?= number_format($totalMaintenance) ?></h3></div>
                    <div class="align-self-center"><i class="fas fa-tools fa-2x text-warning opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div><h6 class="text-muted mb-1"><?= lang('facilities.closed') ?></h6><h3 class="mb-0"><?= number_format($totalClosed) ?></h3></div>
                    <div class="align-self-center"><i class="fas fa-lock fa-2x text-danger opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <form class="row g-2 align-items-end" id="facility-filters" data-filter-table="facilities-table">
            <div class="col-md-3">
                <label class="form-label form-label-sm"><?= lang('common.status') ?></label>
                <select name="status" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="active"><?= lang('facilities.active') ?></option>
                    <option value="under_maintenance"><?= lang('facilities.under_maintenance') ?></option>
                    <option value="closed"><?= lang('facilities.closed') ?></option>
                    <option value="reserved"><?= lang('facilities.reserved') ?></option>
                </select>
            </div>
            <div class="col-md-3">
                <label class="form-label form-label-sm"><?= lang('facilities.type') ?></label>
                <select name="facility_type_id" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <?php foreach ($facilityTypes as $ft): ?>
                    <option value="<?= $ft['id'] ?>"><?= e($ft['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-2">
                <button type="button" class="btn btn-sm btn-outline-primary" onclick="DataTableHelper.reload('facilities-table')"><?= lang('common.filter') ?></button>
                <button type="reset" class="btn btn-sm btn-outline-secondary" onclick="setTimeout(()=>DataTableHelper.reload('facilities-table'),50)"><?= lang('common.clear') ?></button>
            </div>
            <?php if (auth_can('facilities.manage')): ?>
            <div class="col-md-4 text-end">
                <a href="/facilities/create" class="btn btn-sm btn-primary"><i class="fas fa-plus me-1"></i><?= lang('facilities.create') ?></a>
            </div>
            <?php endif; ?>
        </form>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="facilities-table" class="table table-striped table-hover w-100"
               data-url="/api/facilities/list"
               data-columns='<?= json_encode([
                   ["data"=>"id","title"=>"#","width"=>"50px"],
                   ["data"=>"code","title"=>lang("facilities.code")],
                   ["data"=>"name","title"=>lang("facilities.name")],
                   ["data"=>"type","title"=>lang("facilities.type")],
                   ["data"=>"capacity","title"=>lang("facilities.capacity")],
                   ["data"=>"bookable","title"=>lang("facilities.bookable"),"className"=>"text-center"],
                   ["data"=>"status","title"=>lang("common.status")],
                   ["data"=>"actions","title"=>lang("common.actions"),"orderable"=>false,"searchable"=>false],
               ]) ?>'>
            <thead>
                <tr>
                    <th>#</th>
                    <th><?= lang('facilities.code') ?></th>
                    <th><?= lang('facilities.name') ?></th>
                    <th><?= lang('facilities.type') ?></th>
                    <th><?= lang('facilities.capacity') ?></th>
                    <th><?= lang('facilities.bookable') ?></th>
                    <th><?= lang('common.status') ?></th>
                    <th><?= lang('common.actions') ?></th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('facilities-table', { order: [[0, 'desc']] });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\maintenance\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4><?= lang('facilities.maintenance') ?>: <?= e($facility['name_en']) ?></h4>
    <button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addMaintenanceModal"><i class="fas fa-plus me-1"></i>Add Record</button>
</div>

<div class="card">
    <div class="card-body">
        <table id="maintenance-table" class="table table-striped table-hover w-100"
               data-url="/api/facilities/maintenance/<?= $facility['id'] ?>"
               data-columns='<?= json_encode([
                   ["data"=>"id","title"=>"#","width"=>"50px"],
                   ["data"=>"type","title"=>"Type"],
                   ["data"=>"title","title"=>"Title"],
                   ["data"=>"start","title"=>"Start Date"],
                   ["data"=>"end","title"=>"End Date"],
                   ["data"=>"cost","title"=>"Est. Cost"],
                   ["data"=>"priority","title"=>"Priority"],
                   ["data"=>"status","title"=>"Status"],
               ]) ?>'>
            <thead><tr><th>#</th><th>Type</th><th>Title</th><th>Start</th><th>End</th><th>Cost</th><th>Priority</th><th>Status</th></tr></thead>
        </table>
    </div>
</div>

<!-- Add Maintenance Modal -->
<div class="modal fade" id="addMaintenanceModal" tabindex="-1"><div class="modal-dialog modal-lg"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Add Maintenance Record</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form method="POST" action="/facilities/<?= $facility['id'] ?>/maintenance" data-ajax="true" id="maintenance-form">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6">
                    <label class="form-label">Type <span class="text-danger">*</span></label>
                    <select name="maintenance_type" class="form-select" required>
                        <option value="scheduled">Scheduled</option>
                        <option value="emergency">Emergency</option>
                        <option value="inspection">Inspection</option>
                        <option value="upgrade">Upgrade</option>
                        <option value="repair">Repair</option>
                    </select>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Priority <span class="text-danger">*</span></label>
                    <select name="priority" class="form-select" required>
                        <option value="low">Low</option>
                        <option value="medium" selected>Medium</option>
                        <option value="high">High</option>
                        <option value="critical">Critical</option>
                    </select>
                </div>
                <div class="col-12">
                    <label class="form-label">Title <span class="text-danger">*</span></label>
                    <input type="text" name="title" class="form-control" required>
                    <div class="invalid-feedback" data-field="title"></div>
                </div>
                <div class="col-12">
                    <label class="form-label">Description</label>
                    <textarea name="description" class="form-control" rows="3"></textarea>
                </div>
                <div class="col-md-4"><label class="form-label">Start Date <span class="text-danger">*</span></label><input type="date" name="start_date" class="form-control" required></div>
                <div class="col-md-4"><label class="form-label">End Date</label><input type="date" name="end_date" class="form-control"></div>
                <div class="col-md-4"><label class="form-label">Estimated Cost</label><input type="number" name="estimated_cost" class="form-control" step="0.01" min="0"></div>
                <div class="col-md-6"><label class="form-label">Assigned To</label><input type="text" name="assigned_to" class="form-control"></div>
                <div class="col-md-6"><label class="form-label">Vendor</label><input type="text" name="vendor" class="form-control"></div>
            </div>
            <div class="mt-3"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button></div>
        </form>
    </div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('maintenance-table', { order: [[3, 'desc']] });
    document.getElementById('maintenance-form').addEventListener('submit', function() {
        setTimeout(function() { DataTableHelper.reload('maintenance-table'); }, 1000);
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\parking\index.php

```php
<ul class="nav nav-tabs mb-4" role="tablist">
    <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-zones">Zones</a></li>
    <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-permits">Permits</a></li>
    <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-log">Parking Log</a></li>
</ul>

<div class="tab-content">
    <!-- Zones Tab -->
    <div class="tab-pane fade show active" id="tab-zones">
        <div class="row g-3">
            <?php foreach ($zones as $z): ?>
            <div class="col-md-4">
                <div class="card h-100">
                    <div class="card-body text-center">
                        <i class="fas fa-parking fa-2x text-primary mb-2"></i>
                        <h5><?= e($z['name_en']) ?> <small class="text-muted">(<?= e($z['zone_code']) ?>)</small></h5>
                        <div class="row g-1 mt-2">
                            <div class="col-4"><div class="border rounded p-2"><strong><?= $z['total_spots'] ?></strong><br><small class="text-muted">Total</small></div></div>
                            <div class="col-4"><div class="border rounded p-2"><strong><?= $z['reserved_spots'] ?></strong><br><small class="text-muted">Reserved</small></div></div>
                            <div class="col-4"><div class="border rounded p-2"><strong><?= $z['vip_spots'] ?></strong><br><small class="text-muted">VIP</small></div></div>
                        </div>
                    </div>
                </div>
            </div>
            <?php endforeach; ?>
        </div>
    </div>

    <!-- Permits Tab -->
    <div class="tab-pane fade" id="tab-permits">
        <?php if (auth_can('facilities.manage')): ?>
        <div class="mb-3 text-end"><button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#permitModal"><i class="fas fa-plus me-1"></i>Issue Permit</button></div>
        <?php endif; ?>
        <div class="card"><div class="card-body">
            <table id="permits-table" class="table table-striped w-100"
                   data-url="/api/facilities/parking/permits"
                   data-columns='<?= json_encode([
                       ["data"=>"id","title"=>"#","width"=>"50px"],
                       ["data"=>"number","title"=>"Permit #"],
                       ["data"=>"member","title"=>"Member"],
                       ["data"=>"plate","title"=>"Plate"],
                       ["data"=>"vehicle","title"=>"Vehicle"],
                       ["data"=>"zone","title"=>"Zone"],
                       ["data"=>"type","title"=>"Type"],
                       ["data"=>"dates","title"=>"Validity"],
                       ["data"=>"status","title"=>"Status"],
                   ]) ?>'>
                <thead><tr><th>#</th><th>Permit #</th><th>Member</th><th>Plate</th><th>Vehicle</th><th>Zone</th><th>Type</th><th>Validity</th><th>Status</th></tr></thead>
            </table>
        </div></div>
    </div>

    <!-- Log Tab -->
    <div class="tab-pane fade" id="tab-log">
        <div class="mb-3"><label class="form-label">Date</label><input type="date" id="log-date" class="form-control" style="max-width:200px" value="<?= date('Y-m-d') ?>"></div>
        <div class="card"><div class="card-body">
            <table id="parking-log-table" class="table table-striped w-100"
                   data-url="/api/facilities/parking/log"
                   data-columns='<?= json_encode([
                       ["data"=>"id","title"=>"#","width"=>"50px"],
                       ["data"=>"time","title"=>"Time"],
                       ["data"=>"direction","title"=>"Direction"],
                       ["data"=>"plate","title"=>"Plate"],
                       ["data"=>"member","title"=>"Member"],
                       ["data"=>"zone","title"=>"Zone"],
                       ["data"=>"authorized","title"=>"Auth","className"=>"text-center"],
                   ]) ?>'>
                <thead><tr><th>#</th><th>Time</th><th>Direction</th><th>Plate</th><th>Member</th><th>Zone</th><th>Auth</th></tr></thead>
            </table>
        </div></div>
    </div>
</div>

<!-- Issue Permit Modal -->
<div class="modal fade" id="permitModal" tabindex="-1"><div class="modal-dialog modal-lg"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Issue Parking Permit</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form method="POST" action="/facilities/parking/permits" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-6"><label class="form-label">Member <span class="text-danger">*</span></label><select name="member_id" class="form-select select2" data-placeholder="Search member..." required></select></div>
                <div class="col-md-6"><label class="form-label">Zone</label><select name="zone_id" class="form-select"><option value="">Any</option><?php foreach ($zones as $z): ?><option value="<?= $z['id'] ?>"><?= e($z['name_en']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Vehicle Plate <span class="text-danger">*</span></label><input type="text" name="vehicle_plate" class="form-control" required></div>
                <div class="col-md-4"><label class="form-label">Make</label><input type="text" name="vehicle_make" class="form-control"></div>
                <div class="col-md-4"><label class="form-label">Model</label><input type="text" name="vehicle_model" class="form-control"></div>
                <div class="col-md-4"><label class="form-label">Color</label><input type="text" name="vehicle_color" class="form-control"></div>
                <div class="col-md-4"><label class="form-label">Permit Type <span class="text-danger">*</span></label><select name="permit_type" class="form-select" required><option value="annual">Annual</option><option value="seasonal">Seasonal</option><option value="monthly">Monthly</option><option value="daily">Daily</option><option value="vip">VIP</option><option value="disabled">Disabled</option></select></div>
                <div class="col-md-4"><label class="form-label">Start Date <span class="text-danger">*</span></label><input type="date" name="start_date" class="form-control" value="<?= date('Y-m-d') ?>" required></div>
                <div class="col-md-4"><label class="form-label">End Date</label><input type="date" name="end_date" class="form-control"></div>
            </div>
            <div class="mt-3"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Issue Permit</button></div>
        </form>
    </div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('permits-table');

    var logTable = DataTableHelper.init('parking-log-table', {
        ajax: {
            url: '/api/facilities/parking/log',
            data: function(d) { d.date = document.getElementById('log-date').value; return d; }
        }
    });

    document.getElementById('log-date').addEventListener('change', function() { DataTableHelper.reload('parking-log-table'); });

    $('#permitModal select.select2').select2({
        theme: 'bootstrap-5', width: '100%', dropdownParent: $('#permitModal'), minimumInputLength: 2,
        ajax: { url: '/api/members/search', dataType: 'json', delay: 300, data: function(p) { return { q: p.term }; }, processResults: function(d) { return { results: d.map(m => ({ id: m.id, text: m.membership_number + ' - ' + m.text })) }; } }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\partials\_facility_form.php

```php
<?php $f = $facility ?? []; ?>
<div class="row g-3">
    <?php if (empty($f)): ?>
    <div class="col-md-4">
        <label class="form-label"><?= lang('facilities.code') ?> <span class="text-danger">*</span></label>
        <input type="text" name="facility_code" class="form-control" value="" required maxlength="20" placeholder="e.g. COURT-01">
        <div class="invalid-feedback" data-field="facility_code"></div>
    </div>
    <?php endif; ?>
    <div class="col-md-4">
        <label class="form-label"><?= lang('facilities.type') ?> <span class="text-danger">*</span></label>
        <select name="facility_type_id" class="form-select" required>
            <option value=""><?= lang('common.search') ?>...</option>
            <?php foreach ($facilityTypes as $ft): ?>
            <option value="<?= $ft['id'] ?>" <?= selected($ft['id'], $f['facility_type_id'] ?? '') ?>><?= e($ft['name_en']) ?> - <?= e($ft['name_ar']) ?></option>
            <?php endforeach; ?>
        </select>
        <div class="invalid-feedback" data-field="facility_type_id"></div>
    </div>
    <div class="col-md-4">
        <label class="form-label"><?= lang('common.status') ?></label>
        <select name="status" class="form-select">
            <option value="active" <?= selected('active', $f['status'] ?? 'active') ?>>Active</option>
            <option value="under_maintenance" <?= selected('under_maintenance', $f['status'] ?? '') ?>>Under Maintenance</option>
            <option value="closed" <?= selected('closed', $f['status'] ?? '') ?>>Closed</option>
            <option value="reserved" <?= selected('reserved', $f['status'] ?? '') ?>>Reserved</option>
        </select>
    </div>
    <div class="col-md-6">
        <label class="form-label"><?= lang('facilities.name') ?> (EN) <span class="text-danger">*</span></label>
        <input type="text" name="name_en" class="form-control" value="<?= e($f['name_en'] ?? '') ?>" required>
        <div class="invalid-feedback" data-field="name_en"></div>
    </div>
    <div class="col-md-6">
        <label class="form-label"><?= lang('facilities.name') ?> (AR) <span class="text-danger">*</span></label>
        <input type="text" name="name_ar" class="form-control" value="<?= e($f['name_ar'] ?? '') ?>" required dir="rtl">
        <div class="invalid-feedback" data-field="name_ar"></div>
    </div>
    <div class="col-md-6">
        <label class="form-label"><?= lang('facilities.location') ?></label>
        <input type="text" name="location" class="form-control" value="<?= e($f['location'] ?? '') ?>">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.capacity') ?></label>
        <input type="number" name="capacity" class="form-control" value="<?= e($f['capacity'] ?? '') ?>" min="0">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.area_sqm') ?></label>
        <input type="number" name="area_sqm" class="form-control" value="<?= e($f['area_sqm'] ?? '') ?>" step="0.01" min="0">
    </div>
    <div class="col-12"><hr></div>
    <div class="col-md-3">
        <div class="form-check form-switch mt-3">
            <input type="hidden" name="is_indoor" value="0">
            <input class="form-check-input" type="checkbox" name="is_indoor" value="1" id="is_indoor" <?= checked(1, $f['is_indoor'] ?? 1) ?>>
            <label class="form-check-label" for="is_indoor"><?= lang('facilities.indoor') ?></label>
        </div>
    </div>
    <div class="col-md-3">
        <div class="form-check form-switch mt-3">
            <input type="hidden" name="is_bookable" value="0">
            <input class="form-check-input" type="checkbox" name="is_bookable" value="1" id="is_bookable" <?= checked(1, $f['is_bookable'] ?? 1) ?>>
            <label class="form-check-label" for="is_bookable"><?= lang('facilities.bookable') ?></label>
        </div>
    </div>
    <div class="col-12"><hr><h6><?= lang('facilities.booking_settings') ?></h6></div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.fee_per_hour') ?></label>
        <input type="number" name="booking_fee_per_hour" class="form-control" value="<?= e($f['booking_fee_per_hour'] ?? '0') ?>" step="0.01" min="0">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.fee_members') ?></label>
        <input type="number" name="booking_fee_members" class="form-control" value="<?= e($f['booking_fee_members'] ?? '0') ?>" step="0.01" min="0">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.fee_guests') ?></label>
        <input type="number" name="booking_fee_guests" class="form-control" value="<?= e($f['booking_fee_guests'] ?? '0') ?>" step="0.01" min="0">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.advance_days') ?></label>
        <input type="number" name="advance_booking_days" class="form-control" value="<?= e($f['advance_booking_days'] ?? '7') ?>" min="1">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.min_minutes') ?></label>
        <input type="number" name="min_booking_minutes" class="form-control" value="<?= e($f['min_booking_minutes'] ?? '60') ?>" min="15" step="15">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.max_minutes') ?></label>
        <input type="number" name="max_booking_minutes" class="form-control" value="<?= e($f['max_booking_minutes'] ?? '120') ?>" min="15" step="15">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.operating_start') ?></label>
        <input type="time" name="operating_hours_start" class="form-control" value="<?= e($f['operating_hours_start'] ?? '06:00') ?>">
    </div>
    <div class="col-md-3">
        <label class="form-label"><?= lang('facilities.operating_end') ?></label>
        <input type="time" name="operating_hours_end" class="form-control" value="<?= e($f['operating_hours_end'] ?? '22:00') ?>">
    </div>
</div>
```

## File: modules\facilities\views\pools\index.php

```php
<div class="row g-3 mb-4">
    <?php foreach ($pools as $pool): ?>
    <div class="col-md-6">
        <div class="card h-100">
            <div class="card-header d-flex justify-content-between">
                <h6 class="mb-0"><?= e($pool['facility_name_en']) ?> <small class="text-muted">(<?= e($pool['facility_code']) ?>)</small></h6>
                <span class="badge bg-<?= $pool['is_active'] ? 'success' : 'danger' ?>"><?= $pool['is_active'] ? 'Active' : 'Inactive' ?></span>
            </div>
            <div class="card-body">
                <div class="row g-2 mb-3">
                    <div class="col-6"><strong>Type:</strong> <?= e(ucfirst(str_replace('_', ' ', $pool['pool_type']))) ?></div>
                    <div class="col-6"><strong>Max Capacity:</strong> <?= $pool['max_capacity'] ?></div>
                    <div class="col-6"><strong>Size:</strong> <?= $pool['length_m'] ?>m Ã— <?= $pool['width_m'] ?>m</div>
                    <div class="col-6"><strong>Depth:</strong> <?= $pool['depth_min_m'] ?>m - <?= $pool['depth_max_m'] ?>m</div>
                    <div class="col-6"><strong>Lanes:</strong> <?= $pool['lanes'] ?? '-' ?></div>
                    <div class="col-6"><strong>Heated:</strong> <?= $pool['heated'] ? 'Yes' : 'No' ?></div>
                    <div class="col-6"><strong>Water:</strong> <?= e(ucfirst($pool['water_type'])) ?></div>
                    <div class="col-6"><strong>Temp:</strong> <?= $pool['temperature_celsius'] ? $pool['temperature_celsius'] . 'Â°C' : '-' ?></div>
                </div>

                <h6>Recent Readings</h6>
                <div class="table-responsive">
                    <table id="pool-readings-<?= $pool['id'] ?>" class="table table-sm table-striped w-100"
                           data-url="/api/facilities/pools/readings/<?= $pool['id'] ?>"
                           data-columns='<?= json_encode([
                               ["data"=>"date","title"=>"Date"],
                               ["data"=>"time","title"=>"Time"],
                               ["data"=>"ph","title"=>"pH"],
                               ["data"=>"chlorine","title"=>"Clâ‚‚"],
                               ["data"=>"temperature","title"=>"Temp"],
                               ["data"=>"compliant","title"=>"Status"],
                           ]) ?>'>
                        <thead><tr><th>Date</th><th>Time</th><th>pH</th><th>Clâ‚‚</th><th>Temp</th><th>Status</th></tr></thead>
                    </table>
                </div>
                <?php if (auth_can('facilities.manage')): ?>
                <button class="btn btn-sm btn-outline-primary mt-2 btn-add-reading" data-pool-id="<?= $pool['id'] ?>" data-pool-name="<?= e($pool['facility_name_en']) ?>">
                    <i class="fas fa-plus me-1"></i>Add Reading
                </button>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <?php endforeach; ?>
</div>

<!-- Add Reading Modal -->
<div class="modal fade" id="readingModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Add Pool Reading: <span id="reading-pool-name"></span></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form method="POST" action="/facilities/pools/readings" data-ajax="true" id="reading-form">
            <?= csrf_field() ?>
            <input type="hidden" name="pool_id" id="reading-pool-id">
            <div class="row g-3">
                <div class="col-6"><label class="form-label">Date <span class="text-danger">*</span></label><input type="date" name="reading_date" class="form-control" value="<?= date('Y-m-d') ?>" required></div>
                <div class="col-6"><label class="form-label">Time <span class="text-danger">*</span></label><input type="time" name="reading_time" class="form-control" value="<?= date('H:i') ?>" required></div>
                <div class="col-6"><label class="form-label">pH Level (7.2-7.8)</label><input type="number" name="ph_level" class="form-control" step="0.01" min="0" max="14"></div>
                <div class="col-6"><label class="form-label">Chlorine (1.0-3.0 ppm)</label><input type="number" name="chlorine_level" class="form-control" step="0.001" min="0"></div>
                <div class="col-6"><label class="form-label">Temperature (Â°C)</label><input type="number" name="temperature_celsius" class="form-control" step="0.1" min="0"></div>
                <div class="col-6"><label class="form-label">Turbidity (NTU)</label><input type="number" name="turbidity" class="form-control" step="0.01" min="0"></div>
                <div class="col-12"><label class="form-label">Action Taken</label><textarea name="action_taken" class="form-control" rows="2"></textarea></div>
            </div>
            <div class="mt-3"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save Reading</button></div>
        </form>
    </div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    <?php foreach ($pools as $pool): ?>
    DataTableHelper.init('pool-readings-<?= $pool['id'] ?>', { pageLength: 5, ordering: false, searching: false, info: false });
    <?php endforeach; ?>

    document.querySelectorAll('.btn-add-reading').forEach(btn => {
        btn.addEventListener('click', function() {
            document.getElementById('reading-pool-id').value = this.dataset.poolId;
            document.getElementById('reading-pool-name').textContent = this.dataset.poolName;
            new bootstrap.Modal(document.getElementById('readingModal')).show();
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h3 class="mb-0"><?= e($facility['name_en']) ?> <small class="text-muted">(<?= e($facility['facility_code']) ?>)</small></h3>
    <div class="d-flex gap-2">
        <?php if (auth_can('facilities.manage')): ?>
        <a href="/facilities/<?= $facility['id'] ?>/edit" class="btn btn-warning"><i class="fas fa-edit me-1"></i><?= lang('common.edit') ?></a>
        <a href="/facilities/<?= $facility['id'] ?>/maintenance" class="btn btn-secondary"><i class="fas fa-tools me-1"></i><?= lang('facilities.maintenance') ?></a>
        <?php endif; ?>
        <?php if ($facility['is_bookable'] && auth_can('bookings.view')): ?>
        <a href="/bookings/calendar/<?= $facility['id'] ?>" class="btn btn-primary"><i class="fas fa-calendar me-1"></i><?= lang('facilities.booking_calendar') ?></a>
        <?php endif; ?>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-8">
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0"><?= lang('common.details') ?></h6></div>
            <div class="card-body">
                <div class="row g-2">
                    <div class="col-md-6"><strong><?= lang('facilities.type') ?>:</strong> <?= e($facility['type_name_en'] ?? '-') ?></div>
                    <div class="col-md-6"><strong><?= lang('common.status') ?>:</strong> <?= status_badge($facility['status']) ?></div>
                    <div class="col-md-6"><strong><?= lang('facilities.name') ?> (AR):</strong> <?= e($facility['name_ar']) ?></div>
                    <div class="col-md-6"><strong><?= lang('facilities.location') ?>:</strong> <?= e($facility['location'] ?? '-') ?></div>
                    <div class="col-md-4"><strong><?= lang('facilities.capacity') ?>:</strong> <?= $facility['capacity'] ? number_format($facility['capacity']) : '-' ?></div>
                    <div class="col-md-4"><strong><?= lang('facilities.area_sqm') ?>:</strong> <?= $facility['area_sqm'] ? number_format($facility['area_sqm'], 2) . ' mÂ²' : '-' ?></div>
                    <div class="col-md-4"><strong><?= lang('facilities.indoor') ?>:</strong> <?= $facility['is_indoor'] ? 'âœ“ Yes' : 'âœ— No' ?></div>
                    <div class="col-md-4"><strong><?= lang('facilities.bookable') ?>:</strong> <?= $facility['is_bookable'] ? 'âœ“ Yes' : 'âœ— No' ?></div>
                    <div class="col-md-4"><strong><?= lang('facilities.fee_members') ?>:</strong> <?= format_money($facility['booking_fee_members']) ?>/hr</div>
                    <div class="col-md-4"><strong><?= lang('facilities.operating_start') ?>:</strong> <?= e($facility['operating_hours_start'] ?? '-') ?> - <?= e($facility['operating_hours_end'] ?? '-') ?></div>
                </div>
            </div>
        </div>

        <?php if (!empty($amenities)): ?>
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0"><?= lang('facilities.amenities') ?></h6></div>
            <div class="card-body">
                <div class="d-flex flex-wrap gap-2">
                    <?php foreach ($amenities as $a): ?>
                    <span class="badge <?= $a['is_available'] ? 'bg-success' : 'bg-secondary' ?>">
                        <?= e($a['amenity_name_en']) ?>
                    </span>
                    <?php endforeach; ?>
                </div>
            </div>
        </div>
        <?php endif; ?>

        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('facilities.upcoming_bookings') ?></h6></div>
            <div class="card-body p-0">
                <?php if (empty($recentBookings)): ?>
                <div class="text-center text-muted py-4"><?= lang('common.no_data') ?></div>
                <?php else: ?>
                <div class="table-responsive">
                    <table class="table table-sm table-hover mb-0">
                        <thead><tr><th>Date</th><th>Time</th><th>Member</th><th>Status</th></tr></thead>
                        <tbody>
                            <?php foreach ($recentBookings as $b): ?>
                            <tr>
                                <td><?= e($b['booking_date']) ?></td>
                                <td><?= e($b['start_time']) ?> - <?= e($b['end_time']) ?></td>
                                <td><?= e($b['member_name'] ?? '-') ?> <small class="text-muted"><?= e($b['membership_number'] ?? '') ?></small></td>
                                <td><?= status_badge($b['status']) ?></td>
                            </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>

    <div class="col-md-4">
        <?php if (!empty($timeSlots)): ?>
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0"><?= lang('facilities.time_slots') ?></h6></div>
            <div class="card-body p-0">
                <?php
                $dayNames = ['','Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
                ?>
                <table class="table table-sm mb-0">
                    <thead><tr><th>Day</th><th>Time</th><th>Type</th></tr></thead>
                    <tbody>
                        <?php foreach ($timeSlots as $ts): ?>
                        <tr>
                            <td><?= $dayNames[$ts['day_of_week']] ?? $ts['day_of_week'] ?></td>
                            <td><?= e($ts['start_time']) ?>-<?= e($ts['end_time']) ?></td>
                            <td><span class="badge bg-secondary"><?= e($ts['slot_type']) ?></span></td>
                        </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>

        <?php if (!empty($maintenanceRecords)): ?>
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('facilities.recent_maintenance') ?></h6></div>
            <div class="card-body p-0">
                <div class="list-group list-group-flush">
                    <?php foreach ($maintenanceRecords as $mr): ?>
                    <div class="list-group-item">
                        <div class="d-flex justify-content-between">
                            <strong><?= e($mr['title']) ?></strong>
                            <?= status_badge($mr['status']) ?>
                        </div>
                        <small class="text-muted"><?= e($mr['start_date']) ?> â€¢ <?= e(ucfirst($mr['maintenance_type'])) ?></small>
                    </div>
                    <?php endforeach; ?>
                </div>
            </div>
        </div>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\facilities\views\types\form.php

```php
<?php $t = $type ?? null; ?>
<div class="row justify-content-center">
    <div class="col-lg-6">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><?= $t ? lang('facilities.edit_type') : lang('facilities.create_type') ?></h5></div>
            <div class="card-body">
                <form method="POST" action="<?= $t ? "/facilities/types/{$t['id']}" : '/facilities/types' ?>" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label">Type Code <span class="text-danger">*</span></label>
                        <input type="text" name="type_code" class="form-control" value="<?= e($t['type_code'] ?? '') ?>" <?= $t ? 'readonly' : 'required' ?> maxlength="20">
                        <div class="invalid-feedback" data-field="type_code"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                        <input type="text" name="name_en" class="form-control" value="<?= e($t['name_en'] ?? '') ?>" required>
                        <div class="invalid-feedback" data-field="name_en"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                        <input type="text" name="name_ar" class="form-control" value="<?= e($t['name_ar'] ?? '') ?>" required dir="rtl">
                        <div class="invalid-feedback" data-field="name_ar"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Icon (FontAwesome class)</label>
                        <input type="text" name="icon" class="form-control" value="<?= e($t['icon'] ?? '') ?>" placeholder="e.g. fas fa-swimming-pool">
                    </div>
                    <div class="mb-3">
                        <div class="form-check form-switch">
                            <input type="hidden" name="is_bookable" value="0">
                            <input class="form-check-input" type="checkbox" name="is_bookable" value="1" id="type_bookable" <?= checked(1, $t['is_bookable'] ?? 1) ?>>
                            <label class="form-check-label" for="type_bookable">Bookable</label>
                        </div>
                    </div>
                    <?php if ($t): ?>
                    <div class="mb-3">
                        <div class="form-check form-switch">
                            <input type="hidden" name="is_active" value="0">
                            <input class="form-check-input" type="checkbox" name="is_active" value="1" id="type_active" <?= checked(1, $t['is_active'] ?? 1) ?>>
                            <label class="form-check-label" for="type_active">Active</label>
                        </div>
                    </div>
                    <?php endif; ?>
                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/facilities/types" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\facilities\views\types\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4><?= lang('facilities.types') ?></h4>
    <a href="/facilities/types/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('facilities.create_type') ?></a>
</div>
<div class="card">
    <div class="card-body">
        <table id="types-table" class="table table-striped table-hover w-100"
               data-url="/api/facilities/types/list"
               data-columns='<?= json_encode([
                   ["data"=>"id","title"=>"#","width"=>"50px"],
                   ["data"=>"code","title"=>"Code"],
                   ["data"=>"name_en","title"=>"Name (EN)"],
                   ["data"=>"name_ar","title"=>"Name (AR)"],
                   ["data"=>"bookable","title"=>"Bookable","className"=>"text-center"],
                   ["data"=>"active","title"=>"Active"],
                   ["data"=>"facilities","title"=>"Facilities"],
                   ["data"=>"actions","title"=>"Actions","orderable"=>false,"searchable"=>false],
               ]) ?>'>
            <thead><tr><th>#</th><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Bookable</th><th>Active</th><th>Facilities</th><th>Actions</th></tr></thead>
        </table>
    </div>
</div>
<?php $__view->push('scripts') ?>
<script>document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('types-table'); });</script>
<?php $__view->endPush() ?>
```

## File: modules\facilities\views\widgets\facility_status.php

```php
<?php
$db = Database::getInstance();
$active = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE status = 'active' AND is_active = 1");
$maintenance = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE status = 'under_maintenance'");
$closed = (int) $db->fetchColumn("SELECT COUNT(*) FROM facilities WHERE status = 'closed'");
?>
<div class="card h-100">
    <div class="card-header bg-transparent"><h6 class="mb-0"><i class="fas fa-building me-1 text-success"></i> <?= lang('facilities.facility_status_title') ?></h6></div>
    <div class="card-body">
        <div class="d-flex justify-content-around text-center">
            <div><h4 class="text-success mb-0"><?= $active ?></h4><small class="text-muted">Active</small></div>
            <div><h4 class="text-warning mb-0"><?= $maintenance ?></h4><small class="text-muted">Maintenance</small></div>
            <div><h4 class="text-danger mb-0"><?= $closed ?></h4><small class="text-muted">Closed</small></div>
        </div>
    </div>
    <div class="card-footer bg-transparent border-0 pt-0"><a href="/facilities" class="small text-primary">Manage facilities â†’</a></div>
</div>
```

## File: modules\facilities\views\widgets\todays_bookings.php

```php
<?php
$db = Database::getInstance();
$todayBookings = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_bookings WHERE booking_date = CURDATE() AND status NOT IN ('cancelled','no_show')");
$checkedIn = (int) $db->fetchColumn("SELECT COUNT(*) FROM facility_bookings WHERE booking_date = CURDATE() AND status = 'checked_in'");
?>
<div class="card border-primary h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('facilities.today_bookings') ?></h6>
                <h3 class="mb-0"><?= $todayBookings ?></h3>
                <small class="text-success"><?= $checkedIn ?> checked in</small>
            </div>
            <div class="align-self-center"><i class="fas fa-calendar-check fa-2x text-primary opacity-50"></i></div>
        </div>
    </div>
    <div class="card-footer bg-transparent border-0 pt-0"><a href="/bookings" class="small text-primary">View all bookings â†’</a></div>
</div>
```

## File: modules\facilities\views\widgets\upcoming_events.php

```php
<?php
$db = Database::getInstance();
$upcoming = $db->fetchAll(
    "SELECT event_code, title_en, start_datetime, current_attendees, max_attendees, status
     FROM club_events WHERE start_datetime >= NOW() AND status NOT IN ('cancelled','completed')
     ORDER BY start_datetime LIMIT 3"
);
?>
<div class="card h-100">
    <div class="card-header bg-transparent d-flex justify-content-between align-items-center">
        <h6 class="mb-0"><i class="fas fa-calendar-alt me-1 text-info"></i> <?= lang('facilities.upcoming_events') ?></h6>
        <a href="/events" class="small">View all</a>
    </div>
    <div class="card-body p-0">
        <?php if (empty($upcoming)): ?>
        <div class="text-center text-muted py-3">No upcoming events</div>
        <?php else: ?>
        <div class="list-group list-group-flush">
            <?php foreach ($upcoming as $ev): ?>
            <div class="list-group-item">
                <div class="d-flex justify-content-between align-items-center">
                    <div>
                        <strong><?= e($ev['title_en']) ?></strong><br>
                        <small class="text-muted"><i class="fas fa-clock me-1"></i><?= format_datetime($ev['start_datetime']) ?></small>
                    </div>
                    <span class="badge bg-info"><?= $ev['current_attendees'] ?><?= $ev['max_attendees'] ? '/' . $ev['max_attendees'] : '' ?></span>
                </div>
            </div>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\finance\Controllers\CashRegisterController.php

```php
<?php
class CashRegisterController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $registers = $db->fetchAll(
            "SELECT cr.*, crs.id AS session_id, crs.status AS session_status,
                    crs.opening_balance, crs.opened_at, crs.total_cash_in, crs.total_transactions,
                    u.full_name_en AS operator_name
             FROM cash_registers cr
             LEFT JOIN cash_register_sessions crs ON cr.id=crs.register_id AND crs.status='open'
             LEFT JOIN auth_users u ON crs.user_id=u.id
             WHERE cr.is_active=1
             ORDER BY cr.id"
        );

        // Current user's active session
        $mySession = $db->fetchOne(
            "SELECT crs.*, cr.name_en AS register_name
             FROM cash_register_sessions crs
             JOIN cash_registers cr ON crs.register_id=cr.id
             WHERE crs.user_id=? AND crs.status='open'",
            [Auth::id()]
        );

        $sessionTransactions = [];
        if ($mySession) {
            $sessionTransactions = $db->fetchAll(
                "SELECT crt.*, p.payment_number
                 FROM cash_register_transactions crt
                 LEFT JOIN payments p ON crt.reference_type='payment' AND crt.reference_id=p.id
                 WHERE crt.session_id=?
                 ORDER BY crt.created_at DESC
                 LIMIT 50",
                [$mySession['id']]
            );
        }

        $this->view('cashier/index', [
            'title' => lang('finance.cash_register'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.cash_register')],
            ],
            'registers'    => $registers,
            'mySession'    => $mySession,
            'transactions' => $sessionTransactions,
        ]);
    }

    public function open(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $registerId     = (int)$request->post('register_id');
        $openingBalance = round((float)$request->post('opening_balance', 0), 4);

        $db = $this->db();
        $db->execute("CALL sp_open_cash_register_session(?, ?, ?, @sess_id, @result)", [
            $registerId, Auth::id(), $openingBalance
        ]);
        $result = $db->fetchOne("SELECT @sess_id AS session_id, @result AS msg");

        if (!$result['session_id']) {
            $this->json(['success' => false, 'message' => $result['msg']], 422);
            return;
        }

        AuditLog::log('create', 'finance', 'cash_register_session', (int)$result['session_id'], 'Opened cash register session');
        $this->json(['success' => true, 'message' => $result['msg'], 'redirect' => '/finance/cashier']);
    }

    public function close(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $sessionId      = (int)$request->post('session_id');
        $closingBalance = round((float)$request->post('closing_balance', 0), 4);

        $db = $this->db();
        $db->execute("CALL sp_close_cash_register_session(?, ?, ?, @close_result)", [
            $sessionId, $closingBalance, Auth::id()
        ]);
        $result = $db->fetchOne("SELECT @close_result AS msg");

        AuditLog::log('update', 'finance', 'cash_register_session', $sessionId, 'Closed cash register session');
        $this->json(['success' => true, 'message' => $result['msg'], 'redirect' => '/finance/cashier']);
    }

    public function sessions(Request $request): void
    {
        $this->view('cashier/sessions', [
            'title' => lang('finance.session_history'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.cash_register'), 'url' => '/finance/cashier'],
                ['label' => lang('finance.session_history')],
            ],
        ]);
    }

    public function sessionsDatatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);

        $totalRecords = (int)$db->fetchColumn("SELECT COUNT(*) FROM cash_register_sessions");

        $rows = $db->fetchAll(
            "SELECT crs.*, cr.name_en AS register_name, u.full_name_en AS operator_name
             FROM cash_register_sessions crs
             JOIN cash_registers cr ON crs.register_id=cr.id
             JOIN auth_users u ON crs.user_id=u.id
             ORDER BY crs.id DESC
             LIMIT {$length} OFFSET {$start}"
        );

        $data = [];
        foreach ($rows as $row) {
            $data[] = [
                'id'              => $row['id'],
                'register_name'   => e($row['register_name']),
                'operator_name'   => e($row['operator_name']),
                'session_date'    => format_date($row['session_date']),
                'opening_balance' => money($row['opening_balance']),
                'closing_balance' => $row['closing_balance'] !== null ? money($row['closing_balance']) : '-',
                'variance'        => $row['variance'] !== null ? money($row['variance']) : '-',
                'total_transactions' => $row['total_transactions'],
                'status'          => status_badge($row['status']),
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalRecords,
            'recordsFiltered' => $totalRecords,
            'data'            => $data,
        ]);
    }
}
```

## File: modules\finance\Controllers\FeeTypeController.php

```php
<?php
class FeeTypeController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('fee-types/index', [
            'title' => lang('finance.fee_types'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.fee_types')],
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM fee_types");
        $rows  = $db->fetchAll("SELECT * FROM fee_types ORDER BY sort_order LIMIT {$length} OFFSET {$start}");

        $data = [];
        foreach ($rows as $row) {
            $data[] = [
                'id'           => $row['id'],
                'fee_code'     => e($row['fee_code']),
                'name_en'      => e($row['name_en']),
                'name_ar'      => e($row['name_ar']),
                'category'     => e(ucfirst(str_replace('_', ' ', $row['category']))),
                'is_recurring' => $row['is_recurring'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-muted"></i>',
                'is_active'    => $row['is_active'] ? status_badge('active') : status_badge('inactive'),
                'actions'      => '<button class="btn btn-sm btn-outline-primary btn-edit-fee" data-id="'.$row['id'].'" data-fee=\''.e(json_encode($row)).'\'><i class="fas fa-edit"></i></button>',
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'fee_code' => 'required|string|max:30|unique:fee_types,fee_code',
            'name_en'  => 'required|string|max:200',
            'name_ar'  => 'required|string|max:200',
            'category' => 'required|in:registration,subscription,annual,transfer,freeze,card,carnet,penalty,sport,facility,guest,other',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_recurring']   = (int)$request->post('is_recurring', 0);
        $data['is_refundable']  = (int)$request->post('is_refundable', 0);
        $data['tax_applicable'] = (int)$request->post('tax_applicable', 0);
        $data['is_active']      = (int)$request->post('is_active', 1);
        $data['sort_order']     = (int)$request->post('sort_order', 0);

        $id = $this->db()->insert('fee_types', $data);
        AuditLog::log('create', 'finance', 'fee_type', $id, "Created fee type: {$data['fee_code']}");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');

        $validator = new Validator($request->all(), [
            'name_en'  => 'required|string|max:200',
            'name_ar'  => 'required|string|max:200',
            'category' => 'required|in:registration,subscription,annual,transfer,freeze,card,carnet,penalty,sport,facility,guest,other',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_recurring']   = (int)$request->post('is_recurring', 0);
        $data['is_refundable']  = (int)$request->post('is_refundable', 0);
        $data['tax_applicable'] = (int)$request->post('tax_applicable', 0);
        $data['is_active']      = (int)$request->post('is_active', 1);
        $data['sort_order']     = (int)$request->post('sort_order', 0);

        $this->db()->update('fee_types', $data, 'id = ?', [$id]);
        AuditLog::log('update', 'finance', 'fee_type', $id, "Updated fee type");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }

    public function listActive(Request $request): void
    {
        $feeTypes = $this->db()->fetchAll("SELECT id, fee_code, name_en, name_ar, category FROM fee_types WHERE is_active=1 ORDER BY sort_order");
        $this->json(['success' => true, 'data' => $feeTypes]);
    }
}
```

## File: modules\finance\Controllers\FiscalYearController.php

```php
<?php
class FiscalYearController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $years = $db->fetchAll("SELECT * FROM fiscal_years ORDER BY start_date DESC");
        $periods = [];
        foreach ($years as $y) {
            $periods[$y['id']] = $db->fetchAll("SELECT * FROM fiscal_periods WHERE fiscal_year_id=? ORDER BY period_number", [$y['id']]);
        }

        $this->view('fiscal-years/index', [
            'title' => lang('finance.fiscal_years'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.fiscal_years')],
            ],
            'years'   => $years,
            'periods' => $periods,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $rows = $db->fetchAll("SELECT * FROM fiscal_years ORDER BY start_date DESC");
        $data = [];
        foreach ($rows as $row) {
            $periodCount = (int)$db->fetchColumn("SELECT COUNT(*) FROM fiscal_periods WHERE fiscal_year_id=?", [$row['id']]);
            $data[] = [
                'id'         => $row['id'],
                'year_code'  => e($row['year_code']),
                'name_en'    => e($row['name_en']),
                'start_date' => format_date($row['start_date']),
                'end_date'   => format_date($row['end_date']),
                'periods'    => $periodCount,
                'status'     => status_badge($row['status']),
            ];
        }
        $this->json(['draw' => 1, 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'year_code'  => 'required|string|max:20|unique:fiscal_years,year_code',
            'name_en'    => 'required|string|max:100',
            'name_ar'    => 'required|string|max:100',
            'start_date' => 'required|date',
            'end_date'   => 'required|date',
            'status'     => 'required|in:planning,open,closed,locked',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $id = $this->db()->insert('fiscal_years', $validator->validated());
        AuditLog::log('create', 'finance', 'fiscal_year', $id, "Created fiscal year");
        $this->json(['success' => true, 'message' => lang('common.success'), 'redirect' => '/finance/fiscal-years']);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'name_en'    => 'required|string|max:100',
            'name_ar'    => 'required|string|max:100',
            'status'     => 'required|in:planning,open,closed,locked',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $this->db()->update('fiscal_years', $validator->validated(), 'id = ?', [$id]);
        AuditLog::log('update', 'finance', 'fiscal_year', $id, "Updated fiscal year");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }

    public function storePeriod(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $yearId = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'period_number' => 'required|integer|min:1|max:12',
            'name_en'       => 'required|string|max:100',
            'name_ar'       => 'required|string|max:100',
            'start_date'    => 'required|date',
            'end_date'      => 'required|date',
            'status'        => 'required|in:open,closed,locked',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['fiscal_year_id'] = $yearId;

        $id = $this->db()->insert('fiscal_periods', $data);
        AuditLog::log('create', 'finance', 'fiscal_period', $id, "Created fiscal period");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }
}
```

## File: modules\finance\Controllers\InvoiceController.php

```php
<?php
class InvoiceController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $totalOutstanding = (float)$db->fetchColumn("SELECT COALESCE(SUM(balance_due),0) FROM invoices WHERE status IN ('issued','partially_paid','overdue') AND is_deleted=0");
        $totalOverdue     = (float)$db->fetchColumn("SELECT COALESCE(SUM(balance_due),0) FROM invoices WHERE status='overdue' AND is_deleted=0");
        $issuedToday      = (int)$db->fetchColumn("SELECT COUNT(*) FROM invoices WHERE DATE(created_at)=CURDATE() AND is_deleted=0");
        $collectedToday   = (float)$db->fetchColumn("SELECT COALESCE(SUM(amount),0) FROM payments WHERE payment_date=CURDATE() AND status='completed' AND is_deleted=0");

        $this->view('invoices/index', [
            'title' => lang('finance.invoices'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.invoices')],
            ],
            'totalOutstanding' => $totalOutstanding,
            'totalOverdue'     => $totalOverdue,
            'issuedToday'      => $issuedToday,
            'collectedToday'   => $collectedToday,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $filterStatus = $request->get('filter_status', '');
        $filterType   = $request->get('filter_type', '');
        $filterFrom   = $request->get('filter_from', '');
        $filterTo     = $request->get('filter_to', '');
        $filterOverdue = $request->get('filter_overdue', '');

        $where = "i.is_deleted=0";
        $params = [];

        if ($filterStatus) {
            $where .= " AND i.status=?";
            $params[] = $filterStatus;
        }
        if ($filterType) {
            $where .= " AND i.invoice_type=?";
            $params[] = $filterType;
        }
        if ($filterFrom) {
            $where .= " AND i.invoice_date >= ?";
            $params[] = $filterFrom;
        }
        if ($filterTo) {
            $where .= " AND i.invoice_date <= ?";
            $params[] = $filterTo;
        }
        if ($filterOverdue === '1') {
            $where .= " AND i.status='overdue'";
        }
        if ($search) {
            $where .= " AND (i.invoice_number LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s]);
        }

        $totalRecords = (int)$db->fetchColumn("SELECT COUNT(*) FROM invoices i WHERE i.is_deleted=0");

        $filteredCount = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM invoices i LEFT JOIN members m ON i.member_id=m.id WHERE {$where}",
            $params
        );

        $orderCol = (int)($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtoupper($request->get('order')[0]['dir'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
        $columns = ['i.id','i.invoice_number','m.full_name_en','i.invoice_type','i.invoice_date','i.due_date','i.total_amount','i.paid_amount','i.balance_due','i.status'];
        $orderColumn = $columns[$orderCol] ?? 'i.id';

        $rows = $db->fetchAll(
            "SELECT i.id, i.invoice_number, i.invoice_type, i.invoice_date, i.due_date,
                    i.total_amount, i.paid_amount, i.balance_due, i.status,
                    m.full_name_en AS member_name, m.membership_number
             FROM invoices i
             LEFT JOIN members m ON i.member_id=m.id
             WHERE {$where}
             ORDER BY {$orderColumn} {$orderDir}
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<a href="/finance/invoices/'.$row['id'].'" class="btn btn-outline-primary" title="'.lang('common.view').'"><i class="fas fa-eye"></i></a>';
            $actions .= '</div>';

            $data[] = [
                'id'             => $row['id'],
                'invoice_number' => e($row['invoice_number']),
                'member_name'    => e($row['member_name'] ?? '') . ' <small class="text-muted">'.e($row['membership_number'] ?? '').'</small>',
                'invoice_type'   => e(ucfirst(str_replace('_',' ',$row['invoice_type']))),
                'invoice_date'   => format_date($row['invoice_date']),
                'due_date'       => format_date($row['due_date']),
                'total_amount'   => money($row['total_amount']),
                'paid_amount'    => money($row['paid_amount']),
                'balance_due'    => money($row['balance_due']),
                'status'         => status_badge($row['status']),
                'actions'        => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalRecords,
            'recordsFiltered' => $filteredCount,
            'data'            => $data,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $feeTypes = $db->fetchAll("SELECT id, fee_code, name_en, name_ar, category FROM fee_types WHERE is_active=1 ORDER BY sort_order");
        $discountTypes = $db->fetchAll("SELECT id, discount_code, name_en, discount_method, value FROM discount_types WHERE is_active=1");

        $this->view('invoices/create', [
            'title' => lang('finance.create_invoice'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.invoices'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.create_invoice')],
            ],
            'feeTypes'      => $feeTypes,
            'discountTypes' => $discountTypes,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'member_id'    => 'required|integer|exists:members,id',
            'invoice_type' => 'required|in:registration,subscription,renewal,transfer,freeze,penalty,sport,facility,guest,card,carnet,other',
            'due_date'     => 'required|date',
            'notes'        => 'nullable|string|max:1000',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $items = $request->post('items');
        if (!is_array($items) || empty($items)) {
            $this->json(['success' => false, 'message' => lang('finance.items_required')], 422);
            return;
        }

        $db = $this->db();
        $db->beginTransaction();

        try {
            $memberId    = (int)$request->post('member_id');
            $invoiceType = $request->post('invoice_type');
            $dueDate     = $request->post('due_date');
            $notes       = $request->post('notes', '');
            $issueNow    = (bool)$request->post('issue_now', false);

            // Call stored procedure to create invoice
            $db->execute("CALL sp_create_invoice(?, ?, ?, ?, @inv_id, @inv_num)", [
                $memberId, $invoiceType, $dueDate, Auth::id()
            ]);
            $result = $db->fetchOne("SELECT @inv_id AS invoice_id, @inv_num AS invoice_number");
            $invoiceId     = (int)$result['invoice_id'];
            $invoiceNumber = $result['invoice_number'];

            if (!$invoiceId) {
                throw new \RuntimeException('Failed to create invoice');
            }

            // Update notes
            if ($notes) {
                $db->update('invoices', ['notes' => $notes], 'id = ?', [$invoiceId]);
            }

            // Add items
            foreach ($items as $item) {
                $feeTypeId   = (int)($item['fee_type_id'] ?? 0);
                $descEn      = trim($item['description_en'] ?? '');
                $descAr      = trim($item['description_ar'] ?? '');
                $qty         = max(1, (int)($item['quantity'] ?? 1));
                $unitPrice   = round((float)($item['unit_price'] ?? 0), 4);
                $discount    = round((float)($item['discount_amount'] ?? 0), 4);
                $taxRate     = round((float)($item['tax_rate'] ?? 0), 2);

                if ($unitPrice <= 0) continue;

                $db->execute("CALL sp_add_invoice_item(?, ?, ?, ?, ?, ?, ?, ?)", [
                    $invoiceId, $feeTypeId ?: null, $descEn, $descAr, $qty, $unitPrice, $discount, $taxRate
                ]);
            }

            // Optionally issue immediately
            if ($issueNow) {
                $db->execute("CALL sp_issue_invoice(?, ?, @issue_result)", [$invoiceId, Auth::id()]);
            }

            $db->commit();

            AuditLog::log('create', 'finance', 'invoice', $invoiceId, "Created invoice {$invoiceNumber}");

            $this->json([
                'success'  => true,
                'message'  => lang('finance.invoice_created'),
                'redirect' => '/finance/invoices/' . $invoiceId,
            ]);

        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Invoice creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => lang('common.error') . ': ' . $e->getMessage()], 500);
        }
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $invoice = $db->fetchOne(
            "SELECT i.*, m.full_name_en AS member_name, m.membership_number, m.primary_phone,
                    m.primary_email, mt.name_en AS membership_type,
                    c.symbol AS currency_symbol, c.currency_code
             FROM invoices i
             LEFT JOIN members m ON i.member_id=m.id
             LEFT JOIN membership_types mt ON m.membership_type_id=mt.id
             LEFT JOIN sys_currencies c ON i.currency_id=c.id
             WHERE i.id=? AND i.is_deleted=0", [$id]
        );

        if (!$invoice) {
            $this->abort(404, 'Invoice not found');
            return;
        }

        $items = $db->fetchAll(
            "SELECT ii.*, ft.name_en AS fee_type_name, ft.fee_code
             FROM invoice_items ii
             LEFT JOIN fee_types ft ON ii.fee_type_id=ft.id
             WHERE ii.invoice_id=?
             ORDER BY ii.sort_order", [$id]
        );

        $payments = $db->fetchAll(
            "SELECT pa.allocated_amount, p.payment_number, p.payment_date, p.status AS payment_status,
                    pm.name_en AS method_name
             FROM payment_allocations pa
             JOIN payments p ON pa.payment_id=p.id
             LEFT JOIN payment_methods pm ON p.payment_method_id=pm.id
             WHERE pa.invoice_id=? AND pa.is_reversed=0
             ORDER BY p.payment_date DESC", [$id]
        );

        $discounts = $db->fetchAll(
            "SELECT id.*, dt.name_en AS discount_type_name
             FROM invoice_discounts id
             LEFT JOIN discount_types dt ON id.discount_type_id=dt.id
             WHERE id.invoice_id=?", [$id]
        );

        $clubName  = Config::get('club_name_en', 'The Club');
        $clubNameAr = Config::get('club_name_ar', 'Ø§Ù„Ù†Ø§Ø¯ÙŠ');
        $clubAddress = Config::get('club_address_en', '');
        $clubPhone = Config::get('club_phone', '');

        $this->view('invoices/show', [
            'title' => lang('finance.invoice') . ' #' . $invoice['invoice_number'],
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.invoices'), 'url' => '/finance/invoices'],
                ['label' => '#' . $invoice['invoice_number']],
            ],
            'invoice'    => $invoice,
            'items'      => $items,
            'payments'   => $payments,
            'discounts'  => $discounts,
            'clubName'   => $clubName,
            'clubNameAr' => $clubNameAr,
            'clubAddress' => $clubAddress,
            'clubPhone'  => $clubPhone,
        ]);
    }

    public function issue(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $db->execute("CALL sp_issue_invoice(?, ?, @result)", [$id, Auth::id()]);
        $result = $db->fetchOne("SELECT @result AS msg");

        AuditLog::log('update', 'finance', 'invoice', $id, 'Issued invoice');
        $this->json(['success' => true, 'message' => $result['msg'] ?? lang('finance.invoice_issued')]);
    }

    public function void(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $id = (int)$request->routeParam('id');
        $reason = trim($request->post('reason', ''));

        if (!$reason) {
            $this->json(['success' => false, 'message' => 'Reason is required'], 422);
            return;
        }

        $db = $this->db();
        $invoice = $db->fetchOne("SELECT id, status, invoice_number, member_id, balance_due FROM invoices WHERE id=? AND is_deleted=0", [$id]);

        if (!$invoice) {
            $this->json(['success' => false, 'message' => 'Invoice not found'], 404);
            return;
        }

        if (!in_array($invoice['status'], ['draft', 'issued'])) {
            $this->json(['success' => false, 'message' => 'Cannot void invoice in status: ' . $invoice['status']], 422);
            return;
        }

        $db->update('invoices', [
            'status'        => 'void',
            'voided_reason' => $reason,
            'voided_by'     => Auth::id(),
            'voided_at'     => date('Y-m-d H:i:s'),
            'status_changed_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        // Reverse member outstanding if was issued
        if ($invoice['status'] !== 'draft' && $invoice['balance_due'] > 0) {
            $db->execute(
                "UPDATE members SET outstanding_balance = GREATEST(outstanding_balance - ?, 0), updated_at = NOW() WHERE id = ?",
                [$invoice['balance_due'], $invoice['member_id']]
            );
        }

        AuditLog::log('update', 'finance', 'invoice', $id, "Voided invoice {$invoice['invoice_number']}: {$reason}");
        $this->json(['success' => true, 'message' => lang('finance.invoice_voided')]);
    }

    public function memberSearch(Request $request): void
    {
        $q = trim($request->get('q', ''));
        $page = max(1, (int)$request->get('page', 1));
        $perPage = 20;
        $offset = ($page - 1) * $perPage;

        if (mb_strlen($q) < 2) {
            $this->json(['results' => [], 'pagination' => ['more' => false]]);
            return;
        }

        $db = $this->db();
        $like = "%{$q}%";

        $members = $db->fetchAll(
            "SELECT id, membership_number, full_name_en, full_name_ar, primary_phone, status, outstanding_balance
             FROM members
             WHERE is_deleted=0 AND (membership_number LIKE ? OR full_name_en LIKE ? OR full_name_ar LIKE ? OR national_id LIKE ? OR primary_phone LIKE ?)
             ORDER BY full_name_en
             LIMIT ? OFFSET ?",
            [$like, $like, $like, $like, $like, $perPage + 1, $offset]
        );

        $hasMore = count($members) > $perPage;
        if ($hasMore) array_pop($members);

        $results = [];
        foreach ($members as $m) {
            $results[] = [
                'id'   => $m['id'],
                'text'  => $m['membership_number'] . ' - ' . $m['full_name_en'] . ($m['full_name_ar'] ? ' (' . $m['full_name_ar'] . ')' : ''),
                'membership_number' => $m['membership_number'],
                'name'              => $m['full_name_en'],
                'phone'             => $m['primary_phone'],
                'status'            => $m['status'],
                'outstanding'       => (float)$m['outstanding_balance'],
            ];
        }

        $this->json(['results' => $results, 'pagination' => ['more' => $hasMore]]);
    }

    public function forMember(Request $request): void
    {
        $memberId = (int)$request->get('member_id');
        if (!$memberId) {
            $this->json(['success' => false, 'data' => []]);
            return;
        }

        $db = $this->db();
        $invoices = $db->fetchAll(
            "SELECT id, invoice_number, invoice_type, total_amount, paid_amount, balance_due, status, due_date
             FROM invoices
             WHERE member_id=? AND status IN ('issued','partially_paid','overdue') AND is_deleted=0 AND balance_due > 0
             ORDER BY due_date ASC",
            [$memberId]
        );

        $this->json(['success' => true, 'data' => $invoices]);
    }
}
```

## File: modules\finance\Controllers\MemberStatementController.php

```php
<?php
class MemberStatementController extends Controller
{
    public function show(Request $request): void
    {
        $memberId = (int)$request->routeParam('id');
        $db = $this->db();

        $member = $db->fetchOne(
            "SELECT m.id, m.membership_number, m.full_name_en, m.full_name_ar, m.primary_phone,
                    m.outstanding_balance, m.credit_balance, m.total_paid,
                    mt.name_en AS membership_type
             FROM members m
             LEFT JOIN membership_types mt ON m.membership_type_id=mt.id
             WHERE m.id=? AND m.is_deleted=0", [$memberId]
        );

        if (!$member) { $this->abort(404); return; }

        $dateFrom = $request->get('date_from', date('Y-01-01'));
        $dateTo   = $request->get('date_to', date('Y-m-d'));

        $ledger = $db->fetchAll(
            "SELECT ml.*, c.symbol AS currency_symbol
             FROM member_ledger ml
             LEFT JOIN sys_currencies c ON ml.currency_id=c.id
             WHERE ml.member_id=? AND ml.transaction_date BETWEEN ? AND ?
             ORDER BY ml.transaction_date, ml.id",
            [$memberId, $dateFrom, $dateTo]
        );

        $balance = $db->fetchOne("SELECT * FROM member_balances WHERE member_id=?", [$memberId]);

        $this->view('statement/index', [
            'title' => lang('finance.member_statement') . ' - ' . $member['full_name_en'],
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.member_statement')],
            ],
            'member'   => $member,
            'ledger'   => $ledger,
            'balance'  => $balance,
            'dateFrom' => $dateFrom,
            'dateTo'   => $dateTo,
        ]);
    }
}
```

## File: modules\finance\Controllers\OverdueReportController.php

```php
<?php
class OverdueReportController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('reports/overdue', [
            'title' => lang('finance.overdue_invoices'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.overdue_invoices')],
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);

        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM vw_overdue_invoices");

        $rows = $db->fetchAll(
            "SELECT * FROM vw_overdue_invoices ORDER BY days_overdue DESC LIMIT {$length} OFFSET {$start}"
        );

        $data = [];
        foreach ($rows as $row) {
            $agingBucket = 'current';
            $daysOverdue = (int)$row['days_overdue'];
            if ($daysOverdue > 120)     $agingBucket = '120+';
            elseif ($daysOverdue > 90)  $agingBucket = '91-120';
            elseif ($daysOverdue > 60)  $agingBucket = '61-90';
            elseif ($daysOverdue > 30)  $agingBucket = '31-60';
            else                        $agingBucket = '1-30';

            $data[] = [
                'invoice_number'    => e($row['invoice_number']),
                'member_name'       => e($row['member_name'] ?? '') . '<br><small class="text-muted">' . e($row['membership_number'] ?? '') . '</small>',
                'invoice_type'      => e(ucfirst(str_replace('_', ' ', $row['invoice_type']))),
                'total_amount'      => money($row['total_amount']),
                'balance_due'       => '<strong class="text-danger">' . money($row['balance_due']) . '</strong>',
                'due_date'          => format_date($row['due_date']),
                'days_overdue'      => '<span class="badge bg-danger">' . $daysOverdue . ' ' . lang('finance.days') . '</span>',
                'aging_bucket'      => '<span class="badge bg-dark">' . $agingBucket . '</span>',
                'actions'           => '<a href="/finance/invoices/'.$row['id'].'" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>',
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }
}
```

## File: modules\finance\Controllers\PaymentController.php

```php
<?php
class PaymentController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $methods = $db->fetchAll("SELECT id, name_en FROM payment_methods WHERE is_active=1 ORDER BY sort_order");

        $this->view('payments/index', [
            'title' => lang('finance.payments'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.payments')],
            ],
            'methods' => $methods,
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $filterMethod = $request->get('filter_method', '');
        $filterStatus = $request->get('filter_status', '');
        $filterFrom   = $request->get('filter_from', '');
        $filterTo     = $request->get('filter_to', '');

        $where = "p.is_deleted=0";
        $params = [];

        if ($filterMethod) { $where .= " AND p.payment_method_id=?"; $params[] = (int)$filterMethod; }
        if ($filterStatus) { $where .= " AND p.status=?"; $params[] = $filterStatus; }
        if ($filterFrom)   { $where .= " AND p.payment_date >= ?"; $params[] = $filterFrom; }
        if ($filterTo)     { $where .= " AND p.payment_date <= ?"; $params[] = $filterTo; }
        if ($search) {
            $where .= " AND (p.payment_number LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s]);
        }

        $totalRecords = (int)$db->fetchColumn("SELECT COUNT(*) FROM payments p WHERE p.is_deleted=0");
        $filteredCount = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM payments p LEFT JOIN members m ON p.member_id=m.id WHERE {$where}", $params
        );

        $orderDir = strtoupper($request->get('order')[0]['dir'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';

        $rows = $db->fetchAll(
            "SELECT p.id, p.payment_number, p.amount, p.payment_date, p.status,
                    m.full_name_en AS member_name, m.membership_number,
                    pm.name_en AS method_name
             FROM payments p
             LEFT JOIN members m ON p.member_id=m.id
             LEFT JOIN payment_methods pm ON p.payment_method_id=pm.id
             WHERE {$where}
             ORDER BY p.id {$orderDir}
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<a href="/finance/payments/'.$row['id'].'" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';
            $data[] = [
                'id'             => $row['id'],
                'payment_number' => e($row['payment_number']),
                'member_name'    => e($row['member_name'] ?? '') . ' <small class="text-muted">'.e($row['membership_number'] ?? '').'</small>',
                'amount'         => money($row['amount']),
                'method_name'    => e($row['method_name'] ?? ''),
                'payment_date'   => format_date($row['payment_date']),
                'status'         => status_badge($row['status']),
                'actions'        => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalRecords,
            'recordsFiltered' => $filteredCount,
            'data'            => $data,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $methods = $db->fetchAll("SELECT id, name_en, method_type, requires_reference FROM payment_methods WHERE is_active=1 ORDER BY sort_order");
        $registers = $db->fetchAll("SELECT cr.id, cr.name_en, crs.id AS session_id FROM cash_registers cr LEFT JOIN cash_register_sessions crs ON cr.id=crs.register_id AND crs.status='open' WHERE cr.is_active=1");

        // Pre-populate member if passed
        $memberId = (int)$request->get('member_id', 0);
        $member = null;
        if ($memberId) {
            $member = $db->fetchOne("SELECT id, membership_number, full_name_en, outstanding_balance FROM members WHERE id=? AND is_deleted=0", [$memberId]);
        }

        $invoiceId = (int)$request->get('invoice_id', 0);
        $invoice = null;
        if ($invoiceId) {
            $invoice = $db->fetchOne("SELECT id, invoice_number, balance_due, member_id FROM invoices WHERE id=? AND is_deleted=0", [$invoiceId]);
        }

        $this->view('payments/create', [
            'title' => lang('finance.record_payment'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.payments'), 'url' => '/finance/payments'],
                ['label' => lang('finance.record_payment')],
            ],
            'methods'   => $methods,
            'registers' => $registers,
            'member'    => $member,
            'invoice'   => $invoice,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'member_id'         => 'required|integer|exists:members,id',
            'payment_method_id' => 'required|integer|exists:payment_methods,id',
            'amount'            => 'required|decimal|min:0.01',
            'payment_date'      => 'required|date',
            'reference_number'  => 'nullable|string|max:100',
            'invoice_id'        => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = $this->db();
        $db->beginTransaction();

        try {
            $memberId   = (int)$request->post('member_id');
            $methodId   = (int)$request->post('payment_method_id');
            $amount     = round((float)$request->post('amount'), 4);
            $payDate    = $request->post('payment_date');
            $refNum     = $request->post('reference_number', '');
            $invoiceId  = (int)$request->post('invoice_id', 0) ?: null;
            $sessionId  = (int)$request->post('cash_register_session_id', 0) ?: null;

            $db->execute("CALL sp_record_payment(?, ?, ?, ?, ?, ?, ?, ?, @pay_id, @pay_num, @pay_result)", [
                $memberId, $methodId, $amount, $payDate, $refNum, $invoiceId, Auth::id(), $sessionId
            ]);

            $result = $db->fetchOne("SELECT @pay_id AS payment_id, @pay_num AS payment_number, @pay_result AS msg");
            $paymentId = (int)$result['payment_id'];
            $paymentNumber = $result['payment_number'];

            if (!$paymentId) {
                throw new \RuntimeException($result['msg'] ?? 'Payment failed');
            }

            // Generate receipt
            $db->execute("CALL sp_generate_receipt(?, ?, @rcpt_id, @rcpt_num)", [$paymentId, Auth::id()]);
            $rcpt = $db->fetchOne("SELECT @rcpt_id AS receipt_id, @rcpt_num AS receipt_number");

            $db->commit();

            AuditLog::log('create', 'finance', 'payment', $paymentId, "Recorded payment {$paymentNumber} for " . money_format($amount));

            $this->json([
                'success'   => true,
                'message'   => lang('finance.payment_recorded') . ($rcpt['receipt_number'] ? ' | ' . lang('finance.receipt') . ': ' . $rcpt['receipt_number'] : ''),
                'redirect'  => '/finance/payments/' . $paymentId,
                'receipt_id' => $rcpt['receipt_id'] ?? null,
            ]);

        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Payment recording failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $payment = $db->fetchOne(
            "SELECT p.*, m.full_name_en AS member_name, m.membership_number,
                    pm.name_en AS method_name, pm.method_type,
                    c.symbol AS currency_symbol
             FROM payments p
             LEFT JOIN members m ON p.member_id=m.id
             LEFT JOIN payment_methods pm ON p.payment_method_id=pm.id
             LEFT JOIN sys_currencies c ON p.currency_id=c.id
             WHERE p.id=? AND p.is_deleted=0", [$id]
        );

        if (!$payment) {
            $this->abort(404, 'Payment not found');
            return;
        }

        $allocations = $db->fetchAll(
            "SELECT pa.allocated_amount, i.invoice_number, i.total_amount, i.balance_due
             FROM payment_allocations pa
             JOIN invoices i ON pa.invoice_id=i.id
             WHERE pa.payment_id=? AND pa.is_reversed=0", [$id]
        );

        $receipt = $db->fetchOne("SELECT id, receipt_number FROM receipts WHERE payment_id=? AND status='issued'", [$id]);

        $this->view('payments/show', [
            'title' => lang('finance.payment') . ' #' . $payment['payment_number'],
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.payments'), 'url' => '/finance/payments'],
                ['label' => '#' . $payment['payment_number']],
            ],
            'payment'     => $payment,
            'allocations' => $allocations,
            'receipt'     => $receipt,
        ]);
    }
}
```

## File: modules\finance\Controllers\PaymentMethodController.php

```php
<?php
class PaymentMethodController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('payment-methods/index', [
            'title' => lang('finance.payment_methods'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.payment_methods')],
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $total = (int)$db->fetchColumn("SELECT COUNT(*) FROM payment_methods");
        $rows  = $db->fetchAll("SELECT * FROM payment_methods ORDER BY sort_order");

        $data = [];
        foreach ($rows as $row) {
            $data[] = [
                'id'          => $row['id'],
                'method_code' => e($row['method_code']),
                'name_en'     => e($row['name_en']),
                'name_ar'     => e($row['name_ar']),
                'method_type' => e(ucfirst(str_replace('_', ' ', $row['method_type']))),
                'is_active'   => $row['is_active'] ? status_badge('active') : status_badge('inactive'),
                'actions'     => '<button class="btn btn-sm btn-outline-primary btn-edit-method" data-id="'.$row['id'].'" data-method=\''.e(json_encode($row)).'\'><i class="fas fa-edit"></i></button>',
            ];
        }

        $this->json(['draw' => (int)$request->get('draw', 1), 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $validator = new Validator($request->all(), [
            'method_code' => 'required|string|max:20|unique:payment_methods,method_code',
            'name_en'     => 'required|string|max:100',
            'name_ar'     => 'required|string|max:100',
            'method_type' => 'required|in:cash,check,bank_transfer,credit_card,debit_card,online,wallet,other',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['requires_reference'] = (int)$request->post('requires_reference', 0);
        $data['is_active'] = (int)$request->post('is_active', 1);
        $data['sort_order'] = (int)$request->post('sort_order', 0);

        $id = $this->db()->insert('payment_methods', $data);
        AuditLog::log('create', 'finance', 'payment_method', $id, "Created payment method: {$data['method_code']}");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int)$request->routeParam('id');
        $validator = new Validator($request->all(), [
            'name_en'     => 'required|string|max:100',
            'name_ar'     => 'required|string|max:100',
            'method_type' => 'required|in:cash,check,bank_transfer,credit_card,debit_card,online,wallet,other',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['requires_reference'] = (int)$request->post('requires_reference', 0);
        $data['is_active'] = (int)$request->post('is_active', 1);
        $data['sort_order'] = (int)$request->post('sort_order', 0);

        $this->db()->update('payment_methods', $data, 'id = ?', [$id]);
        AuditLog::log('update', 'finance', 'payment_method', $id, "Updated payment method");
        $this->json(['success' => true, 'message' => lang('common.success')]);
    }
}
```

## File: modules\finance\Controllers\ReceiptController.php

```php
<?php
class ReceiptController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('receipts/index', [
            'title' => lang('finance.receipts'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.receipts')],
            ],
        ]);
    }

    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int)$request->get('draw', 1);
        $start  = (int)$request->get('start', 0);
        $length = (int)$request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = "1=1";
        $params = [];

        if ($search) {
            $where .= " AND (r.receipt_number LIKE ? OR m.full_name_en LIKE ? OR m.membership_number LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s]);
        }

        $totalRecords = (int)$db->fetchColumn("SELECT COUNT(*) FROM receipts");
        $filteredCount = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM receipts r LEFT JOIN members m ON r.member_id=m.id WHERE {$where}", $params
        );

        $rows = $db->fetchAll(
            "SELECT r.id, r.receipt_number, r.receipt_date, r.total_amount, r.status,
                    m.full_name_en AS member_name, m.membership_number
             FROM receipts r
             LEFT JOIN members m ON r.member_id=m.id
             WHERE {$where}
             ORDER BY r.id DESC
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<a href="/finance/receipts/'.$row['id'].'" class="btn btn-outline-primary"><i class="fas fa-eye"></i></a>';
            if (Gate::can('receipts.print')) {
                $actions .= '<a href="/finance/receipts/'.$row['id'].'/print" target="_blank" class="btn btn-outline-info"><i class="fas fa-print"></i></a>';
            }
            $actions .= '</div>';

            $data[] = [
                'id'             => $row['id'],
                'receipt_number' => e($row['receipt_number']),
                'member_name'    => e($row['member_name'] ?? ''),
                'total_amount'   => money($row['total_amount']),
                'receipt_date'   => format_date($row['receipt_date']),
                'status'         => status_badge($row['status']),
                'actions'        => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalRecords,
            'recordsFiltered' => $filteredCount,
            'data'            => $data,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $receipt = $db->fetchOne(
            "SELECT r.*, m.full_name_en AS member_name, m.membership_number,
                    pm.name_en AS method_name, c.symbol AS currency_symbol, c.currency_code
             FROM receipts r
             LEFT JOIN members m ON r.member_id=m.id
             LEFT JOIN payment_methods pm ON r.payment_method_id=pm.id
             LEFT JOIN sys_currencies c ON r.currency_id=c.id
             WHERE r.id=?", [$id]
        );

        if (!$receipt) {
            $this->abort(404, 'Receipt not found');
            return;
        }

        $items = $db->fetchAll("SELECT * FROM receipt_items WHERE receipt_id=? ORDER BY sort_order", [$id]);

        $this->view('receipts/show', [
            'title' => lang('finance.receipt') . ' #' . $receipt['receipt_number'],
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.receipts'), 'url' => '/finance/receipts'],
                ['label' => '#' . $receipt['receipt_number']],
            ],
            'receipt' => $receipt,
            'items'   => $items,
            'clubName' => Config::get('club_name_en', 'The Club'),
            'clubNameAr' => Config::get('club_name_ar', 'Ø§Ù„Ù†Ø§Ø¯ÙŠ'),
            'clubAddress' => Config::get('club_address_en', ''),
            'clubPhone' => Config::get('club_phone', ''),
        ]);
    }

    public function printReceipt(Request $request): void
    {
        $id = (int)$request->routeParam('id');
        $db = $this->db();

        $receipt = $db->fetchOne(
            "SELECT r.*, m.full_name_en AS member_name, m.full_name_ar AS member_name_ar,
                    m.membership_number, pm.name_en AS method_name,
                    c.symbol AS currency_symbol, c.currency_code
             FROM receipts r
             LEFT JOIN members m ON r.member_id=m.id
             LEFT JOIN payment_methods pm ON r.payment_method_id=pm.id
             LEFT JOIN sys_currencies c ON r.currency_id=c.id
             WHERE r.id=?", [$id]
        );

        if (!$receipt) {
            $this->abort(404);
            return;
        }

        $items = $db->fetchAll("SELECT * FROM receipt_items WHERE receipt_id=? ORDER BY sort_order", [$id]);

        // Update print count
        $db->execute("UPDATE receipts SET reprint_count = reprint_count + 1, printed_at = NOW(), printed_by = ? WHERE id = ?", [Auth::id(), $id]);
        AuditLog::log('print', 'finance', 'receipt', $id, "Printed receipt {$receipt['receipt_number']}");

        $this->view('receipts/print', [
            'title'   => lang('finance.receipt') . ' #' . $receipt['receipt_number'],
            'receipt' => $receipt,
            'items'   => $items,
            'clubName' => Config::get('club_name_en', 'The Club'),
            'clubNameAr' => Config::get('club_name_ar', 'Ø§Ù„Ù†Ø§Ø¯ÙŠ'),
            'clubAddress' => Config::get('club_address_en', ''),
            'clubPhone' => Config::get('club_phone', ''),
        ], 'print');
    }
}
```

## File: modules\finance\Controllers\RevenueReportController.php

```php
<?php
class RevenueReportController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('reports/daily-revenue', [
            'title' => lang('finance.daily_revenue'),
            'breadcrumbs' => [
                ['label' => lang('finance.title'), 'url' => '/finance/invoices'],
                ['label' => lang('finance.daily_revenue')],
            ],
        ]);
    }

    public function data(Request $request): void
    {
        $date = $request->get('date', date('Y-m-d'));
        $db   = $this->db();

        $summary = $db->fetchAll(
            "SELECT pm.name_en AS method, pm.method_type, COUNT(p.id) AS count, COALESCE(SUM(p.amount),0) AS total
             FROM payments p
             JOIN payment_methods pm ON p.payment_method_id=pm.id
             WHERE p.payment_date=? AND p.status='completed' AND p.is_deleted=0
             GROUP BY pm.id, pm.name_en, pm.method_type
             ORDER BY total DESC",
            [$date]
        );

        $grandTotal = (float)$db->fetchColumn(
            "SELECT COALESCE(SUM(amount),0) FROM payments WHERE payment_date=? AND status='completed' AND is_deleted=0",
            [$date]
        );

        $transactionCount = (int)$db->fetchColumn(
            "SELECT COUNT(*) FROM payments WHERE payment_date=? AND status='completed' AND is_deleted=0",
            [$date]
        );

        // Last 7 days for chart
        $chartData = $db->fetchAll(
            "SELECT payment_date, COALESCE(SUM(amount),0) AS total
             FROM payments
             WHERE payment_date BETWEEN DATE_SUB(?, INTERVAL 6 DAY) AND ? AND status='completed' AND is_deleted=0
             GROUP BY payment_date
             ORDER BY payment_date",
            [$date, $date]
        );

        $this->json([
            'success'    => true,
            'summary'    => $summary,
            'grandTotal' => $grandTotal,
            'transactionCount' => $transactionCount,
            'chartData'  => $chartData,
        ]);
    }
}
```

## File: modules\finance\css\finance.css

```css
/* Finance Module Styles */
.finance-invoice-header {
    border-bottom: 3px solid var(--primary);
    padding-bottom: 15px;
    margin-bottom: 20px;
}

#items-table input[type="number"] {
    text-align: right;
}

#items-table .item-total {
    display: block;
    text-align: right;
    padding: 6px 0;
    font-weight: 600;
}

#grand-total {
    font-size: 1.2em;
    color: var(--primary);
}

.card.border-success .card-body h3,
.card.border-danger .card-body h3 {
    font-size: 1.4rem;
}

@media print {
    .btn, .card-header, nav, .breadcrumb, .filter-control { display: none !important; }
    .card { border: none !important; box-shadow: none !important; }
}
```

## File: modules\finance\js\finance.js

```js
/**
 * Finance Module â€” Client-side utilities
 */
(function() {
    'use strict';

    // Auto-reload datatables on successful form submission within modals
    document.addEventListener('DOMContentLoaded', function() {
        // Close modals and reload tables on AJAX form success inside modals
        var originalSubmit = FormHelper.submit.bind(FormHelper);
        var _origSuccess = null;

        document.querySelectorAll('.modal form[data-ajax="true"]').forEach(function(form) {
            form.addEventListener('submit', function(e) {
                e.preventDefault();

                var modal = form.closest('.modal');
                FormHelper.clearErrors(form);
                var submitBtn = form.querySelector('[type="submit"]');
                var originalText = submitBtn ? submitBtn.innerHTML : '';

                if (submitBtn) {
                    submitBtn.disabled = true;
                    submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>...';
                }

                var formData = new FormData(form);

                $.ajax({
                    url: form.action,
                    type: form.method || 'POST',
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function(response) {
                        if (response.success) {
                            FormHelper.showToast('success', response.message || 'Success');
                            if (modal) {
                                bootstrap.Modal.getInstance(modal).hide();
                            }
                            // Reload all visible DataTables
                            Object.keys(DataTableHelper.instances).forEach(function(key) {
                                DataTableHelper.reload(key);
                            });
                            if (response.redirect) {
                                setTimeout(function() { window.location.href = response.redirect; }, 500);
                            }
                        } else if (response.errors) {
                            FormHelper.showErrors(form, response.errors);
                        } else {
                            FormHelper.showToast('error', response.message || 'Operation failed');
                        }
                    },
                    error: function(xhr) {
                        if (xhr.status === 422 && xhr.responseJSON && xhr.responseJSON.errors) {
                            FormHelper.showErrors(form, xhr.responseJSON.errors);
                        }
                    },
                    complete: function() {
                        if (submitBtn) {
                            submitBtn.disabled = false;
                            submitBtn.innerHTML = originalText;
                        }
                    }
                });
            });
        });
    });
})();
```

## File: modules\finance\lang\ar.php

```php
<?php
return [
    'finance.title' => 'Ø§Ù„Ù…Ø§Ù„ÙŠØ©',
    'finance.invoices' => 'Ø§Ù„ÙÙˆØ§ØªÙŠØ±',
    'finance.invoice' => 'ÙØ§ØªÙˆØ±Ø©',
    'finance.create_invoice' => 'Ø¥Ù†Ø´Ø§Ø¡ ÙØ§ØªÙˆØ±Ø©',
    'finance.invoice_no' => 'Ø±Ù‚Ù… Ø§Ù„ÙØ§ØªÙˆØ±Ø©',
    'finance.invoice_created' => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„ÙØ§ØªÙˆØ±Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'finance.invoice_issued' => 'ØªÙ… Ø¥ØµØ¯Ø§Ø± Ø§Ù„ÙØ§ØªÙˆØ±Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'finance.invoice_voided' => 'ØªÙ… Ø¥Ù„ØºØ§Ø¡ Ø§Ù„ÙØ§ØªÙˆØ±Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'finance.items_required' => 'Ù…Ø·Ù„ÙˆØ¨ Ø¹Ù†ØµØ± ÙˆØ§Ø­Ø¯ Ø¹Ù„Ù‰ Ø§Ù„Ø£Ù‚Ù„',
    'finance.issue' => 'Ø¥ØµØ¯Ø§Ø±',
    'finance.issue_immediately' => 'Ø¥ØµØ¯Ø§Ø± ÙÙˆØ±ÙŠ',
    'finance.void' => 'Ø¥Ù„ØºØ§Ø¡',
    'finance.void_invoice' => 'Ø¥Ù„ØºØ§Ø¡ Ø§Ù„ÙØ§ØªÙˆØ±Ø©',
    'finance.void_reason' => 'Ø³Ø¨Ø¨ Ø§Ù„Ø¥Ù„ØºØ§Ø¡...',
    'finance.reason_required' => 'Ø§Ù„Ø³Ø¨Ø¨ Ù…Ø·Ù„ÙˆØ¨',
    'finance.confirm_issue' => 'Ø¥ØµØ¯Ø§Ø± Ø§Ù„ÙØ§ØªÙˆØ±Ø©ØŸ',
    'finance.confirm_issue_text' => 'Ø³ÙŠØªÙ… Ø¥ØµØ¯Ø§Ø± Ø§Ù„ÙØ§ØªÙˆØ±Ø© ÙˆØªØ­Ø¯ÙŠØ« Ø±ØµÙŠØ¯ Ø§Ù„Ø¹Ø¶Ùˆ.',
    'finance.payments' => 'Ø§Ù„Ù…Ø¯ÙÙˆØ¹Ø§Øª',
    'finance.payment' => 'Ø¯ÙØ¹Ø©',
    'finance.payment_no' => 'Ø±Ù‚Ù… Ø§Ù„Ø¯ÙØ¹Ø©',
    'finance.record_payment' => 'ØªØ³Ø¬ÙŠÙ„ Ø¯ÙØ¹Ø©',
    'finance.payment_recorded' => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯ÙØ¹Ø© Ø¨Ù†Ø¬Ø§Ø­',
    'finance.payment_details' => 'ØªÙØ§ØµÙŠÙ„ Ø§Ù„Ø¯ÙØ¹Ø©',
    'finance.receipts' => 'Ø§Ù„Ø¥ÙŠØµØ§Ù„Ø§Øª',
    'finance.receipt' => 'Ø¥ÙŠØµØ§Ù„',
    'finance.receipt_no' => 'Ø±Ù‚Ù… Ø§Ù„Ø¥ÙŠØµØ§Ù„',
    'finance.print_receipt' => 'Ø·Ø¨Ø§Ø¹Ø© Ø§Ù„Ø¥ÙŠØµØ§Ù„',
    'finance.cash_register' => 'Ø§Ù„ØµÙ†Ø¯ÙˆÙ‚',
    'finance.active_session' => 'Ø¬Ù„Ø³Ø© Ù†Ø´Ø·Ø©',
    'finance.open_session' => 'ÙØªØ­ Ø¬Ù„Ø³Ø©',
    'finance.close_session' => 'Ø¥ØºÙ„Ø§Ù‚ Ø§Ù„Ø¬Ù„Ø³Ø©',
    'finance.session_history' => 'Ø³Ø¬Ù„ Ø§Ù„Ø¬Ù„Ø³Ø§Øª',
    'finance.select_register' => 'Ø§Ø®ØªØ± Ø§Ù„ØµÙ†Ø¯ÙˆÙ‚',
    'finance.opening_balance' => 'Ø§Ù„Ø±ØµÙŠØ¯ Ø§Ù„Ø§ÙØªØªØ§Ø­ÙŠ',
    'finance.closing_balance' => 'Ø§Ù„Ø±ØµÙŠØ¯ Ø§Ù„Ø®ØªØ§Ù…ÙŠ',
    'finance.cash_in' => 'Ø¥ÙŠØ¯Ø§Ø¹',
    'finance.cash_out' => 'Ø³Ø­Ø¨',
    'finance.transactions' => 'Ø§Ù„Ù…Ø¹Ø§Ù…Ù„Ø§Øª',
    'finance.recent_transactions' => 'Ø¢Ø®Ø± Ø§Ù„Ù…Ø¹Ø§Ù…Ù„Ø§Øª',
    'finance.fee_types' => 'Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø±Ø³ÙˆÙ…',
    'finance.fee_type' => 'Ù†ÙˆØ¹ Ø§Ù„Ø±Ø³Ù…',
    'finance.payment_methods' => 'Ø·Ø±Ù‚ Ø§Ù„Ø¯ÙØ¹',
    'finance.payment_method' => 'Ø·Ø±ÙŠÙ‚Ø© Ø§Ù„Ø¯ÙØ¹',
    'finance.fiscal_years' => 'Ø§Ù„Ø³Ù†ÙˆØ§Øª Ø§Ù„Ù…Ø§Ù„ÙŠØ©',
    'finance.member_statement' => 'ÙƒØ´Ù Ø­Ø³Ø§Ø¨ Ø§Ù„Ø¹Ø¶Ùˆ',
    'finance.daily_revenue' => 'Ø§Ù„Ø¥ÙŠØ±Ø§Ø¯Ø§Øª Ø§Ù„ÙŠÙˆÙ…ÙŠØ©',
    'finance.overdue_invoices' => 'ÙÙˆØ§ØªÙŠØ± Ù…ØªØ£Ø®Ø±Ø©',
    'finance.total_outstanding' => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ù…Ø³ØªØ­Ù‚',
    'finance.total_overdue' => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ù…ØªØ£Ø®Ø±',
    'finance.issued_today' => 'ØµØ¯Ø±Øª Ø§Ù„ÙŠÙˆÙ…',
    'finance.collected_today' => 'ØªÙ… ØªØ­ØµÙŠÙ„Ù‡ Ø§Ù„ÙŠÙˆÙ…',
    'finance.revenue_today' => 'Ø¥ÙŠØ±Ø§Ø¯Ø§Øª Ø§Ù„ÙŠÙˆÙ…',
    'finance.member' => 'Ø§Ù„Ø¹Ø¶Ùˆ',
    'finance.search_member' => 'Ø¨Ø­Ø« Ø¹Ù† Ø¹Ø¶Ùˆ',
    'finance.type' => 'Ø§Ù„Ù†ÙˆØ¹',
    'finance.date' => 'Ø§Ù„ØªØ§Ø±ÙŠØ®',
    'finance.due_date' => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ø³ØªØ­Ù‚Ø§Ù‚',
    'finance.total' => 'Ø§Ù„Ø¥Ø¬Ù…Ø§Ù„ÙŠ',
    'finance.paid' => 'Ø§Ù„Ù…Ø¯ÙÙˆØ¹',
    'finance.balance' => 'Ø§Ù„Ø±ØµÙŠØ¯',
    'finance.balance_due' => 'Ø§Ù„Ù…Ø¨Ù„Øº Ø§Ù„Ù…Ø³ØªØ­Ù‚',
    'finance.amount' => 'Ø§Ù„Ù…Ø¨Ù„Øº',
    'finance.method' => 'Ø§Ù„Ø·Ø±ÙŠÙ‚Ø©',
    'finance.date_from' => 'Ù…Ù†',
    'finance.date_to' => 'Ø¥Ù„Ù‰',
    'finance.overdue_only' => 'Ø§Ù„Ù…ØªØ£Ø®Ø±Ø© ÙÙ‚Ø·',
    'finance.invoice_type' => 'Ù†ÙˆØ¹ Ø§Ù„ÙØ§ØªÙˆØ±Ø©',
    'finance.line_items' => 'Ø¨Ù†ÙˆØ¯ Ø§Ù„ÙØ§ØªÙˆØ±Ø©',
    'finance.select_fee_type' => 'Ø§Ø®ØªØ± Ù†ÙˆØ¹ Ø§Ù„Ø±Ø³Ù…',
    'finance.description' => 'Ø§Ù„ÙˆØµÙ',
    'finance.qty' => 'Ø§Ù„ÙƒÙ…ÙŠØ©',
    'finance.unit_price' => 'Ø³Ø¹Ø± Ø§Ù„ÙˆØ­Ø¯Ø©',
    'finance.discount' => 'Ø§Ù„Ø®ØµÙ…',
    'finance.tax_rate' => 'Ù†Ø³Ø¨Ø© Ø§Ù„Ø¶Ø±ÙŠØ¨Ø©',
    'finance.tax' => 'Ø§Ù„Ø¶Ø±ÙŠØ¨Ø©',
    'finance.add_item' => 'Ø¥Ø¶Ø§ÙØ© Ø¨Ù†Ø¯',
    'finance.grand_total' => 'Ø§Ù„Ù…Ø¬Ù…ÙˆØ¹ Ø§Ù„ÙƒÙ„ÙŠ',
    'finance.notes' => 'Ù…Ù„Ø§Ø­Ø¸Ø§Øª',
    'finance.bill_to' => 'ÙØ§ØªÙˆØ±Ø© Ø¥Ù„Ù‰',
    'finance.currency' => 'Ø§Ù„Ø¹Ù…Ù„Ø©',
    'finance.subtotal' => 'Ø§Ù„Ù…Ø¬Ù…ÙˆØ¹ Ø§Ù„ÙØ±Ø¹ÙŠ',
    'finance.total_amount' => 'Ø§Ù„Ù…Ø¨Ù„Øº Ø§Ù„Ø¥Ø¬Ù…Ø§Ù„ÙŠ',
    'finance.payment_history' => 'Ø³Ø¬Ù„ Ø§Ù„Ù…Ø¯ÙÙˆØ¹Ø§Øª',
    'finance.reference' => 'Ø§Ù„Ù…Ø±Ø¬Ø¹',
    'finance.reference_number' => 'Ø±Ù‚Ù… Ø§Ù„Ù…Ø±Ø¬Ø¹',
    'finance.payment_date' => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¯ÙØ¹',
    'finance.allocate_to_invoice' => 'ØªØ®ØµÙŠØµ Ù„ÙØ§ØªÙˆØ±Ø©',
    'finance.auto_allocate' => 'ØªØ®ØµÙŠØµ ØªÙ„Ù‚Ø§Ø¦ÙŠ',
    'finance.cash_register_session' => 'Ø¬Ù„Ø³Ø© Ø§Ù„ØµÙ†Ø¯ÙˆÙ‚',
    'finance.no_session' => 'Ù„Ø§ ØªÙˆØ¬Ø¯ Ø¬Ù„Ø³Ø© Ù†Ø´Ø·Ø©',
    'finance.name' => 'Ø§Ù„Ø§Ø³Ù…',
    'finance.membership_no' => 'Ø±Ù‚Ù… Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'finance.allocated_to' => 'Ù…Ø®ØµØµ Ù„Ù€',
    'finance.allocated' => 'Ø§Ù„Ù…Ø®ØµØµ',
    'finance.outstanding' => 'Ø§Ù„Ù…Ø³ØªØ­Ù‚',
    'finance.register' => 'Ø§Ù„ØµÙ†Ø¯ÙˆÙ‚',
    'finance.operator' => 'Ø§Ù„Ù…Ø´ØºÙ„',
    'finance.opening' => 'Ø§Ù„Ø§ÙØªØªØ§Ø­ÙŠ',
    'finance.closing' => 'Ø§Ù„Ø®ØªØ§Ù…ÙŠ',
    'finance.variance' => 'Ø§Ù„ÙØ±Ù‚',
    'finance.opened' => 'ØªÙ… Ø§Ù„ÙØªØ­',
    'finance.in_use' => 'Ù…Ø³ØªØ®Ø¯Ù…',
    'finance.open' => 'Ù…ÙØªÙˆØ­',
    'finance.time' => 'Ø§Ù„ÙˆÙ‚Øª',
    'finance.direction' => 'Ø§Ù„Ø§ØªØ¬Ø§Ù‡',
    'finance.debit' => 'Ù…Ø¯ÙŠÙ†',
    'finance.credit' => 'Ø¯Ø§Ø¦Ù†',
    'finance.breakdown_by_method' => 'ØªØ­Ù„ÙŠÙ„ Ø­Ø³Ø¨ Ø·Ø±ÙŠÙ‚Ø© Ø§Ù„Ø¯ÙØ¹',
    'finance.count' => 'Ø§Ù„Ø¹Ø¯Ø¯',
    'finance.days_overdue' => 'Ø£ÙŠØ§Ù… Ø§Ù„ØªØ£Ø®ÙŠØ±',
    'finance.days' => 'ÙŠÙˆÙ…',
    'finance.aging' => 'Ø§Ù„ØªÙ‚Ø§Ø¯Ù…',
    'finance.draft' => 'Ù…Ø³ÙˆØ¯Ø©',
    'finance.issued' => 'ØµØ§Ø¯Ø±Ø©',
    'finance.partially_paid' => 'Ù…Ø¯ÙÙˆØ¹Ø© Ø¬Ø²Ø¦ÙŠØ§Ù‹',
    'finance.overdue' => 'Ù…ØªØ£Ø®Ø±Ø©',
    'finance.cashier_signature' => 'ØªÙˆÙ‚ÙŠØ¹ Ø£Ù…ÙŠÙ† Ø§Ù„ØµÙ†Ø¯ÙˆÙ‚',
    'finance.member_signature' => 'ØªÙˆÙ‚ÙŠØ¹ Ø§Ù„Ø¹Ø¶Ùˆ',
];
```

## File: modules\finance\lang\en.php

```php
<?php
return [
    'finance.title' => 'Finance',
    'finance.invoices' => 'Invoices',
    'finance.invoice' => 'Invoice',
    'finance.create_invoice' => 'Create Invoice',
    'finance.invoice_no' => 'Invoice #',
    'finance.invoice_created' => 'Invoice created successfully',
    'finance.invoice_issued' => 'Invoice issued successfully',
    'finance.invoice_voided' => 'Invoice voided successfully',
    'finance.items_required' => 'At least one line item is required',
    'finance.issue' => 'Issue',
    'finance.issue_immediately' => 'Issue Immediately',
    'finance.void' => 'Void',
    'finance.void_invoice' => 'Void Invoice',
    'finance.void_reason' => 'Reason for voiding...',
    'finance.reason_required' => 'Reason is required',
    'finance.confirm_issue' => 'Issue Invoice?',
    'finance.confirm_issue_text' => 'This will issue the invoice and update the member balance.',
    'finance.payments' => 'Payments',
    'finance.payment' => 'Payment',
    'finance.payment_no' => 'Payment #',
    'finance.record_payment' => 'Record Payment',
    'finance.payment_recorded' => 'Payment recorded successfully',
    'finance.payment_details' => 'Payment Details',
    'finance.receipts' => 'Receipts',
    'finance.receipt' => 'Receipt',
    'finance.receipt_no' => 'Receipt #',
    'finance.print_receipt' => 'Print Receipt',
    'finance.cash_register' => 'Cash Register',
    'finance.active_session' => 'Active Session',
    'finance.open_session' => 'Open Session',
    'finance.close_session' => 'Close Session',
    'finance.session_history' => 'Session History',
    'finance.select_register' => 'Select Register',
    'finance.opening_balance' => 'Opening Balance',
    'finance.closing_balance' => 'Closing Balance',
    'finance.cash_in' => 'Cash In',
    'finance.cash_out' => 'Cash Out',
    'finance.transactions' => 'Transactions',
    'finance.recent_transactions' => 'Recent Transactions',
    'finance.fee_types' => 'Fee Types',
    'finance.fee_type' => 'Fee Type',
    'finance.payment_methods' => 'Payment Methods',
    'finance.payment_method' => 'Payment Method',
    'finance.fiscal_years' => 'Fiscal Years',
    'finance.member_statement' => 'Member Statement',
    'finance.daily_revenue' => 'Daily Revenue',
    'finance.overdue_invoices' => 'Overdue Invoices',
    'finance.total_outstanding' => 'Total Outstanding',
    'finance.total_overdue' => 'Total Overdue',
    'finance.issued_today' => 'Issued Today',
    'finance.collected_today' => 'Collected Today',
    'finance.revenue_today' => 'Revenue Today',
    'finance.member' => 'Member',
    'finance.search_member' => 'Search member',
    'finance.type' => 'Type',
    'finance.date' => 'Date',
    'finance.due_date' => 'Due Date',
    'finance.total' => 'Total',
    'finance.paid' => 'Paid',
    'finance.balance' => 'Balance',
    'finance.balance_due' => 'Balance Due',
    'finance.amount' => 'Amount',
    'finance.method' => 'Method',
    'finance.date_from' => 'From',
    'finance.date_to' => 'To',
    'finance.overdue_only' => 'Overdue Only',
    'finance.invoice_type' => 'Invoice Type',
    'finance.line_items' => 'Line Items',
    'finance.select_fee_type' => 'Select fee type',
    'finance.description' => 'Description',
    'finance.qty' => 'Qty',
    'finance.unit_price' => 'Unit Price',
    'finance.discount' => 'Discount',
    'finance.tax_rate' => 'Tax Rate',
    'finance.tax' => 'Tax',
    'finance.add_item' => 'Add Item',
    'finance.grand_total' => 'Grand Total',
    'finance.notes' => 'Notes',
    'finance.bill_to' => 'Bill To',
    'finance.currency' => 'Currency',
    'finance.subtotal' => 'Subtotal',
    'finance.total_amount' => 'Total Amount',
    'finance.payment_history' => 'Payment History',
    'finance.reference' => 'Reference',
    'finance.reference_number' => 'Reference Number',
    'finance.payment_date' => 'Payment Date',
    'finance.allocate_to_invoice' => 'Allocate to Invoice',
    'finance.auto_allocate' => 'Auto-allocate',
    'finance.cash_register_session' => 'Cash Register Session',
    'finance.no_session' => 'No active session',
    'finance.name' => 'Name',
    'finance.membership_no' => 'Membership #',
    'finance.allocated_to' => 'Allocated To',
    'finance.allocated' => 'Allocated',
    'finance.outstanding' => 'Outstanding',
    'finance.register' => 'Register',
    'finance.operator' => 'Operator',
    'finance.opening' => 'Opening',
    'finance.closing' => 'Closing',
    'finance.variance' => 'Variance',
    'finance.opened' => 'Opened',
    'finance.in_use' => 'In Use',
    'finance.open' => 'Open',
    'finance.time' => 'Time',
    'finance.direction' => 'Direction',
    'finance.debit' => 'Debit',
    'finance.credit' => 'Credit',
    'finance.breakdown_by_method' => 'Breakdown by Method',
    'finance.count' => 'Count',
    'finance.days_overdue' => 'Days Overdue',
    'finance.days' => 'days',
    'finance.aging' => 'Aging',
    'finance.draft' => 'Draft',
    'finance.issued' => 'Issued',
    'finance.partially_paid' => 'Partially Paid',
    'finance.overdue' => 'Overdue',
    'finance.cashier_signature' => 'Cashier Signature',
    'finance.member_signature' => 'Member Signature',
];
```

## File: modules\finance\module.json

```json
{
    "name": "finance",
    "label_en": "Finance & Billing",
    "label_ar": "Ø§Ù„Ù…Ø§Ù„ÙŠØ© ÙˆØ§Ù„ÙÙˆØ§ØªÙŠØ±",
    "version": "1.0.0",
    "description": "Core financial operations: invoicing, payments, receipts, cash register, fiscal management",
    "sort_order": 30,
    "icon": "fas fa-money-bill-wave",
    "routes_file": "routes.php",
    "css_files": ["css/finance.css"],
    "js_files": ["js/finance.js"],
    "menu": [
        {
            "label_en": "Finance",
            "label_ar": "Ø§Ù„Ù…Ø§Ù„ÙŠØ©",
            "icon": "fas fa-money-bill-wave",
            "permission": "invoices.view",
            "sort_order": 30,
            "children": [
                {
                    "label_en": "Invoices",
                    "label_ar": "Ø§Ù„ÙÙˆØ§ØªÙŠØ±",
                    "route": "/finance/invoices",
                    "permission": "invoices.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Create Invoice",
                    "label_ar": "Ø¥Ù†Ø´Ø§Ø¡ ÙØ§ØªÙˆØ±Ø©",
                    "route": "/finance/invoices/create",
                    "permission": "invoices.create",
                    "sort_order": 2
                },
                {
                    "label_en": "Payments",
                    "label_ar": "Ø§Ù„Ù…Ø¯ÙÙˆØ¹Ø§Øª",
                    "route": "/finance/payments",
                    "permission": "payments.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Record Payment",
                    "label_ar": "ØªØ³Ø¬ÙŠÙ„ Ø¯ÙØ¹Ø©",
                    "route": "/finance/payments/create",
                    "permission": "payments.create",
                    "sort_order": 4
                },
                {
                    "label_en": "Receipts",
                    "label_ar": "Ø§Ù„Ø¥ÙŠØµØ§Ù„Ø§Øª",
                    "route": "/finance/receipts",
                    "permission": "receipts.view",
                    "sort_order": 5
                },
                {
                    "label_en": "Cash Register",
                    "label_ar": "Ø§Ù„ØµÙ†Ø¯ÙˆÙ‚",
                    "route": "/finance/cashier",
                    "permission": "cash_register.open",
                    "sort_order": 6
                },
                {
                    "label_en": "Fee Types",
                    "label_ar": "Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø±Ø³ÙˆÙ…",
                    "route": "/finance/fee-types",
                    "permission": "invoices.view",
                    "sort_order": 7
                },
                {
                    "label_en": "Payment Methods",
                    "label_ar": "Ø·Ø±Ù‚ Ø§Ù„Ø¯ÙØ¹",
                    "route": "/finance/payment-methods",
                    "permission": "payments.view",
                    "sort_order": 8
                },
                {
                    "label_en": "Fiscal Years",
                    "label_ar": "Ø§Ù„Ø³Ù†ÙˆØ§Øª Ø§Ù„Ù…Ø§Ù„ÙŠØ©",
                    "route": "/finance/fiscal-years",
                    "permission": "invoices.view",
                    "sort_order": 9
                },
                {
                    "label_en": "Daily Revenue",
                    "label_ar": "Ø§Ù„Ø¥ÙŠØ±Ø§Ø¯Ø§Øª Ø§Ù„ÙŠÙˆÙ…ÙŠØ©",
                    "route": "/finance/reports/daily-revenue",
                    "permission": "payments.view",
                    "sort_order": 10
                },
                {
                    "label_en": "Overdue Invoices",
                    "label_ar": "ÙÙˆØ§ØªÙŠØ± Ù…ØªØ£Ø®Ø±Ø©",
                    "route": "/finance/reports/overdue",
                    "permission": "invoices.view",
                    "sort_order": 11
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "finance_revenue_today",
            "label_en": "Revenue Today",
            "label_ar": "Ø¥ÙŠØ±Ø§Ø¯Ø§Øª Ø§Ù„ÙŠÙˆÙ…",
            "permission": "payments.view",
            "partial": "views/widgets/revenue_today.php",
            "size": "col-md-3",
            "sort_order": 10
        },
        {
            "widget_id": "finance_outstanding",
            "label_en": "Outstanding Balance",
            "label_ar": "Ø§Ù„Ø±ØµÙŠØ¯ Ø§Ù„Ù…Ø³ØªØ­Ù‚",
            "permission": "invoices.view",
            "partial": "views/widgets/outstanding_balance.php",
            "size": "col-md-3",
            "sort_order": 11
        },
        {
            "widget_id": "finance_overdue",
            "label_en": "Overdue Invoices",
            "label_ar": "Ø§Ù„ÙÙˆØ§ØªÙŠØ± Ø§Ù„Ù…ØªØ£Ø®Ø±Ø©",
            "permission": "invoices.view",
            "partial": "views/widgets/overdue_invoices.php",
            "size": "col-md-3",
            "sort_order": 12
        }
    ]
}
```

## File: modules\finance\routes.php

```php
<?php
return [
    // â”€â”€â”€ Invoices â”€â”€â”€
    ['GET',  '/finance/invoices',              'InvoiceController@index',         'invoices.view'],
    ['GET',  '/finance/invoices/datatable',    'InvoiceController@datatable',     'invoices.view'],
    ['GET',  '/finance/invoices/create',       'InvoiceController@create',        'invoices.create'],
    ['POST', '/finance/invoices',              'InvoiceController@store',         'invoices.create'],
    ['GET',  '/finance/invoices/{id}',         'InvoiceController@show',          'invoices.view'],
    ['POST', '/finance/invoices/{id}/issue',   'InvoiceController@issue',         'invoices.create'],
    ['POST', '/finance/invoices/{id}/void',    'InvoiceController@void',          'invoices.void'],

    // â”€â”€â”€ Payments â”€â”€â”€
    ['GET',  '/finance/payments',              'PaymentController@index',         'payments.view'],
    ['GET',  '/finance/payments/datatable',    'PaymentController@datatable',     'payments.view'],
    ['GET',  '/finance/payments/create',       'PaymentController@create',        'payments.create'],
    ['POST', '/finance/payments',              'PaymentController@store',         'payments.create'],
    ['GET',  '/finance/payments/{id}',         'PaymentController@show',          'payments.view'],

    // â”€â”€â”€ Receipts â”€â”€â”€
    ['GET',  '/finance/receipts',              'ReceiptController@index',         'receipts.view'],
    ['GET',  '/finance/receipts/datatable',    'ReceiptController@datatable',     'receipts.view'],
    ['GET',  '/finance/receipts/{id}',         'ReceiptController@show',          'receipts.view'],
    ['GET',  '/finance/receipts/{id}/print',   'ReceiptController@printReceipt', 'receipts.print'],

    // â”€â”€â”€ Cash Register â”€â”€â”€
    ['GET',  '/finance/cashier',               'CashRegisterController@index',    'cash_register.open'],
    ['POST', '/finance/cashier/open',          'CashRegisterController@open',     'cash_register.open'],
    ['POST', '/finance/cashier/close',         'CashRegisterController@close',    'cash_register.close'],
    ['GET',  '/finance/cashier/sessions',      'CashRegisterController@sessions', 'cash_register.open'],
    ['GET',  '/finance/cashier/sessions/datatable', 'CashRegisterController@sessionsDatatable', 'cash_register.open'],

    // â”€â”€â”€ Fee Types â”€â”€â”€
    ['GET',  '/finance/fee-types',             'FeeTypeController@index',         'invoices.view'],
    ['GET',  '/finance/fee-types/datatable',   'FeeTypeController@datatable',     'invoices.view'],
    ['POST', '/finance/fee-types',             'FeeTypeController@store',         'invoices.create'],
    ['POST', '/finance/fee-types/{id}',        'FeeTypeController@update',        'invoices.create'],

    // â”€â”€â”€ Payment Methods â”€â”€â”€
    ['GET',  '/finance/payment-methods',           'PaymentMethodController@index',     'payments.view'],
    ['GET',  '/finance/payment-methods/datatable', 'PaymentMethodController@datatable', 'payments.view'],
    ['POST', '/finance/payment-methods',           'PaymentMethodController@store',     'payments.create'],
    ['POST', '/finance/payment-methods/{id}',      'PaymentMethodController@update',    'payments.create'],

    // â”€â”€â”€ Fiscal Years â”€â”€â”€
    ['GET',  '/finance/fiscal-years',              'FiscalYearController@index',     'invoices.view'],
    ['GET',  '/finance/fiscal-years/datatable',    'FiscalYearController@datatable', 'invoices.view'],
    ['POST', '/finance/fiscal-years',              'FiscalYearController@store',     'invoices.create'],
    ['POST', '/finance/fiscal-years/{id}',         'FiscalYearController@update',    'invoices.create'],
    ['POST', '/finance/fiscal-years/{id}/periods', 'FiscalYearController@storePeriod', 'invoices.create'],

    // â”€â”€â”€ Member Statement â”€â”€â”€
    ['GET',  '/finance/statement/{id}',        'MemberStatementController@show',  'invoices.view'],

    // â”€â”€â”€ Reports â”€â”€â”€
    ['GET',  '/finance/reports/daily-revenue',          'RevenueReportController@index',     'payments.view'],
    ['GET',  '/finance/reports/daily-revenue/data',     'RevenueReportController@data',      'payments.view'],
    ['GET',  '/finance/reports/overdue',                'OverdueReportController@index',     'invoices.view'],
    ['GET',  '/finance/reports/overdue/datatable',      'OverdueReportController@datatable', 'invoices.view'],

    // â”€â”€â”€ API helpers â”€â”€â”€
    ['GET',  '/finance/api/members/search',    'InvoiceController@memberSearch',  'invoices.view'],
    ['GET',  '/finance/api/fee-types/list',    'FeeTypeController@listActive',    'invoices.view'],
    ['GET',  '/finance/api/invoices/for-member', 'InvoiceController@forMember',   'payments.create'],
];
```

## File: modules\finance\views\cashier\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-cash-register me-2"></i><?= lang('finance.cash_register') ?></h4>
    <a href="/finance/cashier/sessions" class="btn btn-outline-secondary"><i class="fas fa-history me-1"></i><?= lang('finance.session_history') ?></a>
</div>

<?php if ($mySession): ?>
<!-- Active Session -->
<div class="card border-success mb-4">
    <div class="card-header bg-success text-white">
        <div class="d-flex justify-content-between align-items-center">
            <h6 class="mb-0"><i class="fas fa-check-circle me-1"></i><?= lang('finance.active_session') ?> â€” <?= e($mySession['register_name']) ?></h6>
            <span class="badge bg-light text-dark"><?= lang('finance.opened') ?>: <?= format_datetime($mySession['opened_at']) ?></span>
        </div>
    </div>
    <div class="card-body">
        <div class="row g-3 mb-3">
            <div class="col-md-3">
                <div class="card bg-light"><div class="card-body py-2 text-center">
                    <small class="text-muted"><?= lang('finance.opening_balance') ?></small><br><strong><?= money($mySession['opening_balance']) ?></strong>
                </div></div>
            </div>
            <div class="col-md-3">
                <div class="card bg-light"><div class="card-body py-2 text-center">
                    <small class="text-muted"><?= lang('finance.cash_in') ?></small><br><strong class="text-success"><?= money($mySession['total_cash_in']) ?></strong>
                </div></div>
            </div>
            <div class="col-md-3">
                <div class="card bg-light"><div class="card-body py-2 text-center">
                    <small class="text-muted"><?= lang('finance.cash_out') ?></small><br><strong class="text-danger"><?= money($mySession['total_cash_out'] ?? 0) ?></strong>
                </div></div>
            </div>
            <div class="col-md-3">
                <div class="card bg-light"><div class="card-body py-2 text-center">
                    <small class="text-muted"><?= lang('finance.transactions') ?></small><br><strong><?= number_format($mySession['total_transactions']) ?></strong>
                </div></div>
            </div>
        </div>

        <!-- Close Session Form -->
        <form method="POST" action="/finance/cashier/close" data-ajax="true" class="row g-3 align-items-end">
            <?= csrf_field() ?>
            <input type="hidden" name="session_id" value="<?= $mySession['id'] ?>">
            <div class="col-md-4">
                <label class="form-label"><?= lang('finance.closing_balance') ?></label>
                <input type="number" name="closing_balance" class="form-control" step="0.01" min="0" required>
            </div>
            <div class="col-md-4">
                <button type="submit" class="btn btn-danger"><i class="fas fa-lock me-1"></i><?= lang('finance.close_session') ?></button>
            </div>
        </form>

        <!-- Recent Transactions -->
        <?php if (!empty($transactions)): ?>
        <hr>
        <h6><?= lang('finance.recent_transactions') ?></h6>
        <table class="table table-sm">
            <thead><tr><th><?= lang('finance.time') ?></th><th><?= lang('finance.type') ?></th><th><?= lang('finance.method') ?></th><th><?= lang('finance.direction') ?></th><th class="text-end"><?= lang('finance.amount') ?></th><th><?= lang('finance.reference') ?></th></tr></thead>
            <tbody>
            <?php foreach ($transactions as $t): ?>
            <tr>
                <td><small><?= format_datetime($t['created_at']) ?></small></td>
                <td><?= e(ucfirst(str_replace('_',' ',$t['transaction_type']))) ?></td>
                <td><?= e(ucfirst($t['payment_method'])) ?></td>
                <td><?= $t['direction'] === 'in' ? '<span class="text-success">â†“ In</span>' : '<span class="text-danger">â†‘ Out</span>' ?></td>
                <td class="text-end"><?= money($t['amount']) ?></td>
                <td><?= $t['payment_number'] ? e($t['payment_number']) : '' ?></td>
            </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
        <?php endif; ?>
    </div>
</div>

<?php else: ?>
<!-- Open Session -->
<div class="row justify-content-center">
    <div class="col-lg-6">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('finance.open_session') ?></h6></div>
            <div class="card-body">
                <form method="POST" action="/finance/cashier/open" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('finance.select_register') ?> <span class="text-danger">*</span></label>
                        <select name="register_id" class="form-select" required>
                            <?php foreach ($registers as $reg): ?>
                            <option value="<?= $reg['id'] ?>" <?= $reg['session_id'] ? 'disabled' : '' ?>>
                                <?= e($reg['name_en']) ?> <?= $reg['session_id'] ? '(' . lang('finance.in_use') . ')' : '' ?>
                            </option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('finance.opening_balance') ?></label>
                        <input type="number" name="opening_balance" class="form-control" step="0.01" value="0" min="0">
                    </div>
                    <button type="submit" class="btn btn-success"><i class="fas fa-unlock me-1"></i><?= lang('finance.open_session') ?></button>
                </form>
            </div>
        </div>
    </div>
</div>
<?php endif; ?>
```

## File: modules\finance\views\cashier\sessions.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.session_history') ?></h4>
    <a href="/finance/cashier" class="btn btn-outline-secondary"><?= lang('common.back') ?></a>
</div>

<div class="card">
    <div class="card-body">
        <table id="sessions-table" class="table table-striped w-100"
               data-url="/finance/cashier/sessions/datatable"
               data-columns='[
                   {"data":"id","title":"#"},
                   {"data":"register_name","title":"<?= lang('finance.register') ?>"},
                   {"data":"operator_name","title":"<?= lang('finance.operator') ?>"},
                   {"data":"session_date","title":"<?= lang('finance.date') ?>"},
                   {"data":"opening_balance","title":"<?= lang('finance.opening') ?>"},
                   {"data":"closing_balance","title":"<?= lang('finance.closing') ?>"},
                   {"data":"variance","title":"<?= lang('finance.variance') ?>"},
                   {"data":"total_transactions","title":"<?= lang('finance.transactions') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"}
               ]'>
            <thead><tr><th>#</th><th>Register</th><th>Operator</th><th>Date</th><th>Opening</th><th>Closing</th><th>Variance</th><th>Trans.</th><th>Status</th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('sessions-table', {order:[[0,'desc']]}); });</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\fee-types\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.fee_types') ?></h4>
    <?php if (auth_can('invoices.create')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#fee-modal" id="btn-new-fee"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></button>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="fee-types-table" class="table table-striped w-100"
               data-url="/finance/fee-types/datatable"
               data-columns='[
                   {"data":"id","title":"#"},
                   {"data":"fee_code","title":"Code"},
                   {"data":"name_en","title":"Name (EN)"},
                   {"data":"name_ar","title":"Name (AR)"},
                   {"data":"category","title":"Category"},
                   {"data":"is_recurring","title":"Recurring"},
                   {"data":"is_active","title":"Status"},
                   {"data":"actions","title":"","orderable":false}
               ]'>
            <thead><tr><th>#</th><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Category</th><th>Recurring</th><th>Status</th><th></th></tr></thead>
        </table>
    </div>
</div>

<!-- Fee Type Modal -->
<div class="modal fade" id="fee-modal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form id="fee-form" method="POST" action="/finance/fee-types" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-header">
                    <h5 class="modal-title" id="fee-modal-title"><?= lang('finance.fee_type') ?></h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <div class="mb-3"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="fee_code" class="form-control" required><div class="invalid-feedback" data-field="fee_code"></div></div>
                    <div class="mb-3"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required><div class="invalid-feedback" data-field="name_en"></div></div>
                    <div class="mb-3"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" required><div class="invalid-feedback" data-field="name_ar"></div></div>
                    <div class="mb-3"><label class="form-label">Category <span class="text-danger">*</span></label>
                        <select name="category" class="form-select" required>
                            <?php foreach (['registration','subscription','annual','transfer','freeze','card','carnet','penalty','sport','facility','guest','other'] as $cat): ?>
                            <option value="<?= $cat ?>"><?= ucfirst($cat) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="row">
                        <div class="col-4"><div class="form-check"><input type="checkbox" name="is_recurring" value="1" class="form-check-input" id="fee-recurring"><label class="form-check-label" for="fee-recurring">Recurring</label></div></div>
                        <div class="col-4"><div class="form-check"><input type="checkbox" name="is_refundable" value="1" class="form-check-input" id="fee-refundable"><label class="form-check-label" for="fee-refundable">Refundable</label></div></div>
                        <div class="col-4"><div class="form-check"><input type="checkbox" name="is_active" value="1" class="form-check-input" id="fee-active" checked><label class="form-check-label" for="fee-active">Active</label></div></div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.close') ?></button>
                    <button type="submit" class="btn btn-primary"><?= lang('common.save') ?></button>
                </div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('fee-types-table');

    document.getElementById('btn-new-fee').addEventListener('click', function() {
        document.getElementById('fee-form').action = '/finance/fee-types';
        document.getElementById('fee-form').reset();
        document.getElementById('fee-modal-title').textContent = 'New Fee Type';
        document.querySelector('[name="fee_code"]').readOnly = false;
    });

    document.addEventListener('click', function(e) {
        var btn = e.target.closest('.btn-edit-fee');
        if (btn) {
            var data = JSON.parse(btn.dataset.fee);
            var form = document.getElementById('fee-form');
            form.action = '/finance/fee-types/' + data.id;
            document.getElementById('fee-modal-title').textContent = 'Edit Fee Type';
            form.querySelector('[name="fee_code"]').value = data.fee_code;
            form.querySelector('[name="fee_code"]').readOnly = true;
            form.querySelector('[name="name_en"]').value = data.name_en;
            form.querySelector('[name="name_ar"]').value = data.name_ar;
            form.querySelector('[name="category"]').value = data.category;
            form.querySelector('[name="is_recurring"]').checked = data.is_recurring == 1;
            form.querySelector('[name="is_refundable"]').checked = data.is_refundable == 1;
            form.querySelector('[name="is_active"]').checked = data.is_active == 1;
            new bootstrap.Modal(document.getElementById('fee-modal')).show();
        }
    });
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\fiscal-years\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.fiscal_years') ?></h4>
    <?php if (auth_can('invoices.create')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#fy-modal"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></button>
    <?php endif; ?>
</div>

<?php foreach ($years as $year): ?>
<div class="card mb-3">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h6 class="mb-0"><?= e($year['name_en']) ?> (<?= e($year['year_code']) ?>) â€” <?= format_date($year['start_date']) ?> to <?= format_date($year['end_date']) ?></h6>
        <?= status_badge($year['status']) ?>
    </div>
    <div class="card-body">
        <?php if (!empty($periods[$year['id']])): ?>
        <table class="table table-sm mb-0">
            <thead><tr><th>#</th><th>Name (EN)</th><th>Start</th><th>End</th><th>Status</th></tr></thead>
            <tbody>
            <?php foreach ($periods[$year['id']] as $p): ?>
            <tr>
                <td><?= $p['period_number'] ?></td>
                <td><?= e($p['name_en']) ?></td>
                <td><?= format_date($p['start_date']) ?></td>
                <td><?= format_date($p['end_date']) ?></td>
                <td><?= status_badge($p['status']) ?></td>
            </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
        <?php else: ?>
        <p class="text-muted mb-0">No periods defined yet.</p>
        <?php endif; ?>
    </div>
</div>
<?php endforeach; ?>

<?php if (empty($years)): ?>
<div class="card"><div class="card-body text-center py-5 text-muted"><i class="fas fa-calendar-alt fa-3x mb-3"></i><p>No fiscal years configured.</p></div></div>
<?php endif; ?>

<!-- Modal -->
<div class="modal fade" id="fy-modal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form method="POST" action="/finance/fiscal-years" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-header"><h5 class="modal-title">New Fiscal Year</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
                <div class="modal-body">
                    <div class="mb-3"><label class="form-label">Year Code <span class="text-danger">*</span></label><input type="text" name="year_code" class="form-control" placeholder="FY-2026" required></div>
                    <div class="mb-3"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required></div>
                    <div class="mb-3"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" required></div>
                    <div class="row">
                        <div class="col-6 mb-3"><label class="form-label">Start Date <span class="text-danger">*</span></label><input type="date" name="start_date" class="form-control" required></div>
                        <div class="col-6 mb-3"><label class="form-label">End Date <span class="text-danger">*</span></label><input type="date" name="end_date" class="form-control" required></div>
                    </div>
                    <div class="mb-3"><label class="form-label">Status</label><select name="status" class="form-select"><option value="planning">Planning</option><option value="open">Open</option></select></div>
                </div>
                <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button><button type="submit" class="btn btn-primary">Save</button></div>
            </form>
        </div>
    </div>
</div>
```

## File: modules\finance\views\invoices\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-10">
        <div class="card">
            <div class="card-header">
                <h5 class="card-title mb-0"><i class="fas fa-file-invoice me-2"></i><?= lang('finance.create_invoice') ?></h5>
            </div>
            <div class="card-body">
                <form id="invoice-form" method="POST" action="/finance/invoices" data-ajax="true">
                    <?= csrf_field() ?>

                    <div class="row g-3 mb-4">
                        <div class="col-md-5">
                            <label class="form-label"><?= lang('finance.member') ?> <span class="text-danger">*</span></label>
                            <select name="member_id" id="member-select" class="form-select" required></select>
                            <div class="invalid-feedback" data-field="member_id"></div>
                            <div id="member-info" class="mt-2 small text-muted d-none"></div>
                        </div>
                        <div class="col-md-3">
                            <label class="form-label"><?= lang('finance.invoice_type') ?> <span class="text-danger">*</span></label>
                            <select name="invoice_type" class="form-select" required>
                                <option value="registration">Registration</option>
                                <option value="subscription" selected>Subscription</option>
                                <option value="renewal">Renewal</option>
                                <option value="transfer">Transfer</option>
                                <option value="freeze">Freeze</option>
                                <option value="penalty">Penalty</option>
                                <option value="sport">Sport</option>
                                <option value="facility">Facility</option>
                                <option value="guest">Guest</option>
                                <option value="card">Card</option>
                                <option value="carnet">Carnet</option>
                                <option value="other">Other</option>
                            </select>
                        </div>
                        <div class="col-md-2">
                            <label class="form-label"><?= lang('finance.due_date') ?> <span class="text-danger">*</span></label>
                            <input type="date" name="due_date" class="form-control" value="<?= date('Y-m-d', strtotime('+30 days')) ?>" required>
                            <div class="invalid-feedback" data-field="due_date"></div>
                        </div>
                        <div class="col-md-2">
                            <label class="form-label">&nbsp;</label>
                            <div class="form-check mt-2">
                                <input type="checkbox" name="issue_now" value="1" class="form-check-input" id="issue-now">
                                <label class="form-check-label" for="issue-now"><?= lang('finance.issue_immediately') ?></label>
                            </div>
                        </div>
                    </div>

                    <h6 class="mb-3"><?= lang('finance.line_items') ?></h6>
                    <div class="table-responsive mb-3">
                        <table class="table table-bordered" id="items-table">
                            <thead class="table-light">
                                <tr>
                                    <th style="width:25%"><?= lang('finance.fee_type') ?></th>
                                    <th style="width:20%"><?= lang('finance.description') ?></th>
                                    <th style="width:8%"><?= lang('finance.qty') ?></th>
                                    <th style="width:12%"><?= lang('finance.unit_price') ?></th>
                                    <th style="width:10%"><?= lang('finance.discount') ?></th>
                                    <th style="width:8%"><?= lang('finance.tax_rate') ?>%</th>
                                    <th style="width:12%"><?= lang('finance.total') ?></th>
                                    <th style="width:5%"></th>
                                </tr>
                            </thead>
                            <tbody id="items-body">
                                <tr class="item-row">
                                    <td>
                                        <select name="items[0][fee_type_id]" class="form-select form-select-sm fee-type-select">
                                            <option value="">-- <?= lang('finance.select_fee_type') ?> --</option>
                                            <?php foreach ($feeTypes as $ft): ?>
                                            <option value="<?= $ft['id'] ?>"><?= e($ft['name_en']) ?> (<?= e($ft['fee_code']) ?>)</option>
                                            <?php endforeach; ?>
                                        </select>
                                    </td>
                                    <td><input type="text" name="items[0][description_en]" class="form-control form-control-sm" placeholder="Description"></td>
                                    <td><input type="number" name="items[0][quantity]" class="form-control form-control-sm item-qty" value="1" min="1"></td>
                                    <td><input type="number" name="items[0][unit_price]" class="form-control form-control-sm item-price" step="0.01" value="0" min="0"></td>
                                    <td><input type="number" name="items[0][discount_amount]" class="form-control form-control-sm item-discount" step="0.01" value="0" min="0"></td>
                                    <td><input type="number" name="items[0][tax_rate]" class="form-control form-control-sm item-tax" step="0.01" value="0" min="0" max="100"></td>
                                    <td><strong class="item-total">0.00</strong></td>
                                    <td><button type="button" class="btn btn-sm btn-outline-danger btn-remove-item"><i class="fas fa-trash"></i></button></td>
                                </tr>
                            </tbody>
                            <tfoot>
                                <tr>
                                    <td colspan="6" class="text-end"><strong><?= lang('finance.grand_total') ?>:</strong></td>
                                    <td><strong id="grand-total">0.00</strong></td>
                                    <td></td>
                                </tr>
                            </tfoot>
                        </table>
                    </div>
                    <button type="button" class="btn btn-sm btn-outline-success mb-3" id="add-item-btn">
                        <i class="fas fa-plus me-1"></i><?= lang('finance.add_item') ?>
                    </button>

                    <div class="mb-3">
                        <label class="form-label"><?= lang('finance.notes') ?></label>
                        <textarea name="notes" class="form-control" rows="2"></textarea>
                    </div>

                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/finance/invoices" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Member Select2
    $('#member-select').select2({
        theme: 'bootstrap-5',
        width: '100%',
        ajax: {
            url: '/finance/api/members/search',
            dataType: 'json',
            delay: 300,
            data: function(params) { return { q: params.term, page: params.page || 1 }; },
            processResults: function(data) { return data; }
        },
        minimumInputLength: 2,
        placeholder: '<?= lang('finance.search_member') ?>...'
    }).on('select2:select', function(e) {
        var d = e.params.data;
        if (d.outstanding > 0) {
            $('#member-info').removeClass('d-none').html('<span class="text-danger"><?= lang('finance.outstanding') ?>: ' + d.outstanding.toFixed(2) + ' <?= Config::get('default_currency','EGP') ?></span>');
        } else {
            $('#member-info').addClass('d-none');
        }
    });

    // Line item calculations
    var itemIndex = 1;
    function recalcRow(row) {
        var qty = parseFloat(row.querySelector('.item-qty').value) || 0;
        var price = parseFloat(row.querySelector('.item-price').value) || 0;
        var discount = parseFloat(row.querySelector('.item-discount').value) || 0;
        var taxRate = parseFloat(row.querySelector('.item-tax').value) || 0;
        var subtotal = (qty * price) - discount;
        var tax = subtotal * taxRate / 100;
        var total = subtotal + tax;
        row.querySelector('.item-total').textContent = total.toFixed(2);
        recalcGrand();
    }

    function recalcGrand() {
        var grand = 0;
        document.querySelectorAll('.item-total').forEach(function(el) {
            grand += parseFloat(el.textContent) || 0;
        });
        document.getElementById('grand-total').textContent = grand.toFixed(2);
    }

    document.getElementById('items-body').addEventListener('input', function(e) {
        var row = e.target.closest('.item-row');
        if (row) recalcRow(row);
    });

    document.getElementById('add-item-btn').addEventListener('click', function() {
        var tbody = document.getElementById('items-body');
        var firstRow = tbody.querySelector('.item-row');
        var newRow = firstRow.cloneNode(true);
        newRow.querySelectorAll('input').forEach(function(inp) {
            inp.name = inp.name.replace(/\[\d+\]/, '[' + itemIndex + ']');
            if (inp.classList.contains('item-qty')) inp.value = '1';
            else if (inp.type === 'number') inp.value = '0';
            else inp.value = '';
        });
        newRow.querySelectorAll('select').forEach(function(sel) {
            sel.name = sel.name.replace(/\[\d+\]/, '[' + itemIndex + ']');
            sel.value = '';
        });
        newRow.querySelector('.item-total').textContent = '0.00';
        tbody.appendChild(newRow);
        itemIndex++;
    });

    document.getElementById('items-body').addEventListener('click', function(e) {
        var btn = e.target.closest('.btn-remove-item');
        if (btn) {
            var rows = document.querySelectorAll('.item-row');
            if (rows.length > 1) {
                btn.closest('.item-row').remove();
                recalcGrand();
            }
        }
    });
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\invoices\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-3">
        <div class="card border-primary h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('finance.total_outstanding') ?></h6>
                        <h3 class="mb-0"><?= money($totalOutstanding) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-file-invoice-dollar fa-2x text-primary opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('finance.total_overdue') ?></h6>
                        <h3 class="mb-0 text-danger"><?= money($totalOverdue) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-exclamation-circle fa-2x text-danger opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-success h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('finance.issued_today') ?></h6>
                        <h3 class="mb-0"><?= number_format($issuedToday) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-plus-circle fa-2x text-success opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-info h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('finance.collected_today') ?></h6>
                        <h3 class="mb-0"><?= money($collectedToday) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-cash-register fa-2x text-info opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.invoices') ?></h4>
    <?php if (auth_can('invoices.create')): ?>
    <a href="/finance/invoices/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('finance.create_invoice') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3">
    <div class="card-body py-2" data-filter-table="invoices-table">
        <div class="row g-2 align-items-end">
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('common.status') ?></label>
                <select name="filter_status" class="form-select form-select-sm filter-control">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="draft"><?= lang('finance.draft') ?></option>
                    <option value="issued"><?= lang('finance.issued') ?></option>
                    <option value="partially_paid"><?= lang('finance.partially_paid') ?></option>
                    <option value="paid"><?= lang('finance.paid') ?></option>
                    <option value="overdue"><?= lang('finance.overdue') ?></option>
                    <option value="void"><?= lang('finance.void') ?></option>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('finance.type') ?></label>
                <select name="filter_type" class="form-select form-select-sm filter-control">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="registration">Registration</option>
                    <option value="subscription">Subscription</option>
                    <option value="renewal">Renewal</option>
                    <option value="penalty">Penalty</option>
                    <option value="sport">Sport</option>
                    <option value="facility">Facility</option>
                    <option value="card">Card</option>
                    <option value="other">Other</option>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('finance.date_from') ?></label>
                <input type="date" name="filter_from" class="form-control form-control-sm filter-control">
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('finance.date_to') ?></label>
                <input type="date" name="filter_to" class="form-control form-control-sm filter-control">
            </div>
            <div class="col-md-2">
                <div class="form-check mt-4">
                    <input type="checkbox" name="filter_overdue" value="1" class="form-check-input filter-control" id="filter-overdue">
                    <label class="form-check-label" for="filter-overdue"><?= lang('finance.overdue_only') ?></label>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="invoices-table" class="table table-striped table-hover w-100"
               data-url="/finance/invoices/datatable"
               data-columns='[
                   {"data":"id","title":"#","width":"40px"},
                   {"data":"invoice_number","title":"<?= lang('finance.invoice_no') ?>"},
                   {"data":"member_name","title":"<?= lang('finance.member') ?>"},
                   {"data":"invoice_type","title":"<?= lang('finance.type') ?>"},
                   {"data":"invoice_date","title":"<?= lang('finance.date') ?>"},
                   {"data":"due_date","title":"<?= lang('finance.due_date') ?>"},
                   {"data":"total_amount","title":"<?= lang('finance.total') ?>"},
                   {"data":"paid_amount","title":"<?= lang('finance.paid') ?>"},
                   {"data":"balance_due","title":"<?= lang('finance.balance') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"},
                   {"data":"actions","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}
               ]'>
            <thead>
                <tr>
                    <th>#</th>
                    <th><?= lang('finance.invoice_no') ?></th>
                    <th><?= lang('finance.member') ?></th>
                    <th><?= lang('finance.type') ?></th>
                    <th><?= lang('finance.date') ?></th>
                    <th><?= lang('finance.due_date') ?></th>
                    <th><?= lang('finance.total') ?></th>
                    <th><?= lang('finance.paid') ?></th>
                    <th><?= lang('finance.balance') ?></th>
                    <th><?= lang('common.status') ?></th>
                    <th><?= lang('common.actions') ?></th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    const dt = DataTableHelper.init('invoices-table', {order: [[0, 'desc']]});
    document.querySelectorAll('.filter-control').forEach(function(el) {
        el.addEventListener('change', function() { DataTableHelper.reload('invoices-table'); });
    });
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\invoices\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><?= lang('finance.invoice') ?> #<?= e($invoice['invoice_number']) ?></h4>
    <div class="d-flex gap-2">
        <?php if ($invoice['status'] === 'draft' && auth_can('invoices.create')): ?>
        <button class="btn btn-success btn-issue-invoice" data-id="<?= $invoice['id'] ?>"><i class="fas fa-check me-1"></i><?= lang('finance.issue') ?></button>
        <?php endif; ?>
        <?php if (in_array($invoice['status'], ['draft','issued']) && auth_can('invoices.void')): ?>
        <button class="btn btn-danger btn-void-invoice" data-id="<?= $invoice['id'] ?>"><i class="fas fa-ban me-1"></i><?= lang('finance.void') ?></button>
        <?php endif; ?>
        <?php if (in_array($invoice['status'], ['issued','partially_paid','overdue']) && auth_can('payments.create')): ?>
        <a href="/finance/payments/create?invoice_id=<?= $invoice['id'] ?>&member_id=<?= $invoice['member_id'] ?>" class="btn btn-primary"><i class="fas fa-money-bill me-1"></i><?= lang('finance.record_payment') ?></a>
        <?php endif; ?>
        <button class="btn btn-outline-secondary" onclick="window.print()"><i class="fas fa-print me-1"></i><?= lang('common.print') ?></button>
        <a href="/finance/invoices" class="btn btn-outline-secondary"><?= lang('common.back') ?></a>
    </div>
</div>

<div class="card mb-4" id="invoice-print-area">
    <div class="card-body p-4">
        <!-- Header -->
        <div class="row mb-4">
            <div class="col-6">
                <h4><?= e($clubName) ?></h4>
                <p class="text-muted mb-0"><?= e($clubAddress) ?></p>
                <p class="text-muted mb-0"><?= e($clubPhone) ?></p>
            </div>
            <div class="col-6 text-end">
                <h2 class="text-primary mb-1"><?= lang('finance.invoice') ?></h2>
                <p class="mb-0"><strong>#<?= e($invoice['invoice_number']) ?></strong></p>
                <p class="mb-0"><?= lang('finance.date') ?>: <?= format_date($invoice['invoice_date']) ?></p>
                <p class="mb-0"><?= lang('finance.due_date') ?>: <?= format_date($invoice['due_date']) ?></p>
                <p class="mt-1"><?= status_badge($invoice['status']) ?></p>
            </div>
        </div>

        <!-- Member Info -->
        <div class="card bg-light mb-4">
            <div class="card-body py-2">
                <div class="row">
                    <div class="col-md-6">
                        <strong><?= lang('finance.bill_to') ?>:</strong><br>
                        <?= e($invoice['member_name']) ?><br>
                        <small class="text-muted"><?= e($invoice['membership_number']) ?> | <?= e($invoice['membership_type'] ?? '') ?></small><br>
                        <?php if ($invoice['primary_phone']): ?><small><?= e($invoice['primary_phone']) ?></small><br><?php endif; ?>
                        <?php if ($invoice['primary_email']): ?><small><?= e($invoice['primary_email']) ?></small><?php endif; ?>
                    </div>
                    <div class="col-md-6 text-end">
                        <strong><?= lang('finance.type') ?>:</strong> <?= e(ucfirst(str_replace('_',' ',$invoice['invoice_type']))) ?><br>
                        <strong><?= lang('finance.currency') ?>:</strong> <?= e($invoice['currency_code'] ?? 'EGP') ?>
                    </div>
                </div>
            </div>
        </div>

        <!-- Line Items -->
        <table class="table table-bordered mb-4">
            <thead class="table-light">
                <tr>
                    <th>#</th>
                    <th><?= lang('finance.description') ?></th>
                    <th class="text-center"><?= lang('finance.qty') ?></th>
                    <th class="text-end"><?= lang('finance.unit_price') ?></th>
                    <th class="text-end"><?= lang('finance.discount') ?></th>
                    <th class="text-end"><?= lang('finance.tax') ?></th>
                    <th class="text-end"><?= lang('finance.total') ?></th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($items as $i => $item): ?>
                <tr>
                    <td><?= $i + 1 ?></td>
                    <td><?= e($item['description_en']) ?><?php if ($item['fee_type_name']): ?> <small class="text-muted">(<?= e($item['fee_code']) ?>)</small><?php endif; ?></td>
                    <td class="text-center"><?= $item['quantity'] ?></td>
                    <td class="text-end"><?= format_number($item['unit_price']) ?></td>
                    <td class="text-end"><?= format_number($item['discount_amount']) ?></td>
                    <td class="text-end"><?= format_number($item['tax_amount']) ?></td>
                    <td class="text-end"><strong><?= format_number($item['total_amount']) ?></strong></td>
                </tr>
                <?php endforeach; ?>
            </tbody>
            <tfoot>
                <tr>
                    <td colspan="6" class="text-end"><?= lang('finance.subtotal') ?>:</td>
                    <td class="text-end"><?= format_number($invoice['subtotal']) ?></td>
                </tr>
                <?php if ((float)$invoice['discount_total'] > 0): ?>
                <tr>
                    <td colspan="6" class="text-end text-success"><?= lang('finance.discount') ?>:</td>
                    <td class="text-end text-success">-<?= format_number($invoice['discount_total']) ?></td>
                </tr>
                <?php endif; ?>
                <?php if ((float)$invoice['tax_total'] > 0): ?>
                <tr>
                    <td colspan="6" class="text-end"><?= lang('finance.tax') ?>:</td>
                    <td class="text-end"><?= format_number($invoice['tax_total']) ?></td>
                </tr>
                <?php endif; ?>
                <tr class="table-primary">
                    <td colspan="6" class="text-end"><strong><?= lang('finance.total_amount') ?>:</strong></td>
                    <td class="text-end"><strong><?= money($invoice['total_amount']) ?></strong></td>
                </tr>
                <tr>
                    <td colspan="6" class="text-end"><?= lang('finance.paid') ?>:</td>
                    <td class="text-end text-success"><?= money($invoice['paid_amount']) ?></td>
                </tr>
                <tr class="table-warning">
                    <td colspan="6" class="text-end"><strong><?= lang('finance.balance_due') ?>:</strong></td>
                    <td class="text-end"><strong class="text-danger"><?= money($invoice['balance_due']) ?></strong></td>
                </tr>
            </tfoot>
        </table>

        <?php if ($invoice['notes']): ?>
        <div class="mb-3">
            <strong><?= lang('finance.notes') ?>:</strong>
            <p class="text-muted mb-0"><?= nl2br(e($invoice['notes'])) ?></p>
        </div>
        <?php endif; ?>
    </div>
</div>

<!-- Payment History -->
<?php if (!empty($payments)): ?>
<div class="card">
    <div class="card-header"><h6 class="mb-0"><?= lang('finance.payment_history') ?></h6></div>
    <div class="card-body">
        <table class="table table-sm">
            <thead><tr><th><?= lang('finance.payment_no') ?></th><th><?= lang('finance.date') ?></th><th><?= lang('finance.method') ?></th><th class="text-end"><?= lang('finance.amount') ?></th><th><?= lang('common.status') ?></th></tr></thead>
            <tbody>
            <?php foreach ($payments as $p): ?>
            <tr>
                <td><?= e($p['payment_number']) ?></td>
                <td><?= format_date($p['payment_date']) ?></td>
                <td><?= e($p['method_name'] ?? '') ?></td>
                <td class="text-end"><?= money($p['allocated_amount']) ?></td>
                <td><?= status_badge($p['payment_status']) ?></td>
            </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</div>
<?php endif; ?>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    document.querySelectorAll('.btn-issue-invoice').forEach(function(btn) {
        btn.addEventListener('click', function() {
            var id = this.dataset.id;
            Swal.fire({
                title: '<?= lang('finance.confirm_issue') ?>',
                text: '<?= lang('finance.confirm_issue_text') ?>',
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: '<?= lang('finance.issue') ?>',
                cancelButtonText: '<?= lang('common.cancel') ?>'
            }).then(function(result) {
                if (result.isConfirmed) {
                    $.ajax({
                        url: '/finance/invoices/' + id + '/issue',
                        type: 'POST',
                        data: { _token: '<?= csrf_token() ?>' },
                        success: function(r) {
                            if (r.success) {
                                FormHelper.showToast('success', r.message);
                                setTimeout(function() { location.reload(); }, 800);
                            } else {
                                FormHelper.showToast('error', r.message);
                            }
                        }
                    });
                }
            });
        });
    });

    document.querySelectorAll('.btn-void-invoice').forEach(function(btn) {
        btn.addEventListener('click', function() {
            var id = this.dataset.id;
            Swal.fire({
                title: '<?= lang('finance.void_invoice') ?>',
                input: 'textarea',
                inputPlaceholder: '<?= lang('finance.void_reason') ?>',
                inputValidator: function(v) { if (!v) return '<?= lang('finance.reason_required') ?>'; },
                showCancelButton: true,
                confirmButtonText: '<?= lang('finance.void') ?>',
                confirmButtonColor: '#dc3545',
                cancelButtonText: '<?= lang('common.cancel') ?>'
            }).then(function(result) {
                if (result.isConfirmed) {
                    $.ajax({
                        url: '/finance/invoices/' + id + '/void',
                        type: 'POST',
                        data: { _token: '<?= csrf_token() ?>', reason: result.value },
                        success: function(r) {
                            if (r.success) {
                                FormHelper.showToast('success', r.message);
                                setTimeout(function() { location.reload(); }, 800);
                            } else {
                                FormHelper.showToast('error', r.message);
                            }
                        }
                    });
                }
            });
        });
    });
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\payment-methods\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.payment_methods') ?></h4>
    <?php if (auth_can('payments.create')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#method-modal" id="btn-new-method"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></button>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="methods-table" class="table table-striped w-100"
               data-url="/finance/payment-methods/datatable"
               data-columns='[{"data":"id","title":"#"},{"data":"method_code","title":"Code"},{"data":"name_en","title":"Name (EN)"},{"data":"name_ar","title":"Name (AR)"},{"data":"method_type","title":"Type"},{"data":"is_active","title":"Status"},{"data":"actions","title":"","orderable":false}]'>
            <thead><tr><th>#</th><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Type</th><th>Status</th><th></th></tr></thead>
        </table>
    </div>
</div>

<div class="modal fade" id="method-modal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form id="method-form" method="POST" action="/finance/payment-methods" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-header"><h5 class="modal-title" id="method-modal-title">Payment Method</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
                <div class="modal-body">
                    <div class="mb-3"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="method_code" class="form-control" required><div class="invalid-feedback" data-field="method_code"></div></div>
                    <div class="mb-3"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required></div>
                    <div class="mb-3"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" required></div>
                    <div class="mb-3"><label class="form-label">Type <span class="text-danger">*</span></label>
                        <select name="method_type" class="form-select" required>
                            <?php foreach (['cash','check','bank_transfer','credit_card','debit_card','online','wallet','other'] as $t): ?>
                            <option value="<?= $t ?>"><?= ucfirst(str_replace('_',' ',$t)) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="form-check mb-3"><input type="checkbox" name="requires_reference" value="1" class="form-check-input" id="pm-ref"><label class="form-check-label" for="pm-ref">Requires Reference #</label></div>
                    <div class="form-check"><input type="checkbox" name="is_active" value="1" class="form-check-input" id="pm-active" checked><label class="form-check-label" for="pm-active">Active</label></div>
                </div>
                <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.close') ?></button><button type="submit" class="btn btn-primary"><?= lang('common.save') ?></button></div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('methods-table');
    document.getElementById('btn-new-method').addEventListener('click', function() {
        document.getElementById('method-form').action = '/finance/payment-methods';
        document.getElementById('method-form').reset();
        document.querySelector('#method-modal [name="method_code"]').readOnly = false;
    });
    document.addEventListener('click', function(e) {
        var btn = e.target.closest('.btn-edit-method');
        if (btn) {
            var d = JSON.parse(btn.dataset.method);
            var f = document.getElementById('method-form');
            f.action = '/finance/payment-methods/' + d.id;
            f.querySelector('[name="method_code"]').value = d.method_code;
            f.querySelector('[name="method_code"]').readOnly = true;
            f.querySelector('[name="name_en"]').value = d.name_en;
            f.querySelector('[name="name_ar"]').value = d.name_ar;
            f.querySelector('[name="method_type"]').value = d.method_type;
            f.querySelector('[name="requires_reference"]').checked = d.requires_reference == 1;
            f.querySelector('[name="is_active"]').checked = d.is_active == 1;
            new bootstrap.Modal(document.getElementById('method-modal')).show();
        }
    });
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\payments\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header">
                <h5 class="card-title mb-0"><i class="fas fa-money-bill-wave me-2"></i><?= lang('finance.record_payment') ?></h5>
            </div>
            <div class="card-body">
                <form id="payment-form" method="POST" action="/finance/payments" data-ajax="true">
                    <?= csrf_field() ?>

                    <div class="row g-3">
                        <div class="col-md-8">
                            <label class="form-label"><?= lang('finance.member') ?> <span class="text-danger">*</span></label>
                            <select name="member_id" id="pay-member-select" class="form-select" required>
                                <?php if ($member): ?>
                                <option value="<?= $member['id'] ?>" selected><?= e($member['membership_number'] . ' - ' . $member['full_name_en']) ?></option>
                                <?php endif; ?>
                            </select>
                            <div class="invalid-feedback" data-field="member_id"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('finance.amount') ?> <span class="text-danger">*</span></label>
                            <input type="number" name="amount" class="form-control" step="0.01" min="0.01" required
                                   value="<?= $invoice ? e($invoice['balance_due']) : '' ?>">
                            <div class="invalid-feedback" data-field="amount"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('finance.payment_method') ?> <span class="text-danger">*</span></label>
                            <select name="payment_method_id" class="form-select" required>
                                <?php foreach ($methods as $m): ?>
                                <option value="<?= $m['id'] ?>" data-type="<?= $m['method_type'] ?>" data-ref="<?= $m['requires_reference'] ?>"><?= e($m['name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('finance.payment_date') ?> <span class="text-danger">*</span></label>
                            <input type="date" name="payment_date" class="form-control" value="<?= date('Y-m-d') ?>" required>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label"><?= lang('finance.reference_number') ?></label>
                            <input type="text" name="reference_number" class="form-control">
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('finance.allocate_to_invoice') ?></label>
                            <select name="invoice_id" id="invoice-select" class="form-select">
                                <option value=""><?= lang('finance.auto_allocate') ?></option>
                                <?php if ($invoice): ?>
                                <option value="<?= $invoice['id'] ?>" selected>#<?= e($invoice['invoice_number']) ?> - <?= lang('finance.balance') ?>: <?= money($invoice['balance_due']) ?></option>
                                <?php endif; ?>
                            </select>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label"><?= lang('finance.cash_register_session') ?></label>
                            <select name="cash_register_session_id" class="form-select">
                                <option value="">-- <?= lang('finance.no_session') ?> --</option>
                                <?php foreach ($registers as $reg): ?>
                                    <?php if ($reg['session_id']): ?>
                                    <option value="<?= $reg['session_id'] ?>"><?= e($reg['name_en']) ?> (<?= lang('finance.open') ?>)</option>
                                    <?php endif; ?>
                                <?php endforeach; ?>
                            </select>
                        </div>
                    </div>

                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('finance.record_payment') ?></button>
                        <a href="/finance/payments" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    <?php if (!$member): ?>
    $('#pay-member-select').select2({
        theme: 'bootstrap-5', width: '100%',
        ajax: {
            url: '/finance/api/members/search', dataType: 'json', delay: 300,
            data: function(params) { return { q: params.term, page: params.page || 1 }; },
            processResults: function(data) { return data; }
        },
        minimumInputLength: 2,
        placeholder: '<?= lang('finance.search_member') ?>...'
    }).on('select2:select', function(e) {
        // Load outstanding invoices for this member
        $.get('/finance/api/invoices/for-member', { member_id: e.params.data.id }, function(response) {
            var sel = document.getElementById('invoice-select');
            sel.innerHTML = '<option value=""><?= lang('finance.auto_allocate') ?></option>';
            if (response.data) {
                response.data.forEach(function(inv) {
                    var opt = document.createElement('option');
                    opt.value = inv.id;
                    opt.textContent = '#' + inv.invoice_number + ' - Balance: ' + parseFloat(inv.balance_due).toFixed(2);
                    sel.appendChild(opt);
                });
            }
        });
    });
    <?php endif; ?>
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\payments\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.payments') ?></h4>
    <?php if (auth_can('payments.create')): ?>
    <a href="/finance/payments/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('finance.record_payment') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3">
    <div class="card-body py-2" data-filter-table="payments-table">
        <div class="row g-2 align-items-end">
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('finance.method') ?></label>
                <select name="filter_method" class="form-select form-select-sm filter-control">
                    <option value=""><?= lang('common.all') ?></option>
                    <?php foreach ($methods as $m): ?>
                    <option value="<?= $m['id'] ?>"><?= e($m['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('common.status') ?></label>
                <select name="filter_status" class="form-select form-select-sm filter-control">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="completed">Completed</option>
                    <option value="pending">Pending</option>
                    <option value="cancelled">Cancelled</option>
                    <option value="reversed">Reversed</option>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('finance.date_from') ?></label>
                <input type="date" name="filter_from" class="form-control form-control-sm filter-control">
            </div>
            <div class="col-md-2">
                <label class="form-label form-label-sm"><?= lang('finance.date_to') ?></label>
                <input type="date" name="filter_to" class="form-control form-control-sm filter-control">
            </div>
        </div>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="payments-table" class="table table-striped table-hover w-100"
               data-url="/finance/payments/datatable"
               data-columns='[
                   {"data":"id","title":"#","width":"40px"},
                   {"data":"payment_number","title":"<?= lang('finance.payment_no') ?>"},
                   {"data":"member_name","title":"<?= lang('finance.member') ?>"},
                   {"data":"amount","title":"<?= lang('finance.amount') ?>"},
                   {"data":"method_name","title":"<?= lang('finance.method') ?>"},
                   {"data":"payment_date","title":"<?= lang('finance.date') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"},
                   {"data":"actions","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}
               ]'>
            <thead><tr><th>#</th><th><?= lang('finance.payment_no') ?></th><th><?= lang('finance.member') ?></th><th><?= lang('finance.amount') ?></th><th><?= lang('finance.method') ?></th><th><?= lang('finance.date') ?></th><th><?= lang('common.status') ?></th><th><?= lang('common.actions') ?></th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('payments-table', {order:[[0,'desc']]});
    document.querySelectorAll('.filter-control').forEach(function(el) {
        el.addEventListener('change', function() { DataTableHelper.reload('payments-table'); });
    });
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\payments\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><?= lang('finance.payment') ?> #<?= e($payment['payment_number']) ?></h4>
    <div class="d-flex gap-2">
        <?php if ($receipt): ?>
        <a href="/finance/receipts/<?= $receipt['id'] ?>/print" target="_blank" class="btn btn-outline-info"><i class="fas fa-print me-1"></i><?= lang('finance.print_receipt') ?></a>
        <?php endif; ?>
        <a href="/finance/payments" class="btn btn-outline-secondary"><?= lang('common.back') ?></a>
    </div>
</div>

<div class="row g-4">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('finance.payment_details') ?></h6></div>
            <div class="card-body">
                <table class="table table-borderless mb-0">
                    <tr><th width="40%"><?= lang('finance.payment_no') ?></th><td><?= e($payment['payment_number']) ?></td></tr>
                    <tr><th><?= lang('finance.amount') ?></th><td><strong class="text-success"><?= money($payment['amount']) ?></strong></td></tr>
                    <tr><th><?= lang('finance.method') ?></th><td><?= e($payment['method_name']) ?></td></tr>
                    <tr><th><?= lang('finance.date') ?></th><td><?= format_date($payment['payment_date']) ?></td></tr>
                    <tr><th><?= lang('common.status') ?></th><td><?= status_badge($payment['status']) ?></td></tr>
                    <?php if ($payment['reference_number']): ?>
                    <tr><th><?= lang('finance.reference') ?></th><td><?= e($payment['reference_number']) ?></td></tr>
                    <?php endif; ?>
                    <?php if ($payment['notes']): ?>
                    <tr><th><?= lang('finance.notes') ?></th><td><?= e($payment['notes']) ?></td></tr>
                    <?php endif; ?>
                </table>
            </div>
        </div>
    </div>
    <div class="col-md-6">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><?= lang('finance.member') ?></h6></div>
            <div class="card-body">
                <table class="table table-borderless mb-0">
                    <tr><th width="40%"><?= lang('finance.name') ?></th><td><?= e($payment['member_name']) ?></td></tr>
                    <tr><th><?= lang('finance.membership_no') ?></th><td><?= e($payment['membership_number']) ?></td></tr>
                </table>
            </div>
        </div>

        <?php if (!empty($allocations)): ?>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0"><?= lang('finance.allocated_to') ?></h6></div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <thead><tr><th><?= lang('finance.invoice') ?></th><th class="text-end"><?= lang('finance.allocated') ?></th></tr></thead>
                    <tbody>
                    <?php foreach ($allocations as $a): ?>
                    <tr><td><?= e($a['invoice_number']) ?></td><td class="text-end"><?= money($a['allocated_amount']) ?></td></tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>

        <?php if ($receipt): ?>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0"><?= lang('finance.receipt') ?></h6></div>
            <div class="card-body">
                <p class="mb-0"><?= lang('finance.receipt_no') ?>: <a href="/finance/receipts/<?= $receipt['id'] ?>">#<?= e($receipt['receipt_number']) ?></a></p>
            </div>
        </div>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\finance\views\receipts\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('finance.receipts') ?></h4>
</div>

<div class="card">
    <div class="card-body">
        <table id="receipts-table" class="table table-striped table-hover w-100"
               data-url="/finance/receipts/datatable"
               data-columns='[
                   {"data":"id","title":"#","width":"40px"},
                   {"data":"receipt_number","title":"<?= lang('finance.receipt_no') ?>"},
                   {"data":"member_name","title":"<?= lang('finance.member') ?>"},
                   {"data":"total_amount","title":"<?= lang('finance.amount') ?>"},
                   {"data":"receipt_date","title":"<?= lang('finance.date') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"},
                   {"data":"actions","title":"<?= lang('common.actions') ?>","orderable":false,"searchable":false}
               ]'>
            <thead><tr><th>#</th><th><?= lang('finance.receipt_no') ?></th><th><?= lang('finance.member') ?></th><th><?= lang('finance.amount') ?></th><th><?= lang('finance.date') ?></th><th><?= lang('common.status') ?></th><th><?= lang('common.actions') ?></th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('receipts-table', {order:[[0,'desc']]}); });</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\receipts\print.php

```php
<div style="max-width:800px;margin:0 auto;padding:20px;font-family:Arial,sans-serif;">
    <div style="text-align:center;margin-bottom:20px;">
        <h2 style="margin:0;"><?= e($clubName) ?></h2>
        <p style="margin:2px 0;color:#666;"><?= e($clubNameAr) ?></p>
        <p style="margin:2px 0;color:#666;font-size:12px;"><?= e($clubAddress) ?> | <?= e($clubPhone) ?></p>
        <hr>
        <h3><?= lang('finance.receipt') ?></h3>
    </div>

    <table style="width:100%;margin-bottom:15px;">
        <tr>
            <td><strong><?= lang('finance.receipt_no') ?>:</strong> <?= e($receipt['receipt_number']) ?></td>
            <td style="text-align:right;"><strong><?= lang('finance.date') ?>:</strong> <?= format_date($receipt['receipt_date']) ?></td>
        </tr>
        <tr>
            <td><strong><?= lang('finance.member') ?>:</strong> <?= e($receipt['member_name']) ?> (<?= e($receipt['membership_number']) ?>)</td>
            <td style="text-align:right;"><strong><?= lang('finance.method') ?>:</strong> <?= e($receipt['method_name'] ?? '') ?></td>
        </tr>
    </table>

    <table style="width:100%;border-collapse:collapse;margin-bottom:15px;">
        <thead>
            <tr style="background:#f0f0f0;">
                <th style="border:1px solid #ddd;padding:6px;">#</th>
                <th style="border:1px solid #ddd;padding:6px;"><?= lang('finance.description') ?></th>
                <th style="border:1px solid #ddd;padding:6px;text-align:right;"><?= lang('finance.amount') ?></th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($items as $i => $item): ?>
            <tr>
                <td style="border:1px solid #ddd;padding:6px;"><?= $i + 1 ?></td>
                <td style="border:1px solid #ddd;padding:6px;"><?= e($item['description_en']) ?></td>
                <td style="border:1px solid #ddd;padding:6px;text-align:right;"><?= format_number($item['amount']) ?></td>
            </tr>
            <?php endforeach; ?>
        </tbody>
        <tfoot>
            <tr style="background:#e8f0fe;">
                <td colspan="2" style="border:1px solid #ddd;padding:8px;text-align:right;"><strong><?= lang('finance.total') ?>:</strong></td>
                <td style="border:1px solid #ddd;padding:8px;text-align:right;"><strong><?= money($receipt['total_amount']) ?></strong></td>
            </tr>
        </tfoot>
    </table>

    <div style="margin-top:40px;">
        <table style="width:100%;">
            <tr>
                <td style="text-align:center;width:50%;">
                    <div style="border-top:1px solid #333;width:150px;margin:40px auto 5px;">&nbsp;</div>
                    <small><?= lang('finance.cashier_signature') ?></small>
                </td>
                <td style="text-align:center;width:50%;">
                    <div style="border-top:1px solid #333;width:150px;margin:40px auto 5px;">&nbsp;</div>
                    <small><?= lang('finance.member_signature') ?></small>
                </td>
            </tr>
        </table>
    </div>
</div>
<script>window.onload = function() { window.print(); };</script>
```

## File: modules\finance\views\receipts\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><?= lang('finance.receipt') ?> #<?= e($receipt['receipt_number']) ?></h4>
    <div class="d-flex gap-2">
        <?php if (auth_can('receipts.print')): ?>
        <a href="/finance/receipts/<?= $receipt['id'] ?>/print" target="_blank" class="btn btn-info"><i class="fas fa-print me-1"></i><?= lang('common.print') ?></a>
        <?php endif; ?>
        <a href="/finance/receipts" class="btn btn-outline-secondary"><?= lang('common.back') ?></a>
    </div>
</div>

<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-body p-4">
                <div class="text-center mb-4">
                    <h4><?= e($clubName) ?></h4>
                    <p class="text-muted mb-0"><?= e($clubAddress) ?></p>
                </div>
                <hr>
                <div class="row mb-3">
                    <div class="col-6">
                        <strong><?= lang('finance.receipt_no') ?>:</strong> <?= e($receipt['receipt_number']) ?><br>
                        <strong><?= lang('finance.date') ?>:</strong> <?= format_date($receipt['receipt_date']) ?>
                    </div>
                    <div class="col-6 text-end">
                        <strong><?= lang('finance.member') ?>:</strong> <?= e($receipt['member_name']) ?><br>
                        <strong><?= lang('finance.membership_no') ?>:</strong> <?= e($receipt['membership_number']) ?>
                    </div>
                </div>

                <table class="table table-bordered">
                    <thead class="table-light">
                        <tr><th>#</th><th><?= lang('finance.description') ?></th><th class="text-end"><?= lang('finance.amount') ?></th></tr>
                    </thead>
                    <tbody>
                        <?php foreach ($items as $i => $item): ?>
                        <tr><td><?= $i + 1 ?></td><td><?= e($item['description_en']) ?></td><td class="text-end"><?= format_number($item['amount']) ?></td></tr>
                        <?php endforeach; ?>
                    </tbody>
                    <tfoot>
                        <tr class="table-primary"><td colspan="2" class="text-end"><strong><?= lang('finance.total') ?>:</strong></td><td class="text-end"><strong><?= money($receipt['total_amount']) ?></strong></td></tr>
                    </tfoot>
                </table>

                <p class="mb-0"><strong><?= lang('finance.payment_method') ?>:</strong> <?= e($receipt['method_name'] ?? 'N/A') ?></p>
                <p class="mt-2"><?= status_badge($receipt['status']) ?></p>
            </div>
        </div>
    </div>
</div>
```

## File: modules\finance\views\reports\daily-revenue.php

```php
<h4 class="mb-4"><?= lang('finance.daily_revenue') ?></h4>

<div class="card mb-3">
    <div class="card-body py-2">
        <div class="row g-2 align-items-end">
            <div class="col-md-3">
                <label class="form-label form-label-sm"><?= lang('finance.date') ?></label>
                <input type="date" id="revenue-date" class="form-control form-control-sm" value="<?= date('Y-m-d') ?>">
            </div>
            <div class="col-md-2">
                <button class="btn btn-sm btn-primary" id="load-revenue"><?= lang('common.filter') ?></button>
            </div>
        </div>
    </div>
</div>

<div class="row g-3 mb-4">
    <div class="col-md-4">
        <div class="card border-success"><div class="card-body text-center"><small class="text-muted"><?= lang('finance.grand_total') ?></small><br><h3 id="grand-total-display" class="text-success mb-0">0.00</h3></div></div>
    </div>
    <div class="col-md-4">
        <div class="card border-info"><div class="card-body text-center"><small class="text-muted"><?= lang('finance.transactions') ?></small><br><h3 id="trans-count-display" class="mb-0">0</h3></div></div>
    </div>
    <div class="col-md-4">
        <div class="card"><div class="card-body"><canvas id="revenue-chart" height="100"></canvas></div></div>
    </div>
</div>

<div class="card">
    <div class="card-header"><h6 class="mb-0"><?= lang('finance.breakdown_by_method') ?></h6></div>
    <div class="card-body">
        <table class="table" id="revenue-table">
            <thead><tr><th><?= lang('finance.method') ?></th><th><?= lang('finance.type') ?></th><th class="text-center"><?= lang('finance.count') ?></th><th class="text-end"><?= lang('finance.total') ?></th></tr></thead>
            <tbody></tbody>
        </table>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    var chart = null;

    function loadRevenue() {
        var date = document.getElementById('revenue-date').value;
        $.get('/finance/reports/daily-revenue/data', { date: date }, function(r) {
            document.getElementById('grand-total-display').textContent = parseFloat(r.grandTotal).toFixed(2) + ' <?= Config::get('default_currency','EGP') ?>';
            document.getElementById('trans-count-display').textContent = r.transactionCount;

            var tbody = document.querySelector('#revenue-table tbody');
            tbody.innerHTML = '';
            if (r.summary.length === 0) {
                tbody.innerHTML = '<tr><td colspan="4" class="text-center text-muted">No transactions</td></tr>';
            }
            r.summary.forEach(function(row) {
                tbody.innerHTML += '<tr><td>' + row.method + '</td><td>' + row.method_type + '</td><td class="text-center">' + row.count + '</td><td class="text-end"><strong>' + parseFloat(row.total).toFixed(2) + '</strong></td></tr>';
            });

            // Chart
            if (chart) chart.destroy();
            var labels = r.chartData.map(function(d) { return d.payment_date; });
            var data = r.chartData.map(function(d) { return parseFloat(d.total); });
            chart = new Chart(document.getElementById('revenue-chart'), {
                type: 'bar',
                data: { labels: labels, datasets: [{ label: 'Revenue', data: data, backgroundColor: '#28a745' }] },
                options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }
            });
        });
    }

    document.getElementById('load-revenue').addEventListener('click', loadRevenue);
    loadRevenue();
});
</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\reports\overdue.php

```php
<h4 class="mb-4"><?= lang('finance.overdue_invoices') ?></h4>

<div class="card">
    <div class="card-body">
        <table id="overdue-table" class="table table-striped w-100"
               data-url="/finance/reports/overdue/datatable"
               data-columns='[
                   {"data":"invoice_number","title":"<?= lang('finance.invoice_no') ?>"},
                   {"data":"member_name","title":"<?= lang('finance.member') ?>"},
                   {"data":"invoice_type","title":"<?= lang('finance.type') ?>"},
                   {"data":"total_amount","title":"<?= lang('finance.total') ?>"},
                   {"data":"balance_due","title":"<?= lang('finance.balance') ?>"},
                   {"data":"due_date","title":"<?= lang('finance.due_date') ?>"},
                   {"data":"days_overdue","title":"<?= lang('finance.days_overdue') ?>"},
                   {"data":"aging_bucket","title":"<?= lang('finance.aging') ?>"},
                   {"data":"actions","title":"","orderable":false}
               ]'>
            <thead><tr><th>Invoice</th><th>Member</th><th>Type</th><th>Total</th><th>Balance</th><th>Due</th><th>Days</th><th>Aging</th><th></th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts'); ?>
<script>document.addEventListener('DOMContentLoaded', function() { DataTableHelper.init('overdue-table'); });</script>
<?php $__view->endPush(); ?>
```

## File: modules\finance\views\statement\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= lang('finance.member_statement') ?></h4>
        <p class="text-muted mb-0"><?= e($member['full_name_en']) ?> (<?= e($member['membership_number']) ?>) â€” <?= e($member['membership_type'] ?? '') ?></p>
    </div>
    <div class="d-flex gap-2">
        <button class="btn btn-outline-secondary" onclick="window.print()"><i class="fas fa-print me-1"></i><?= lang('common.print') ?></button>
    </div>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <form method="GET" action="/finance/statement/<?= $member['id'] ?>" class="row g-2 align-items-end">
            <div class="col-md-3"><label class="form-label form-label-sm"><?= lang('finance.date_from') ?></label><input type="date" name="date_from" class="form-control form-control-sm" value="<?= e($dateFrom) ?>"></div>
            <div class="col-md-3"><label class="form-label form-label-sm"><?= lang('finance.date_to') ?></label><input type="date" name="date_to" class="form-control form-control-sm" value="<?= e($dateTo) ?>"></div>
            <div class="col-md-2"><button type="submit" class="btn btn-sm btn-primary"><?= lang('common.filter') ?></button></div>
        </form>
    </div>
</div>

<div class="row g-3 mb-3">
    <div class="col-md-3"><div class="card border-primary"><div class="card-body py-2 text-center"><small class="text-muted">Outstanding</small><br><strong class="text-danger"><?= money($member['outstanding_balance']) ?></strong></div></div></div>
    <div class="col-md-3"><div class="card border-success"><div class="card-body py-2 text-center"><small class="text-muted">Credit Balance</small><br><strong class="text-success"><?= money($member['credit_balance']) ?></strong></div></div></div>
    <div class="col-md-3"><div class="card border-info"><div class="card-body py-2 text-center"><small class="text-muted">Total Paid</small><br><strong><?= money($member['total_paid']) ?></strong></div></div></div>
    <div class="col-md-3"><div class="card border-warning"><div class="card-body py-2 text-center"><small class="text-muted">Overdue</small><br><strong class="text-warning"><?= money($balance['overdue_amount'] ?? 0) ?></strong></div></div></div>
</div>

<div class="card">
    <div class="card-body">
        <table class="table table-sm table-striped">
            <thead><tr><th><?= lang('finance.date') ?></th><th><?= lang('finance.type') ?></th><th><?= lang('finance.description') ?></th><th class="text-end"><?= lang('finance.debit') ?></th><th class="text-end"><?= lang('finance.credit') ?></th><th class="text-end"><?= lang('finance.balance') ?></th></tr></thead>
            <tbody>
            <?php if (empty($ledger)): ?>
            <tr><td colspan="6" class="text-center text-muted py-4">No transactions found for this period.</td></tr>
            <?php endif; ?>
            <?php foreach ($ledger as $entry): ?>
            <tr>
                <td><?= format_date($entry['transaction_date']) ?></td>
                <td><?= status_badge($entry['transaction_type']) ?></td>
                <td><?= e($entry['description_en']) ?></td>
                <td class="text-end"><?= (float)$entry['debit_amount'] > 0 ? '<span class="text-danger">' . format_number($entry['debit_amount']) . '</span>' : '' ?></td>
                <td class="text-end"><?= (float)$entry['credit_amount'] > 0 ? '<span class="text-success">' . format_number($entry['credit_amount']) . '</span>' : '' ?></td>
                <td class="text-end"><strong><?= format_number($entry['balance_after']) ?></strong></td>
            </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</div>
```

## File: modules\finance\views\widgets\outstanding_balance.php

```php
<?php
if (!auth_can('invoices.view')) return;
$db = Database::getInstance();
$outstanding = (float)$db->fetchColumn("SELECT COALESCE(SUM(balance_due),0) FROM invoices WHERE status IN ('issued','partially_paid','overdue') AND is_deleted=0");
?>
<div class="card border-warning h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('finance.total_outstanding') ?></h6>
                <h3 class="mb-0"><?= money($outstanding) ?></h3>
            </div>
            <div class="align-self-center"><i class="fas fa-file-invoice-dollar fa-2x text-warning opacity-50"></i></div>
        </div>
    </div>
</div>
```

## File: modules\finance\views\widgets\overdue_invoices.php

```php
<?php
if (!auth_can('invoices.view')) return;
$db = Database::getInstance();
$overdueCount = (int)$db->fetchColumn("SELECT COUNT(*) FROM invoices WHERE status='overdue' AND is_deleted=0");
$overdueAmount = (float)$db->fetchColumn("SELECT COALESCE(SUM(balance_due),0) FROM invoices WHERE status='overdue' AND is_deleted=0");
?>
<div class="card border-danger h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('finance.overdue_invoices') ?></h6>
                <h3 class="mb-0"><?= $overdueCount ?></h3>
                <small class="text-danger"><?= money($overdueAmount) ?></small>
            </div>
            <div class="align-self-center"><i class="fas fa-exclamation-triangle fa-2x text-danger opacity-50"></i></div>
        </div>
    </div>
</div>
```

## File: modules\finance\views\widgets\revenue_today.php

```php
<?php
if (!auth_can('payments.view')) return;
$db = Database::getInstance();
$todayRevenue = (float)$db->fetchColumn("SELECT COALESCE(SUM(amount),0) FROM payments WHERE payment_date=CURDATE() AND status='completed' AND is_deleted=0");
$yesterdayRevenue = (float)$db->fetchColumn("SELECT COALESCE(SUM(amount),0) FROM payments WHERE payment_date=DATE_SUB(CURDATE(), INTERVAL 1 DAY) AND status='completed' AND is_deleted=0");
$trend = $yesterdayRevenue > 0 ? round(($todayRevenue - $yesterdayRevenue) / $yesterdayRevenue * 100) : 0;
?>
<div class="card border-success h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('finance.revenue_today') ?></h6>
                <h3 class="mb-0"><?= money($todayRevenue) ?></h3>
                <?php if ($trend != 0): ?>
                <small class="<?= $trend > 0 ? 'text-success' : 'text-danger' ?>">
                    <i class="fas fa-arrow-<?= $trend > 0 ? 'up' : 'down' ?>"></i> <?= abs($trend) ?>%
                </small>
                <?php endif; ?>
            </div>
            <div class="align-self-center"><i class="fas fa-coins fa-2x text-success opacity-50"></i></div>
        </div>
    </div>
</div>
```

## File: modules\members\Controllers\DependentApiController.php

```php
<?php
class DependentApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $memberId = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $rows = $db->fetchAll(
            "SELECT d.*, rt.name_en AS rel_type_en, rt.name_ar AS rel_type_ar
             FROM member_dependents d
             LEFT JOIN dependent_relationship_types rt ON d.relationship_type_id = rt.id
             WHERE d.primary_member_id = ? AND d.is_deleted = 0
             ORDER BY d.add_date", [$memberId]
        );

        $data = [];
        foreach ($rows as $row) {
            $age = $row['date_of_birth'] ? (int) ((time() - strtotime($row['date_of_birth'])) / 31557600) : 'â€”';
            $actions = '<div class="btn-group btn-group-sm">';
            if (Gate::can('dependents.manage')) {
                $actions .= '<button class="btn btn-outline-warning btn-edit-dep" data-id="' . $row['id'] . '" title="Edit"><i class="fas fa-edit"></i></button>';
                $actions .= '<button class="btn btn-outline-danger btn-delete-dep" data-id="' . $row['id'] . '" title="Remove"><i class="fas fa-trash"></i></button>';
            }
            $actions .= '</div>';

            $data[] = [
                'id'           => $row['id'],
                'name'         => e($row['full_name_en']),
                'name_ar'      => e($row['full_name_ar'] ?? ''),
                'relationship' => e(current_lang() === 'ar' ? $row['rel_type_ar'] : $row['rel_type_en']),
                'gender'       => ucfirst($row['gender']),
                'dob'          => format_date($row['date_of_birth']),
                'age'          => $age,
                'national_id'  => e($row['national_id'] ?? 'â€”'),
                'status'       => status_badge($row['status']),
                'actions'      => $actions,
            ];
        }

        $this->json([
            'draw'            => (int) $request->get('draw', 1),
            'recordsTotal'    => count($data),
            'recordsFiltered' => count($data),
            'data'            => $data,
        ]);
    }

    public function get(Request $request): void
    {
        $depId = (int) $request->routeParam('depId');
        $db = Database::getInstance();
        $dep = $db->fetchOne("SELECT * FROM member_dependents WHERE id = ? AND is_deleted = 0", [$depId]);
        if (!$dep) {
            $this->json(['success' => false, 'message' => 'Dependent not found'], 404);
            return;
        }
        $this->json(['success' => true, 'data' => $dep]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $memberId = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $member = $db->fetchOne("SELECT id, family_id, membership_type_id FROM members WHERE id = ? AND is_deleted = 0", [$memberId]);
        if (!$member) {
            $this->json(['success' => false, 'message' => 'Member not found'], 404);
            return;
        }

        $memType = $db->fetchOne("SELECT max_dependents, is_family_membership FROM membership_types WHERE id = ?", [$member['membership_type_id']]);
        if (!$memType || !$memType['is_family_membership']) {
            $this->json(['success' => false, 'message' => 'This membership type does not allow dependents'], 422);
            return;
        }

        $currentCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_dependents WHERE primary_member_id = ? AND is_deleted = 0", [$memberId]);
        if ($memType['max_dependents'] !== null && $currentCount >= $memType['max_dependents']) {
            $this->json(['success' => false, 'message' => 'Maximum dependents limit reached (' . $memType['max_dependents'] . ')'], 422);
            return;
        }

        $validator = new Validator($request->all(), [
            'relationship_type_id' => 'required|integer|exists:dependent_relationship_types,id',
            'first_name_en'        => 'required|string|max:100',
            'last_name_en'         => 'required|string|max:100',
            'gender'               => 'required|in:male,female',
            'date_of_birth'        => 'nullable|date',
            'national_id'          => 'nullable|string|max:20',
            'phone'                => 'nullable|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();

        // Validate relationship type constraints
        $relType = $db->fetchOne("SELECT * FROM dependent_relationship_types WHERE id = ?", [$data['relationship_type_id']]);
        if ($relType) {
            if ($relType['gender_restriction'] !== 'both' && $data['gender'] !== $relType['gender_restriction']) {
                $this->json(['success' => false, 'errors' => ['gender' => ['Gender does not match relationship type requirement']]], 422);
                return;
            }
            if ($relType['max_count_per_member']) {
                $relCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_dependents WHERE primary_member_id = ? AND relationship_type_id = ? AND is_deleted = 0", [$memberId, $data['relationship_type_id']]);
                if ($relCount >= $relType['max_count_per_member']) {
                    $this->json(['success' => false, 'message' => 'Maximum count for this relationship type reached'], 422);
                    return;
                }
            }
        }

        $familyId = $member['family_id'];
        if (!$familyId) {
            $familyCode = 'FAM-' . str_pad($memberId, 6, '0', STR_PAD_LEFT);
            $familyId = $db->insert('member_families', [
                'family_code'       => $familyCode,
                'primary_member_id' => $memberId,
                'created_by'        => Auth::id(),
            ]);
            $db->update('members', ['family_id' => $familyId], 'id = ?', [$memberId]);
        }

        $fullNameEn = trim(($data['first_name_en'] ?? '') . ' ' . ($request->post('second_name_en', '')) . ' ' . ($request->post('third_name_en', '')) . ' ' . ($data['last_name_en'] ?? ''));
        $fullNameEn = preg_replace('/\s+/', ' ', $fullNameEn);

        $depId = $db->insert('member_dependents', [
            'family_id'            => $familyId,
            'primary_member_id'    => $memberId,
            'relationship_type_id' => $data['relationship_type_id'],
            'first_name_en'        => $data['first_name_en'],
            'second_name_en'       => $request->post('second_name_en'),
            'third_name_en'        => $request->post('third_name_en'),
            'last_name_en'         => $data['last_name_en'],
            'full_name_en'         => $fullNameEn,
            'first_name_ar'        => $request->post('first_name_ar'),
            'last_name_ar'         => $request->post('last_name_ar'),
            'national_id'          => $data['national_id'] ?? null,
            'date_of_birth'        => $data['date_of_birth'] ?? null,
            'gender'               => $data['gender'],
            'phone'                => $data['phone'] ?? null,
            'status'               => 'active',
            'add_date'             => date('Y-m-d'),
            'created_by'           => Auth::id(),
        ]);

        AuditLog::log('create', 'members', 'member_dependents', $depId, "Added dependent to member {$memberId}");

        $this->json(['success' => true, 'message' => 'Dependent added successfully', 'id' => $depId]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $depId = (int) $request->routeParam('depId');
        $db = Database::getInstance();

        $validator = new Validator($request->all(), [
            'relationship_type_id' => 'required|integer|exists:dependent_relationship_types,id',
            'first_name_en'        => 'required|string|max:100',
            'last_name_en'         => 'required|string|max:100',
            'gender'               => 'required|in:male,female',
            'date_of_birth'        => 'nullable|date',
            'national_id'          => 'nullable|string|max:20',
            'phone'                => 'nullable|string|max:20',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $fullNameEn = trim(($data['first_name_en'] ?? '') . ' ' . ($request->post('second_name_en', '')) . ' ' . ($request->post('third_name_en', '')) . ' ' . ($data['last_name_en'] ?? ''));
        $fullNameEn = preg_replace('/\s+/', ' ', $fullNameEn);

        $db->update('member_dependents', [
            'relationship_type_id' => $data['relationship_type_id'],
            'first_name_en'        => $data['first_name_en'],
            'second_name_en'       => $request->post('second_name_en'),
            'third_name_en'        => $request->post('third_name_en'),
            'last_name_en'         => $data['last_name_en'],
            'full_name_en'         => $fullNameEn,
            'first_name_ar'        => $request->post('first_name_ar'),
            'last_name_ar'         => $request->post('last_name_ar'),
            'national_id'          => $data['national_id'] ?? null,
            'date_of_birth'        => $data['date_of_birth'] ?? null,
            'gender'               => $data['gender'],
            'phone'                => $data['phone'] ?? null,
        ], 'id = ?', [$depId]);

        AuditLog::log('update', 'members', 'member_dependents', $depId, "Updated dependent");

        $this->json(['success' => true, 'message' => 'Dependent updated']);
    }

    public function delete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $depId = (int) $request->routeParam('depId');
        $db = Database::getInstance();

        $db->update('member_dependents', [
            'is_deleted'    => 1,
            'status'        => 'removed',
            'status_reason' => 'Removed by staff',
            'removal_date'  => date('Y-m-d'),
            'status_changed_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$depId]);

        AuditLog::log('delete', 'members', 'member_dependents', $depId, "Removed dependent");

        $this->json(['success' => true, 'message' => 'Dependent removed']);
    }
}
```

## File: modules\members\Controllers\EndorsementApiController.php

```php
<?php
class EndorsementApiController extends Controller
{
    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $memberId = (int) $request->routeParam('id');

        $validator = new Validator($request->all(), [
            'endorsement_type_id'       => 'required|integer|exists:endorsement_types,id',
            'endorsed_person_name'      => 'required|string|max:300',
            'endorsed_person_phone'     => 'nullable|string|max:20',
            'endorsed_person_national_id' => 'nullable|string|max:20',
            'endorsement_text'          => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $number = SequenceGenerator::next('endorsement_number');
        $db = Database::getInstance();

        $id = $db->insert('member_endorsements', [
            'endorsement_number'          => $number,
            'endorsement_type_id'         => $request->post('endorsement_type_id'),
            'endorser_member_id'          => $memberId,
            'endorsed_person_name'        => $request->post('endorsed_person_name'),
            'endorsed_person_phone'       => $request->post('endorsed_person_phone'),
            'endorsed_person_national_id' => $request->post('endorsed_person_national_id'),
            'endorsement_date'            => date('Y-m-d'),
            'endorsement_text'            => $request->post('endorsement_text'),
            'status'                      => 'active',
        ]);

        AuditLog::log('create', 'members', 'member_endorsements', $id, "Created endorsement {$number}");

        $this->json(['success' => true, 'message' => 'Endorsement created: ' . $number]);
    }
}
```

## File: modules\members\Controllers\MemberApiController.php

```php
<?php
class MemberApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = Database::getInstance();

        $draw    = (int) $request->get('draw', 1);
        $start   = (int) $request->get('start', 0);
        $length  = (int) $request->get('length', 25);
        $search  = $request->get('search')['value'] ?? '';
        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtolower($request->get('order')[0]['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';

        // Filters
        $filterStatus = $request->get('filter_status', '');
        $filterType   = $request->get('filter_type', '');
        $filterGender = $request->get('filter_gender', '');
        $filterFrom   = $request->get('filter_date_from', '');
        $filterTo     = $request->get('filter_date_to', '');

        $columns = ['m.id', 'm.membership_number', 'm.full_name_en', 'm.national_id', 'm.primary_phone', 'mt.name_en', 'm.status', 'm.join_date'];
        $orderColumn = $columns[$orderCol] ?? 'm.id';

        $baseWhere = "m.is_deleted = 0";
        $params = [];

        if ($filterStatus !== '') {
            $baseWhere .= " AND m.status = ?";
            $params[] = $filterStatus;
        }
        if ($filterType !== '') {
            $baseWhere .= " AND m.membership_type_id = ?";
            $params[] = (int) $filterType;
        }
        if ($filterGender !== '') {
            $baseWhere .= " AND m.gender = ?";
            $params[] = $filterGender;
        }
        if ($filterFrom !== '') {
            $baseWhere .= " AND m.join_date >= ?";
            $params[] = $filterFrom;
        }
        if ($filterTo !== '') {
            $baseWhere .= " AND m.join_date <= ?";
            $params[] = $filterTo;
        }

        $searchWhere = '';
        $searchParams = [];
        if ($search !== '') {
            $searchWhere = " AND (m.membership_number LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.national_id LIKE ? OR m.primary_phone LIKE ?)";
            $s = "%{$search}%";
            $searchParams = [$s, $s, $s, $s, $s];
        }

        $totalCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM members m WHERE {$baseWhere}", $params);

        $filteredCount = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM members m LEFT JOIN membership_types mt ON m.membership_type_id = mt.id WHERE {$baseWhere}{$searchWhere}",
            array_merge($params, $searchParams)
        );

        $rows = $db->fetchAll(
            "SELECT m.id, m.membership_number, m.full_name_en, m.full_name_ar, m.national_id, m.primary_phone,
                    m.status, m.join_date, m.photo_path, m.vip, m.gender,
                    mt.name_en AS type_name_en, mt.name_ar AS type_name_ar
             FROM members m
             LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
             WHERE {$baseWhere}{$searchWhere}
             ORDER BY {$orderColumn} {$orderDir}
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            array_merge($params, $searchParams)
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<a href="/members/' . $row['id'] . '" class="btn btn-outline-primary" title="View"><i class="fas fa-eye"></i></a>';
            if (Gate::can('members.edit')) {
                $actions .= '<a href="/members/' . $row['id'] . '/edit" class="btn btn-outline-warning" title="Edit"><i class="fas fa-edit"></i></a>';
            }
            if (Gate::can('members.delete')) {
                $actions .= '<button class="btn btn-outline-danger btn-delete-member" data-id="' . $row['id'] . '" title="Delete"><i class="fas fa-trash"></i></button>';
            }
            $actions .= '</div>';

            $photo = $row['photo_path']
                ? '<img src="/uploads/' . e($row['photo_path']) . '" class="table-avatar" alt="">'
                : '<span class="table-avatar d-inline-flex align-items-center justify-content-center bg-secondary text-white rounded-circle" style="width:36px;height:36px;font-size:14px"><i class="fas fa-user"></i></span>';

            $nameHtml = '<div>' . e($row['full_name_en']);
            if ($row['vip']) {
                $nameHtml .= ' <i class="fas fa-star text-warning" title="VIP"></i>';
            }
            $nameHtml .= '</div>';
            if ($row['full_name_ar']) {
                $nameHtml .= '<small class="text-muted">' . e($row['full_name_ar']) . '</small>';
            }

            $data[] = [
                'id'                => $row['id'],
                'photo'             => $photo,
                'membership_number' => '<a href="/members/' . $row['id'] . '">' . e($row['membership_number']) . '</a>',
                'name'              => $nameHtml,
                'national_id'       => e($row['national_id'] ?? 'â€”'),
                'phone'             => e($row['primary_phone'] ?? 'â€”'),
                'type'              => e(current_lang() === 'ar' ? $row['type_name_ar'] : $row['type_name_en']),
                'status'            => status_badge($row['status']),
                'join_date'         => format_date($row['join_date']),
                'actions'           => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalCount,
            'recordsFiltered' => $filteredCount,
            'data'            => $data,
        ]);
    }

    public function search(Request $request): void
    {
        $q    = $request->get('q', '');
        $page = (int) $request->get('page', 1);
        $limit = 20;
        $offset = ($page - 1) * $limit;

        if (mb_strlen($q) < 1) {
            $this->json(['results' => [], 'pagination' => ['more' => false]]);
            return;
        }

        $db = Database::getInstance();
        $s = "%{$q}%";
        $rows = $db->fetchAll(
            "SELECT m.id, m.membership_number, m.full_name_en, m.full_name_ar, m.national_id, m.primary_phone, m.status, m.photo_path
             FROM members m
             WHERE m.is_deleted = 0 AND m.status IN ('active','frozen','suspended','pending_approval')
               AND (m.membership_number LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.national_id LIKE ? OR m.primary_phone LIKE ?)
             ORDER BY m.full_name_en
             LIMIT " . ($limit + 1) . " OFFSET " . (int)$offset,
            [$s, $s, $s, $s, $s]
        );

        $more = count($rows) > $limit;
        if ($more) array_pop($rows);

        $results = [];
        foreach ($rows as $row) {
            $results[] = [
                'id'                => $row['id'],
                'text'              => $row['membership_number'] . ' â€” ' . $row['full_name_en'],
                'membership_number' => $row['membership_number'],
                'name_en'           => $row['full_name_en'],
                'name_ar'           => $row['full_name_ar'],
                'national_id'       => $row['national_id'],
                'phone'             => $row['primary_phone'],
                'status'            => $row['status'],
            ];
        }

        $this->json(['results' => $results, 'pagination' => ['more' => $more]]);
    }

    public function stats(Request $request): void
    {
        $db = Database::getInstance();
        $active    = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0");
        $frozen    = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'frozen' AND is_deleted = 0");
        $suspended = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'suspended' AND is_deleted = 0");
        $newMonth  = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0 AND MONTH(activation_date) = MONTH(CURDATE()) AND YEAR(activation_date) = YEAR(CURDATE())");

        $this->json([
            'success'   => true,
            'active'    => $active,
            'frozen'    => $frozen,
            'suspended' => $suspended,
            'new_month' => $newMonth,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'membership_type_id' => 'required|integer|exists:membership_types,id',
            'title'              => 'nullable|in:mr,mrs,ms,dr,eng,prof,sheikh,other',
            'first_name_en'      => 'required|string|max:100',
            'second_name_en'     => 'nullable|string|max:100',
            'third_name_en'      => 'nullable|string|max:100',
            'last_name_en'       => 'required|string|max:100',
            'first_name_ar'      => 'nullable|string|max:100',
            'second_name_ar'     => 'nullable|string|max:100',
            'third_name_ar'      => 'nullable|string|max:100',
            'last_name_ar'       => 'nullable|string|max:100',
            'national_id'        => 'nullable|string|max:20',
            'passport_number'    => 'nullable|string|max:30',
            'nationality_id'     => 'nullable|integer',
            'date_of_birth'      => 'nullable|date',
            'gender'             => 'required|in:male,female',
            'marital_status'     => 'nullable|in:single,married,divorced,widowed',
            'primary_phone'      => 'nullable|string|max:20',
            'secondary_phone'    => 'nullable|string|max:20',
            'primary_email'      => 'nullable|email|max:191',
            'address_line1'      => 'nullable|string|max:500',
            'city_id'            => 'nullable|integer',
            'country_id'         => 'nullable|integer',
            'occupation'         => 'nullable|string|max:200',
            'employer'           => 'nullable|string|max:300',
            'job_title'          => 'nullable|string|max:200',
            'emergency_contact_name'  => 'nullable|string|max:200',
            'emergency_contact_phone' => 'nullable|string|max:20',
            'emergency_contact_relation' => 'nullable|string|max:50',
            'vip'                => 'nullable|boolean',
            'internal_notes'     => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $db = Database::getInstance();

        // Check duplicate national ID
        if (!empty($data['national_id'])) {
            $existing = $db->fetchOne("SELECT id, membership_number, status FROM members WHERE national_id = ? AND is_deleted = 0", [$data['national_id']]);
            if ($existing) {
                $this->json(['success' => false, 'errors' => ['national_id' => ['National ID already exists for member ' . $existing['membership_number']]]], 422);
                return;
            }
        }

        // Generate membership number
        $membershipNumber = SequenceGenerator::next('member_number');
        if (!$membershipNumber) {
            $this->json(['success' => false, 'message' => 'Failed to generate membership number'], 500);
            return;
        }

        // Handle photo upload
        $photoPath = null;
        if ($request->file('photo') && $request->file('photo')['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($request->file('photo'));
            $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'webp'])
                   ->setMaxSize(5 * 1024 * 1024)
                   ->setDestination('uploads/members/photos');
            if ($upload->validate()) {
                $photoPath = $upload->save();
            }
        }

        $db->beginTransaction();
        try {
            $fullNameEn = trim(implode(' ', array_filter([
                $data['first_name_en'] ?? '', $data['second_name_en'] ?? '',
                $data['third_name_en'] ?? '', $data['last_name_en'] ?? ''
            ])));
            $fullNameAr = trim(implode(' ', array_filter([
                $data['first_name_ar'] ?? '', $data['second_name_ar'] ?? '',
                $data['third_name_ar'] ?? '', $data['last_name_ar'] ?? ''
            ])));

            $insertData = [
                'membership_number'   => $membershipNumber,
                'membership_type_id'  => $data['membership_type_id'],
                'title'               => $data['title'] ?? null,
                'first_name_en'       => $data['first_name_en'],
                'second_name_en'      => $data['second_name_en'] ?? null,
                'third_name_en'       => $data['third_name_en'] ?? null,
                'last_name_en'        => $data['last_name_en'],
                'full_name_en'        => $fullNameEn,
                'first_name_ar'       => $data['first_name_ar'] ?? null,
                'second_name_ar'      => $data['second_name_ar'] ?? null,
                'third_name_ar'       => $data['third_name_ar'] ?? null,
                'last_name_ar'        => $data['last_name_ar'] ?? null,
                'full_name_ar'        => $fullNameAr ?: null,
                'national_id'         => $data['national_id'] ?? null,
                'passport_number'     => $data['passport_number'] ?? null,
                'nationality_id'      => $data['nationality_id'] ?? null,
                'date_of_birth'       => $data['date_of_birth'] ?? null,
                'gender'              => $data['gender'],
                'marital_status'      => $data['marital_status'] ?? null,
                'primary_phone'       => $data['primary_phone'] ?? null,
                'secondary_phone'     => $data['secondary_phone'] ?? null,
                'primary_email'       => $data['primary_email'] ?? null,
                'address_line1'       => $data['address_line1'] ?? null,
                'city_id'             => $data['city_id'] ?? null,
                'country_id'          => $data['country_id'] ?? null,
                'occupation'          => $data['occupation'] ?? null,
                'employer'            => $data['employer'] ?? null,
                'job_title'           => $data['job_title'] ?? null,
                'emergency_contact_name'     => $data['emergency_contact_name'] ?? null,
                'emergency_contact_phone'    => $data['emergency_contact_phone'] ?? null,
                'emergency_contact_relation' => $data['emergency_contact_relation'] ?? null,
                'photo_path'          => $photoPath,
                'vip'                 => !empty($data['vip']) ? 1 : 0,
                'internal_notes'      => $data['internal_notes'] ?? null,
                'status'              => 'active',
                'join_date'           => date('Y-m-d'),
                'activation_date'     => date('Y-m-d'),
                'created_by'          => Auth::id(),
                'communication_preference' => 'all',
                'language_preference' => 'ar',
            ];

            $memberId = $db->insert('members', $insertData);

            // Create member_balances record
            $db->insert('member_balances', ['member_id' => $memberId]);

            // Create family record if family membership
            $memType = $db->fetchOne("SELECT is_family_membership FROM membership_types WHERE id = ?", [$data['membership_type_id']]);
            if ($memType && $memType['is_family_membership']) {
                $familyCode = 'FAM-' . str_pad($memberId, 6, '0', STR_PAD_LEFT);
                $familyId = $db->insert('member_families', [
                    'family_code'       => $familyCode,
                    'primary_member_id' => $memberId,
                    'family_name_en'    => $fullNameEn,
                    'family_name_ar'    => $fullNameAr ?: null,
                    'created_by'        => Auth::id(),
                ]);
                $db->update('members', ['family_id' => $familyId, 'is_primary_member' => 1], 'id = ?', [$memberId]);
            }

            AuditLog::log('create', 'members', 'members', $memberId, "Created member {$membershipNumber}", null, $insertData);

            $db->commit();

            $this->json([
                'success'  => true,
                'message'  => 'Member created successfully: ' . $membershipNumber,
                'redirect' => '/members/' . $memberId,
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Member creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create member: ' . $e->getMessage()], 500);
        }
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $old = $db->fetchOne("SELECT * FROM members WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Member not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'membership_type_id' => 'required|integer|exists:membership_types,id',
            'title'              => 'nullable|in:mr,mrs,ms,dr,eng,prof,sheikh,other',
            'first_name_en'      => 'required|string|max:100',
            'second_name_en'     => 'nullable|string|max:100',
            'third_name_en'      => 'nullable|string|max:100',
            'last_name_en'       => 'required|string|max:100',
            'first_name_ar'      => 'nullable|string|max:100',
            'second_name_ar'     => 'nullable|string|max:100',
            'third_name_ar'      => 'nullable|string|max:100',
            'last_name_ar'       => 'nullable|string|max:100',
            'national_id'        => 'nullable|string|max:20',
            'passport_number'    => 'nullable|string|max:30',
            'nationality_id'     => 'nullable|integer',
            'date_of_birth'      => 'nullable|date',
            'gender'             => 'required|in:male,female',
            'marital_status'     => 'nullable|in:single,married,divorced,widowed',
            'primary_phone'      => 'nullable|string|max:20',
            'secondary_phone'    => 'nullable|string|max:20',
            'primary_email'      => 'nullable|email|max:191',
            'address_line1'      => 'nullable|string|max:500',
            'city_id'            => 'nullable|integer',
            'country_id'         => 'nullable|integer',
            'occupation'         => 'nullable|string|max:200',
            'employer'           => 'nullable|string|max:300',
            'job_title'          => 'nullable|string|max:200',
            'emergency_contact_name'  => 'nullable|string|max:200',
            'emergency_contact_phone' => 'nullable|string|max:20',
            'emergency_contact_relation' => 'nullable|string|max:50',
            'vip'                => 'nullable|boolean',
            'internal_notes'     => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();

        // Duplicate national ID check (exclude self)
        if (!empty($data['national_id'])) {
            $dup = $db->fetchOne("SELECT id FROM members WHERE national_id = ? AND id != ? AND is_deleted = 0", [$data['national_id'], $id]);
            if ($dup) {
                $this->json(['success' => false, 'errors' => ['national_id' => ['National ID already used by another member']]], 422);
                return;
            }
        }

        // Handle photo
        if ($request->file('photo') && $request->file('photo')['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($request->file('photo'));
            $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'webp'])
                   ->setMaxSize(5 * 1024 * 1024)
                   ->setDestination('uploads/members/photos');
            if ($upload->validate()) {
                $data['photo_path'] = $upload->save();
            }
        }

        $data['vip'] = !empty($data['vip']) ? 1 : 0;
        $data['updated_by'] = Auth::id();

        $db->update('members', $data, 'id = ?', [$id]);

        AuditLog::log('update', 'members', 'members', $id, "Updated member {$old['membership_number']}", $old, $data);

        $this->json([
            'success'  => true,
            'message'  => 'Member updated successfully',
            'redirect' => '/members/' . $id,
        ]);
    }

    public function delete(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $member = $db->fetchOne("SELECT * FROM members WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$member) {
            $this->json(['success' => false, 'message' => 'Member not found'], 404);
            return;
        }

        $db->update('members', [
            'is_deleted' => 1,
            'deleted_at' => date('Y-m-d H:i:s'),
            'deleted_by' => Auth::id(),
        ], 'id = ?', [$id]);

        AuditLog::log('delete', 'members', 'members', $id, "Soft deleted member {$member['membership_number']}");

        $this->json(['success' => true, 'message' => 'Member deleted successfully']);
    }

    public function changeStatus(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id     = (int) $request->routeParam('id');
        $status = $request->post('status', '');
        $reason = $request->post('reason', '');

        $db = Database::getInstance();

        $validStatuses = ['active', 'suspended', 'frozen', 'terminated', 'deceased'];
        if (!in_array($status, $validStatuses)) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 422);
            return;
        }

        try {
            $result = '';
            if ($status === 'active') {
                $db->execute("CALL sp_activate_member(?, ?, ?, @result)", [$id, $reason, Auth::id()]);
                $r = $db->fetchOne("SELECT @result AS res");
                $result = $r['res'] ?? '';
            } elseif ($status === 'suspended') {
                $db->execute("CALL sp_suspend_member(?, ?, ?, @result)", [$id, $reason, Auth::id()]);
                $r = $db->fetchOne("SELECT @result AS res");
                $result = $r['res'] ?? '';
            } elseif ($status === 'terminated') {
                $db->execute("CALL sp_terminate_member(?, ?, ?, @result)", [$id, $reason, Auth::id()]);
                $r = $db->fetchOne("SELECT @result AS res");
                $result = $r['res'] ?? '';
            } else {
                $db->update('members', [
                    'status'            => $status,
                    'status_reason'     => $reason,
                    'status_changed_at' => date('Y-m-d H:i:s'),
                    'status_changed_by' => Auth::id(),
                    'updated_by'        => Auth::id(),
                ], 'id = ?', [$id]);
                $result = 'Status changed to ' . $status;
            }

            AuditLog::log('update', 'members', 'members', $id, "Status changed to {$status}: {$reason}");

            $this->json(['success' => true, 'message' => $result]);
        } catch (\Exception $e) {
            Logger::error('Status change failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed: ' . $e->getMessage()], 500);
        }
    }

    public function financial(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $balances = $db->fetchOne("SELECT * FROM member_balances WHERE member_id = ?", [$id]);
        $ledger   = $db->fetchAll(
            "SELECT ml.*, c.currency_code FROM member_ledger ml
             LEFT JOIN sys_currencies c ON ml.currency_id = c.id
             WHERE ml.member_id = ? ORDER BY ml.transaction_date DESC, ml.id DESC LIMIT 50", [$id]
        );

        $this->json(['success' => true, 'balances' => $balances, 'ledger' => $ledger]);
    }

    public function export(Request $request): void
    {
        $db = Database::getInstance();
        $filterStatus = $request->get('status', '');
        $filterType   = $request->get('type', '');

        $where = "m.is_deleted = 0";
        $params = [];
        if ($filterStatus) { $where .= " AND m.status = ?"; $params[] = $filterStatus; }
        if ($filterType)   { $where .= " AND m.membership_type_id = ?"; $params[] = (int)$filterType; }

        $rows = $db->fetchAll(
            "SELECT m.membership_number, m.full_name_en, m.full_name_ar, m.national_id,
                    m.primary_phone, m.primary_email, m.gender, m.date_of_birth, m.status,
                    m.join_date, mt.name_en AS type_name, m.outstanding_balance
             FROM members m LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
             WHERE {$where} ORDER BY m.membership_number",
            $params
        );

        $format = $request->get('format', 'csv');
        $headers = ['Membership #', 'Name (EN)', 'Name (AR)', 'National ID', 'Phone', 'Email', 'Gender', 'DOB', 'Status', 'Join Date', 'Type', 'Balance'];

        AuditLog::log('export', 'members', 'members', null, "Exported " . count($rows) . " members as {$format}");

        if ($format === 'excel') {
            Export::excel('members_' . date('Ymd') . '.xls', $headers, $rows);
        } else {
            Export::csv('members_' . date('Ymd') . '.csv', $headers, $rows);
        }
    }

    public function addNote(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int) $request->routeParam('id');
        $validator = new Validator($request->all(), [
            'note_type' => 'required|in:general,financial,disciplinary,medical,vip,internal',
            'subject'   => 'nullable|string|max:300',
            'note_text' => 'required|string|min:1',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = Database::getInstance();
        $noteId = $db->insert('member_notes', [
            'member_id'  => $id,
            'note_type'  => $request->post('note_type'),
            'subject'    => $request->post('subject'),
            'note_text'  => $request->post('note_text'),
            'is_pinned'  => $request->post('is_pinned') ? 1 : 0,
            'is_confidential' => $request->post('is_confidential') ? 1 : 0,
            'created_by' => Auth::id(),
        ]);

        $this->json(['success' => true, 'message' => 'Note added', 'id' => $noteId]);
    }

    public function deleteNote(Request $request): void
    {
        $noteId = (int) $request->routeParam('noteId');
        $db = Database::getInstance();
        $db->update('member_notes', ['is_deleted' => 1], 'id = ?', [$noteId]);
        $this->json(['success' => true, 'message' => 'Note deleted']);
    }

    public function addTag(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id      = (int) $request->routeParam('id');
        $tagName = trim($request->post('tag_name', ''));
        if (!$tagName) {
            $this->json(['success' => false, 'message' => 'Tag name is required'], 422);
            return;
        }

        $db = Database::getInstance();
        $exists = $db->fetchOne("SELECT id FROM member_tags WHERE member_id = ? AND tag_name = ?", [$id, $tagName]);
        if ($exists) {
            $this->json(['success' => false, 'message' => 'Tag already exists'], 422);
            return;
        }

        $tagId = $db->insert('member_tags', [
            'member_id'  => $id,
            'tag_name'   => $tagName,
            'tag_color'  => $request->post('tag_color', '#6c757d'),
            'created_by' => Auth::id(),
        ]);

        $this->json(['success' => true, 'message' => 'Tag added', 'id' => $tagId]);
    }

    public function deleteTag(Request $request): void
    {
        $tagId = (int) $request->routeParam('tagId');
        Database::getInstance()->delete('member_tags', 'id = ?', [$tagId]);
        $this->json(['success' => true, 'message' => 'Tag removed']);
    }

    public function uploadDocument(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int) $request->routeParam('id');
        $file = $request->file('document');
        if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
            $this->json(['success' => false, 'message' => 'No file uploaded'], 422);
            return;
        }

        $upload = new FileUpload($file);
        $upload->setAllowedTypes(['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'])
               ->setMaxSize(10 * 1024 * 1024)
               ->setDestination('uploads/members/documents');

        if (!$upload->validate()) {
            $this->json(['success' => false, 'message' => implode(', ', $upload->errors())], 422);
            return;
        }

        $path = $upload->save();
        if (!$path) {
            $this->json(['success' => false, 'message' => 'Upload failed'], 500);
            return;
        }

        $db = Database::getInstance();
        $docId = $db->insert('member_documents', [
            'member_id'          => $id,
            'document_type'      => $request->post('document_type', 'other'),
            'document_name'      => $request->post('document_name', $upload->originalName()),
            'file_path'          => $path,
            'file_original_name' => $upload->originalName(),
            'file_size_bytes'    => $upload->size(),
            'mime_type'          => $upload->mimeType(),
            'expiry_date'        => $request->post('expiry_date') ?: null,
            'uploaded_by'        => Auth::id(),
        ]);

        $this->json(['success' => true, 'message' => 'Document uploaded', 'id' => $docId]);
    }

    public function verifyDocument(Request $request): void
    {
        $docId = (int) $request->routeParam('docId');
        $db = Database::getInstance();
        $db->update('member_documents', [
            'is_verified'  => 1,
            'verified_by'  => Auth::id(),
            'verified_at'  => date('Y-m-d H:i:s'),
        ], 'id = ?', [$docId]);

        $this->json(['success' => true, 'message' => 'Document verified']);
    }

    public function deleteDocument(Request $request): void
    {
        $docId = (int) $request->routeParam('docId');
        $db = Database::getInstance();
        $db->update('member_documents', [
            'is_deleted' => 1,
            'deleted_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$docId]);

        $this->json(['success' => true, 'message' => 'Document deleted']);
    }
}
```

## File: modules\members\Controllers\MemberController.php

```php
<?php
class MemberController extends Controller
{
    public function index(Request $request): void
    {
        $db = Database::getInstance();
        $types = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('index', [
            'title'       => lang('members.title'),
            'breadcrumbs' => [['label' => lang('members.title')]],
            'types'       => $types,
        ]);
    }

    public function create(Request $request): void
    {
        $db = Database::getInstance();
        $types         = $db->fetchAll("SELECT id, name_en, name_ar, is_family_membership, max_dependents, requires_sponsor, sponsor_count FROM membership_types WHERE is_active = 1 ORDER BY sort_order");
        $nationalities = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_nationalities WHERE is_active = 1 ORDER BY name_en");
        $countries     = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_countries WHERE is_active = 1 ORDER BY sort_order, name_en");
        $relTypes      = $db->fetchAll("SELECT id, relationship_code, name_en, name_ar FROM dependent_relationship_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('create', [
            'title'         => lang('members.create'),
            'breadcrumbs'   => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => lang('members.create')],
            ],
            'types'         => $types,
            'nationalities' => $nationalities,
            'countries'     => $countries,
            'relTypes'      => $relTypes,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $member = $db->fetchOne(
            "SELECT m.*, mt.name_en AS type_name_en, mt.name_ar AS type_name_ar, mt.type_code,
                    mt.is_family_membership, mt.max_dependents,
                    n.name_en AS nationality_en, n.name_ar AS nationality_ar,
                    c.name_en AS city_en, c.name_ar AS city_ar,
                    co.name_en AS country_en, co.name_ar AS country_ar
             FROM members m
             LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
             LEFT JOIN sys_nationalities n ON m.nationality_id = n.id
             LEFT JOIN sys_cities c ON m.city_id = c.id
             LEFT JOIN sys_countries co ON m.country_id = co.id
             WHERE m.id = ? AND m.is_deleted = 0", [$id]
        );

        if (!$member) {
            $this->abort(404, 'Member not found');
            return;
        }

        $family = null;
        if ($member['family_id']) {
            $family = $db->fetchOne("SELECT * FROM member_families WHERE id = ?", [$member['family_id']]);
        }

        $dependents = $db->fetchAll(
            "SELECT d.*, rt.name_en AS rel_type_en, rt.name_ar AS rel_type_ar
             FROM member_dependents d
             LEFT JOIN dependent_relationship_types rt ON d.relationship_type_id = rt.id
             WHERE d.primary_member_id = ? AND d.is_deleted = 0
             ORDER BY d.add_date", [$id]
        );

        $balances = $db->fetchOne("SELECT * FROM member_balances WHERE member_id = ?", [$id]);

        $recentInvoices = $db->fetchAll(
            "SELECT id, invoice_number, invoice_type, invoice_date, total_amount, paid_amount, balance_due, status
             FROM invoices WHERE member_id = ? AND is_deleted = 0 ORDER BY invoice_date DESC LIMIT 10", [$id]
        );

        $recentPayments = $db->fetchAll(
            "SELECT p.id, p.payment_number, p.amount, p.payment_date, p.status, pm.name_en AS method_en
             FROM payments p LEFT JOIN payment_methods pm ON p.payment_method_id = pm.id
             WHERE p.member_id = ? AND p.is_deleted = 0 ORDER BY p.payment_date DESC LIMIT 10", [$id]
        );

        $documents = $db->fetchAll(
            "SELECT * FROM member_documents WHERE member_id = ? AND is_deleted = 0 ORDER BY uploaded_at DESC", [$id]
        );

        $subscriptions = $db->fetchAll(
            "SELECT ms.*, sp.name_en AS period_name_en, mt2.name_en AS sub_type_en
             FROM member_subscriptions ms
             LEFT JOIN subscription_periods sp ON ms.subscription_period_id = sp.id
             LEFT JOIN membership_types mt2 ON ms.membership_type_id = mt2.id
             WHERE ms.member_id = ? ORDER BY ms.start_date DESC LIMIT 20", [$id]
        );

        $cards = $db->fetchAll(
            "SELECT mc.*, ct.name_en AS card_type_en
             FROM member_cards mc LEFT JOIN card_types ct ON mc.card_type_id = ct.id
             WHERE mc.member_id = ? ORDER BY mc.issue_date DESC", [$id]
        );

        $freezes = $db->fetchAll(
            "SELECT * FROM member_freezes WHERE member_id = ? ORDER BY start_date DESC", [$id]
        );

        $violations = $db->fetchAll(
            "SELECT v.*, vt.name_en AS type_en, vt.severity
             FROM violations v LEFT JOIN violation_types vt ON v.violation_type_id = vt.id
             WHERE v.member_id = ? ORDER BY v.incident_date DESC LIMIT 20", [$id]
        );

        $notes = $db->fetchAll(
            "SELECT mn.*, u.full_name_en AS author_name
             FROM member_notes mn LEFT JOIN auth_users u ON mn.created_by = u.id
             WHERE mn.member_id = ? AND mn.is_deleted = 0 ORDER BY mn.is_pinned DESC, mn.created_at DESC", [$id]
        );

        $tags = $db->fetchAll("SELECT * FROM member_tags WHERE member_id = ?", [$id]);

        $statusHistory = $db->fetchAll(
            "SELECT msh.*, u.full_name_en AS changed_by_name
             FROM member_status_history msh LEFT JOIN auth_users u ON msh.changed_by = u.id
             WHERE msh.member_id = ? ORDER BY msh.changed_at DESC LIMIT 20", [$id]
        );

        $endorsements = $db->fetchAll(
            "SELECT me.*, et.name_en AS type_en, m2.full_name_en AS endorser_name, m2.membership_number AS endorser_number
             FROM member_endorsements me
             LEFT JOIN endorsement_types et ON me.endorsement_type_id = et.id
             LEFT JOIN members m2 ON me.endorser_member_id = m2.id
             WHERE me.endorser_member_id = ? OR me.endorsed_person_national_id = ?
             ORDER BY me.endorsement_date DESC", [$id, $member['national_id'] ?? '']
        );

        $accessLog = $db->fetchAll(
            "SELECT mal.*, ap.name_en AS point_name
             FROM member_access_log mal LEFT JOIN access_points ap ON mal.access_point_id = ap.id
             WHERE mal.member_id = ? ORDER BY mal.access_time DESC LIMIT 20", [$id]
        );

        $relTypes = $db->fetchAll("SELECT id, name_en, name_ar, relationship_code, gender_restriction, max_count_per_member FROM dependent_relationship_types WHERE is_active = 1 ORDER BY sort_order");
        $nationalities = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_nationalities WHERE is_active = 1 ORDER BY name_en");
        $endorsementTypes = $db->fetchAll("SELECT id, name_en, name_ar, type_code FROM endorsement_types WHERE is_active = 1 ORDER BY id");

        $this->view('show', [
            'title'            => $member['full_name_en'] . ' (' . $member['membership_number'] . ')',
            'breadcrumbs'      => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => $member['membership_number']],
            ],
            'member'           => $member,
            'family'           => $family,
            'dependents'       => $dependents,
            'balances'         => $balances,
            'recentInvoices'   => $recentInvoices,
            'recentPayments'   => $recentPayments,
            'documents'        => $documents,
            'subscriptions'    => $subscriptions,
            'cards'            => $cards,
            'freezes'          => $freezes,
            'violations'       => $violations,
            'notes'            => $notes,
            'tags'             => $tags,
            'statusHistory'    => $statusHistory,
            'endorsements'     => $endorsements,
            'accessLog'        => $accessLog,
            'relTypes'         => $relTypes,
            'nationalities'    => $nationalities,
            'endorsementTypes' => $endorsementTypes,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $member = $db->fetchOne("SELECT * FROM members WHERE id = ? AND is_deleted = 0", [$id]);
        if (!$member) {
            $this->abort(404, 'Member not found');
            return;
        }

        $types         = $db->fetchAll("SELECT id, name_en, name_ar, is_family_membership FROM membership_types WHERE is_active = 1 ORDER BY sort_order");
        $nationalities = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_nationalities WHERE is_active = 1 ORDER BY name_en");
        $countries     = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_countries WHERE is_active = 1 ORDER BY sort_order, name_en");
        $cities        = [];
        if ($member['country_id']) {
            $cities = $db->fetchAll("SELECT id, name_en, name_ar FROM sys_cities WHERE country_id = ? AND is_active = 1 ORDER BY name_en", [$member['country_id']]);
        }

        $this->view('edit', [
            'title'         => lang('members.edit') . ': ' . $member['membership_number'],
            'breadcrumbs'   => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => $member['membership_number'], 'url' => '/members/' . $id],
                ['label' => lang('common.edit')],
            ],
            'member'        => $member,
            'types'         => $types,
            'nationalities' => $nationalities,
            'countries'     => $countries,
            'cities'        => $cities,
        ]);
    }

    public function printView(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();

        $member = $db->fetchOne(
            "SELECT m.*, mt.name_en AS type_name_en, mt.name_ar AS type_name_ar,
                    n.name_en AS nationality_en, c.name_en AS city_en, co.name_en AS country_en
             FROM members m
             LEFT JOIN membership_types mt ON m.membership_type_id = mt.id
             LEFT JOIN sys_nationalities n ON m.nationality_id = n.id
             LEFT JOIN sys_cities c ON m.city_id = c.id
             LEFT JOIN sys_countries co ON m.country_id = co.id
             WHERE m.id = ? AND m.is_deleted = 0", [$id]
        );

        if (!$member) {
            $this->abort(404, 'Member not found');
            return;
        }

        $dependents = $db->fetchAll(
            "SELECT d.*, rt.name_en AS rel_type_en
             FROM member_dependents d LEFT JOIN dependent_relationship_types rt ON d.relationship_type_id = rt.id
             WHERE d.primary_member_id = ? AND d.is_deleted = 0", [$id]
        );

        $this->view('print', [
            'title'      => 'Print - ' . $member['membership_number'],
            'member'     => $member,
            'dependents' => $dependents,
        ], 'print');
    }
}
```

## File: modules\members\Controllers\MembershipCategoryApiController.php

```php
<?php
class MembershipCategoryApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = Database::getInstance();
        $rows = $db->fetchAll(
            "SELECT mc.*, (SELECT COUNT(*) FROM membership_types mt WHERE mt.category_id = mc.id) AS type_count
             FROM membership_categories mc ORDER BY mc.sort_order"
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<div class="btn-group btn-group-sm">';
            $actions .= '<button class="btn btn-outline-warning btn-edit-cat" data-id="' . $row['id'] . '" data-code="' . e($row['category_code']) . '" data-name-en="' . e($row['name_en']) . '" data-name-ar="' . e($row['name_ar']) . '" data-sort="' . $row['sort_order'] . '"><i class="fas fa-edit"></i></button>';
            if ($row['type_count'] == 0) {
                $actions .= '<button class="btn btn-outline-danger btn-delete-cat" data-id="' . $row['id'] . '"><i class="fas fa-trash"></i></button>';
            }
            $actions .= '</div>';

            $data[] = [
                'id'         => $row['id'],
                'code'       => e($row['category_code']),
                'name_en'    => e($row['name_en']),
                'name_ar'    => e($row['name_ar']),
                'type_count' => $row['type_count'],
                'is_active'  => $row['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'sort_order' => $row['sort_order'],
                'actions'    => $actions,
            ];
        }

        $this->json(['draw' => (int) $request->get('draw', 1), 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function list(Request $request): void
    {
        $db = Database::getInstance();
        $cats = $db->fetchAll("SELECT id, category_code, name_en, name_ar FROM membership_categories WHERE is_active = 1 ORDER BY sort_order");
        $this->json(['success' => true, 'data' => $cats]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'category_code' => 'required|string|max:20|unique:membership_categories,category_code',
            'name_en'       => 'required|string|max:200',
            'name_ar'       => 'required|string|max:200',
            'sort_order'    => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $db = Database::getInstance();
        $id = $db->insert('membership_categories', array_merge($validator->validated(), ['is_active' => 1]));

        $this->json(['success' => true, 'message' => 'Category created', 'id' => $id]);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int) $request->routeParam('id');
        $validator = new Validator($request->all(), [
            'name_en'    => 'required|string|max:200',
            'name_ar'    => 'required|string|max:200',
            'sort_order' => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        Database::getInstance()->update('membership_categories', $validator->validated(), 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Category updated']);
    }

    public function delete(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $count = (int) $db->fetchColumn("SELECT COUNT(*) FROM membership_types WHERE category_id = ?", [$id]);
        if ($count > 0) {
            $this->json(['success' => false, 'message' => 'Cannot delete: used by membership types'], 422);
            return;
        }
        $db->delete('membership_categories', 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Category deleted']);
    }
}
```

## File: modules\members\Controllers\MembershipCategoryController.php

```php
<?php
class MembershipCategoryController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('membership-categories/index', [
            'title'       => lang('members.categories'),
            'breadcrumbs' => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => lang('members.categories')],
            ],
        ]);
    }
}
```

## File: modules\members\Controllers\MembershipTypeApiController.php

```php
<?php
class MembershipTypeApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = Database::getInstance();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $where = "1=1";
        $params = [];
        if ($search) {
            $where .= " AND (mt.type_code LIKE ? OR mt.name_en LIKE ? OR mt.name_ar LIKE ?)";
            $s = "%{$search}%";
            $params = [$s, $s, $s];
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM membership_types mt");
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM membership_types mt WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT mt.*, mc.name_en AS cat_name_en,
                    (SELECT COUNT(*) FROM members m WHERE m.membership_type_id = mt.id AND m.is_deleted = 0 AND m.status = 'active') AS active_count
             FROM membership_types mt
             LEFT JOIN membership_categories mc ON mt.category_id = mc.id
             WHERE {$where}
             ORDER BY mt.sort_order
             LIMIT " . (int)$length . " OFFSET " . (int)$start,
            $params
        );

        $data = [];
        foreach ($rows as $row) {
            $actions = '<a href="/membership-types/' . $row['id'] . '/edit" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i></a>';
            $data[] = [
                'id'             => $row['id'],
                'type_code'      => e($row['type_code']),
                'name_en'        => e($row['name_en']),
                'name_ar'        => e($row['name_ar']),
                'category'       => e($row['cat_name_en'] ?? 'â€”'),
                'is_family'      => $row['is_family_membership'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-muted"></i>',
                'max_dependents' => $row['max_dependents'] ?? 'â€”',
                'active_count'   => number_format($row['active_count']),
                'is_active'      => $row['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions'        => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function list(Request $request): void
    {
        $db = Database::getInstance();
        $types = $db->fetchAll("SELECT id, type_code, name_en, name_ar, is_family_membership, max_dependents FROM membership_types WHERE is_active = 1 ORDER BY sort_order");
        $this->json(['success' => true, 'data' => $types]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $validator = new Validator($request->all(), [
            'type_code'              => 'required|string|max:30|unique:membership_types,type_code',
            'name_en'                => 'required|string|max:200',
            'name_ar'                => 'required|string|max:200',
            'category_id'            => 'nullable|integer',
            'is_family_membership'   => 'nullable|boolean',
            'max_dependents'         => 'nullable|integer|min:0',
            'requires_sponsor'       => 'nullable|boolean',
            'sponsor_count'          => 'nullable|integer|min:0',
            'requires_board_approval'=> 'nullable|boolean',
            'gender_restriction'     => 'nullable|in:male,female,both',
            'grace_period_days'      => 'nullable|integer|min:0',
            'sort_order'             => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_family_membership']    = !empty($data['is_family_membership']) ? 1 : 0;
        $data['requires_sponsor']        = !empty($data['requires_sponsor']) ? 1 : 0;
        $data['requires_board_approval'] = !empty($data['requires_board_approval']) ? 1 : 0;
        $data['is_active'] = 1;
        $data['created_by'] = Auth::id();

        $db = Database::getInstance();
        $id = $db->insert('membership_types', $data);

        AuditLog::log('create', 'members', 'membership_types', $id, "Created membership type {$data['type_code']}");

        $this->json(['success' => true, 'message' => 'Membership type created', 'redirect' => '/membership-types/' . $id . '/edit']);
    }

    public function update(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid CSRF token'], 403);
            return;
        }

        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $old = $db->fetchOne("SELECT * FROM membership_types WHERE id = ?", [$id]);
        if (!$old) {
            $this->json(['success' => false, 'message' => 'Not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'name_en'                => 'required|string|max:200',
            'name_ar'                => 'required|string|max:200',
            'category_id'            => 'nullable|integer',
            'is_family_membership'   => 'nullable|boolean',
            'max_dependents'         => 'nullable|integer|min:0',
            'requires_sponsor'       => 'nullable|boolean',
            'sponsor_count'          => 'nullable|integer|min:0',
            'requires_board_approval'=> 'nullable|boolean',
            'gender_restriction'     => 'nullable|in:male,female,both',
            'grace_period_days'      => 'nullable|integer|min:0',
            'sort_order'             => 'nullable|integer',
            'is_active'              => 'nullable|boolean',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['is_family_membership']    = !empty($data['is_family_membership']) ? 1 : 0;
        $data['requires_sponsor']        = !empty($data['requires_sponsor']) ? 1 : 0;
        $data['requires_board_approval'] = !empty($data['requires_board_approval']) ? 1 : 0;
        $data['is_active']               = !empty($data['is_active']) ? 1 : 0;

        $db->update('membership_types', $data, 'id = ?', [$id]);

        AuditLog::log('update', 'members', 'membership_types', $id, "Updated membership type", $old, $data);

        $this->json(['success' => true, 'message' => 'Membership type updated']);
    }

    public function delete(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $count = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE membership_type_id = ? AND is_deleted = 0", [$id]);
        if ($count > 0) {
            $this->json(['success' => false, 'message' => "Cannot delete: {$count} members are using this type"], 422);
            return;
        }
        $db->update('membership_types', ['is_active' => 0], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Membership type deactivated']);
    }
}
```

## File: modules\members\Controllers\MembershipTypeController.php

```php
<?php
class MembershipTypeController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('membership-types/index', [
            'title'       => lang('members.membership_types'),
            'breadcrumbs' => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => lang('members.membership_types')],
            ],
        ]);
    }

    public function create(Request $request): void
    {
        $db = Database::getInstance();
        $categories = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_categories WHERE is_active = 1 ORDER BY sort_order");
        $feeTypes   = $db->fetchAll("SELECT id, fee_code, name_en, name_ar FROM fee_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('membership-types/create', [
            'title'       => lang('members.create_type'),
            'breadcrumbs' => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => lang('members.membership_types'), 'url' => '/membership-types'],
                ['label' => lang('common.create')],
            ],
            'categories' => $categories,
            'feeTypes'   => $feeTypes,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = Database::getInstance();
        $type = $db->fetchOne("SELECT * FROM membership_types WHERE id = ?", [$id]);
        if (!$type) {
            $this->abort(404, 'Type not found');
            return;
        }

        $categories = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_categories WHERE is_active = 1 ORDER BY sort_order");
        $feeTypes   = $db->fetchAll("SELECT id, fee_code, name_en, name_ar FROM fee_types WHERE is_active = 1 ORDER BY sort_order");
        $typeFees   = $db->fetchAll("SELECT mtf.*, ft.name_en AS fee_name_en FROM membership_type_fees mtf LEFT JOIN fee_types ft ON mtf.fee_type_id = ft.id WHERE mtf.membership_type_id = ? AND mtf.is_active = 1", [$id]);
        $benefits   = $db->fetchAll("SELECT * FROM membership_type_benefits WHERE membership_type_id = ? AND is_active = 1 ORDER BY sort_order", [$id]);

        $this->view('membership-types/edit', [
            'title'       => lang('common.edit') . ': ' . $type['name_en'],
            'breadcrumbs' => [
                ['label' => lang('members.title'), 'url' => '/members'],
                ['label' => lang('members.membership_types'), 'url' => '/membership-types'],
                ['label' => $type['name_en']],
            ],
            'type'       => $type,
            'categories' => $categories,
            'feeTypes'   => $feeTypes,
            'typeFees'   => $typeFees,
            'benefits'   => $benefits,
        ]);
    }
}
```

## File: modules\members\css\members.css

```css
/* Members Module Styles */
.w-40 { width: 40%; }
.table-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; }
.member-photo-lg { width: 120px; height: 120px; object-fit: cover; }
.member-card-sidebar .card-body { padding: 1.25rem; }
.tab-content > .tab-pane { animation: fadeIn 0.2s ease-in; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.badge .btn-remove-tag { opacity: 0.7; transition: opacity 0.2s; }
.badge .btn-remove-tag:hover { opacity: 1; }
#stats-cards .card { transition: transform 0.2s; }
#stats-cards .card:hover { transform: translateY(-2px); }
```

## File: modules\members\js\members.js

```js
/**
 * Members Module JavaScript
 */
(function() {
    'use strict';

    // Initialize member search Select2 (used by other modules too)
    window.initMemberSearch = function(selector, options) {
        $(selector).select2(Object.assign({
            theme: 'bootstrap-5',
            width: '100%',
            ajax: {
                url: '/api/members/search',
                dataType: 'json',
                delay: 300,
                data: function(params) {
                    return { q: params.term, page: params.page || 1 };
                },
                processResults: function(data) {
                    return {
                        results: data.results || [],
                        pagination: data.pagination || { more: false }
                    };
                },
                cache: true
            },
            minimumInputLength: 2,
            placeholder: 'Search member...',
            templateResult: function(item) {
                if (item.loading) return item.text;
                return $('<div>').html(
                    '<strong>' + (item.membership_number || '') + '</strong> â€” ' +
                    (item.name_en || item.text) +
                    (item.phone ? '<br><small class="text-muted">' + item.phone + '</small>' : '')
                );
            },
            templateSelection: function(item) {
                return item.text || item.membership_number;
            }
        }, options || {}));
    };

    // Auto-init any member search fields on page
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelectorAll('.member-search').forEach(function(el) {
            initMemberSearch(el);
        });
    });
})();
```

## File: modules\members\lang\ar.php

```php
<?php
return [
    'members.title'           => 'Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡',
    'members.create'          => 'Ø¥Ø¶Ø§ÙØ© Ø¹Ø¶Ùˆ',
    'members.edit'            => 'ØªØ¹Ø¯ÙŠÙ„ Ø¹Ø¶Ùˆ',
    'members.add_member'      => 'Ø¥Ø¶Ø§ÙØ© Ø¹Ø¶Ùˆ',
    'members.membership_no'   => 'Ø±Ù‚Ù… Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'members.name'            => 'Ø§Ù„Ø§Ø³Ù…',
    'members.national_id'     => 'Ø§Ù„Ø±Ù‚Ù… Ø§Ù„Ù‚ÙˆÙ…ÙŠ',
    'members.phone'           => 'Ø§Ù„Ù‡Ø§ØªÙ',
    'members.type'            => 'Ø§Ù„Ù†ÙˆØ¹',
    'members.join_date'       => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ù†Ø¶Ù…Ø§Ù…',
    'members.gender'          => 'Ø§Ù„Ø¬Ù†Ø³',
    'members.male'            => 'Ø°ÙƒØ±',
    'members.female'          => 'Ø£Ù†Ø«Ù‰',
    'members.total_active'    => 'Ø¥Ø¬Ù…Ø§Ù„ÙŠ Ø§Ù„Ù†Ø´Ø·ÙŠÙ†',
    'members.new_this_month'  => 'Ø¬Ø¯Ø¯ Ù‡Ø°Ø§ Ø§Ù„Ø´Ù‡Ø±',
    'members.frozen'          => 'Ù…Ø¬Ù…Ø¯ÙŠÙ†',
    'members.suspended'       => 'Ù…ÙˆÙ‚ÙˆÙÙŠÙ†',
    'members.join_from'       => 'Ø§Ù†Ø¶Ù…Ø§Ù… Ù…Ù†',
    'members.join_to'         => 'Ø§Ù†Ø¶Ù…Ø§Ù… Ø¥Ù„Ù‰',
    'members.personal_info'   => 'Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø´Ø®ØµÙŠØ©',
    'members.contact'         => 'Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø§ØªØµØ§Ù„',
    'members.address'         => 'Ø§Ù„Ø¹Ù†ÙˆØ§Ù†',
    'members.employment'      => 'Ø§Ù„Ø¹Ù…Ù„',
    'members.additional'      => 'Ø¥Ø¶Ø§ÙÙŠ',
    'members.membership_type' => 'Ù†ÙˆØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'members.title_field'     => 'Ø§Ù„Ù„Ù‚Ø¨',
    'members.first_name_en'   => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„ (EN)',
    'members.second_name_en'  => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù†ÙŠ (EN)',
    'members.third_name_en'   => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù„Ø« (EN)',
    'members.last_name_en'    => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© (EN)',
    'members.first_name_ar'   => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„ (AR)',
    'members.second_name_ar'  => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù†ÙŠ (AR)',
    'members.third_name_ar'   => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø«Ø§Ù„Ø« (AR)',
    'members.last_name_ar'    => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© (AR)',
    'members.passport'        => 'Ø±Ù‚Ù… Ø¬ÙˆØ§Ø² Ø§Ù„Ø³ÙØ±',
    'members.nationality'     => 'Ø§Ù„Ø¬Ù†Ø³ÙŠØ©',
    'members.dob'             => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ù…ÙŠÙ„Ø§Ø¯',
    'members.marital_status'  => 'Ø§Ù„Ø­Ø§Ù„Ø© Ø§Ù„Ø§Ø¬ØªÙ…Ø§Ø¹ÙŠØ©',
    'members.single'          => 'Ø£Ø¹Ø²Ø¨',
    'members.married'         => 'Ù…ØªØ²ÙˆØ¬',
    'members.divorced'        => 'Ù…Ø·Ù„Ù‚',
    'members.widowed'         => 'Ø£Ø±Ù…Ù„',
    'members.photo'           => 'Ø§Ù„ØµÙˆØ±Ø©',
    'members.primary_phone'   => 'Ø§Ù„Ù‡Ø§ØªÙ Ø§Ù„Ø±Ø¦ÙŠØ³ÙŠ',
    'members.secondary_phone' => 'Ø§Ù„Ù‡Ø§ØªÙ Ø§Ù„Ø«Ø§Ù†ÙˆÙŠ',
    'members.email'           => 'Ø§Ù„Ø¨Ø±ÙŠØ¯ Ø§Ù„Ø¥Ù„ÙƒØªØ±ÙˆÙ†ÙŠ',
    'members.address_line1'   => 'Ø§Ù„Ø¹Ù†ÙˆØ§Ù†',
    'members.country'         => 'Ø§Ù„Ø¯ÙˆÙ„Ø©',
    'members.city'            => 'Ø§Ù„Ù…Ø¯ÙŠÙ†Ø©',
    'members.occupation'      => 'Ø§Ù„ÙˆØ¸ÙŠÙØ©',
    'members.employer'        => 'Ø¬Ù‡Ø© Ø§Ù„Ø¹Ù…Ù„',
    'members.job_title'       => 'Ø§Ù„Ù…Ø³Ù…Ù‰ Ø§Ù„ÙˆØ¸ÙŠÙÙŠ',
    'members.emergency_name'  => 'Ø§Ø³Ù… Ø§Ù„Ø·ÙˆØ§Ø±Ø¦',
    'members.emergency_phone' => 'Ù‡Ø§ØªÙ Ø§Ù„Ø·ÙˆØ§Ø±Ø¦',
    'members.emergency_relation' => 'ØµÙ„Ø© Ø§Ù„Ù‚Ø±Ø§Ø¨Ø©',
    'members.internal_notes'  => 'Ù…Ù„Ø§Ø­Ø¸Ø§Øª Ø¯Ø§Ø®Ù„ÙŠØ©',
    'members.membership_types'=> 'Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'members.create_type'     => 'Ø¥Ù†Ø´Ø§Ø¡ Ù†ÙˆØ¹ Ø¹Ø¶ÙˆÙŠØ©',
    'members.categories'      => 'ØªØµÙ†ÙŠÙØ§Øª Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'members.distribution'    => 'ØªÙˆØ²ÙŠØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ§Øª',
    'members.status_active'   => 'Ù†Ø´Ø·',
    'members.status_suspended'=> 'Ù…ÙˆÙ‚ÙˆÙ',
    'members.status_frozen'   => 'Ù…Ø¬Ù…Ø¯',
    'members.status_terminated'=> 'Ù…Ù†ØªÙ‡ÙŠ',
    'members.status_pending'  => 'ÙÙŠ Ø§Ù†ØªØ¸Ø§Ø± Ø§Ù„Ù…ÙˆØ§ÙÙ‚Ø©',
    'common.select'           => 'Ø§Ø®ØªØ±',
];
```

## File: modules\members\lang\en.php

```php
<?php
return [
    'members.title'           => 'Members',
    'members.create'          => 'Add Member',
    'members.edit'            => 'Edit Member',
    'members.add_member'      => 'Add Member',
    'members.membership_no'   => 'Membership #',
    'members.name'            => 'Name',
    'members.national_id'     => 'National ID',
    'members.phone'           => 'Phone',
    'members.type'            => 'Type',
    'members.join_date'       => 'Join Date',
    'members.gender'          => 'Gender',
    'members.male'            => 'Male',
    'members.female'          => 'Female',
    'members.total_active'    => 'Total Active',
    'members.new_this_month'  => 'New This Month',
    'members.frozen'          => 'Frozen',
    'members.suspended'       => 'Suspended',
    'members.join_from'       => 'Join From',
    'members.join_to'         => 'Join To',
    'members.personal_info'   => 'Personal Info',
    'members.contact'         => 'Contact',
    'members.address'         => 'Address',
    'members.employment'      => 'Employment',
    'members.additional'      => 'Additional',
    'members.membership_type' => 'Membership Type',
    'members.title_field'     => 'Title',
    'members.first_name_en'   => 'First Name (EN)',
    'members.second_name_en'  => 'Second Name (EN)',
    'members.third_name_en'   => 'Third Name (EN)',
    'members.last_name_en'    => 'Last Name (EN)',
    'members.first_name_ar'   => 'First Name (AR)',
    'members.second_name_ar'  => 'Second Name (AR)',
    'members.third_name_ar'   => 'Third Name (AR)',
    'members.last_name_ar'    => 'Last Name (AR)',
    'members.passport'        => 'Passport #',
    'members.nationality'     => 'Nationality',
    'members.dob'             => 'Date of Birth',
    'members.marital_status'  => 'Marital Status',
    'members.single'          => 'Single',
    'members.married'         => 'Married',
    'members.divorced'        => 'Divorced',
    'members.widowed'         => 'Widowed',
    'members.photo'           => 'Photo',
    'members.primary_phone'   => 'Primary Phone',
    'members.secondary_phone' => 'Secondary Phone',
    'members.email'           => 'Email',
    'members.address_line1'   => 'Address',
    'members.country'         => 'Country',
    'members.city'            => 'City',
    'members.occupation'      => 'Occupation',
    'members.employer'        => 'Employer',
    'members.job_title'       => 'Job Title',
    'members.emergency_name'  => 'Emergency Name',
    'members.emergency_phone' => 'Emergency Phone',
    'members.emergency_relation' => 'Emergency Relation',
    'members.internal_notes'  => 'Internal Notes',
    'members.membership_types'=> 'Membership Types',
    'members.create_type'     => 'Create Membership Type',
    'members.categories'      => 'Membership Categories',
    'members.distribution'    => 'Membership Distribution',
    'members.status_active'   => 'Active',
    'members.status_suspended'=> 'Suspended',
    'members.status_frozen'   => 'Frozen',
    'members.status_terminated'=> 'Terminated',
    'members.status_pending'  => 'Pending Approval',
    'common.select'           => 'Select',
];
```

## File: modules\members\module.json

```json
{
    "name": "members",
    "label_en": "Members & Dependents",
    "label_ar": "Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ ÙˆØ§Ù„ØªØ§Ø¨Ø¹ÙŠÙ†",
    "version": "1.0.0",
    "description": "Member registration, profiles, dependents, membership types management",
    "sort_order": 10,
    "icon": "fas fa-users",
    "routes_file": "routes.php",
    "css_files": ["css/members.css"],
    "js_files": ["js/members.js"],
    "menu": [
        {
            "label_en": "Members",
            "label_ar": "Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡",
            "icon": "fas fa-users",
            "permission": "members.view",
            "sort_order": 10,
            "children": [
                {
                    "label_en": "All Members",
                    "label_ar": "Ø¬Ù…ÙŠØ¹ Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡",
                    "route": "/members",
                    "permission": "members.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Add Member",
                    "label_ar": "Ø¥Ø¶Ø§ÙØ© Ø¹Ø¶Ùˆ",
                    "route": "/members/create",
                    "permission": "members.create",
                    "sort_order": 2
                },
                {
                    "label_en": "Membership Types",
                    "label_ar": "Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©",
                    "route": "/membership-types",
                    "permission": "members.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Categories",
                    "label_ar": "Ø§Ù„ØªØµÙ†ÙŠÙØ§Øª",
                    "route": "/membership-categories",
                    "permission": "members.view",
                    "sort_order": 4
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "members_active_count",
            "label_en": "Active Members",
            "label_ar": "Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ Ø§Ù„Ù†Ø´Ø·ÙŠÙ†",
            "permission": "members.view",
            "partial": "views/widgets/active_members.php",
            "size": "col-md-3",
            "sort_order": 1
        },
        {
            "widget_id": "members_distribution",
            "label_en": "Membership Distribution",
            "label_ar": "ØªÙˆØ²ÙŠØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ§Øª",
            "permission": "members.view",
            "partial": "views/widgets/distribution.php",
            "size": "col-md-6",
            "sort_order": 2
        },
        {
            "widget_id": "members_new_this_month",
            "label_en": "New Members This Month",
            "label_ar": "Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ Ø§Ù„Ø¬Ø¯Ø¯ Ù‡Ø°Ø§ Ø§Ù„Ø´Ù‡Ø±",
            "permission": "members.view",
            "partial": "views/widgets/new_members.php",
            "size": "col-md-3",
            "sort_order": 3
        }
    ]
}
```

## File: modules\members\routes.php

```php
<?php
return [
    // â”€â”€â”€ Page Routes â”€â”€â”€
    ['GET',  '/members',                                'MemberController@index',    'members.view'],
    ['GET',  '/members/create',                         'MemberController@create',   'members.create'],
    ['GET',  '/members/{id}',                           'MemberController@show',     'members.view'],
    ['GET',  '/members/{id}/edit',                      'MemberController@edit',     'members.edit'],
    ['GET',  '/members/{id}/print',                     'MemberController@printView','members.view'],
    ['GET',  '/membership-types',                       'MembershipTypeController@index',  'members.view'],
    ['GET',  '/membership-types/create',                'MembershipTypeController@create', 'members.create'],
    ['GET',  '/membership-types/{id}/edit',             'MembershipTypeController@edit',   'members.edit'],
    ['GET',  '/membership-categories',                  'MembershipCategoryController@index','members.view'],

    // â”€â”€â”€ Member API Routes â”€â”€â”€
    ['GET',  '/api/members/datatable',                  'MemberApiController@datatable',    'members.view'],
    ['GET',  '/api/members/search',                     'MemberApiController@search',       'members.view'],
    ['GET',  '/api/members/stats',                      'MemberApiController@stats',        'members.view'],
    ['POST', '/api/members/store',                      'MemberApiController@store',        'members.create'],
    ['POST', '/api/members/{id}/update',                'MemberApiController@update',       'members.edit'],
    ['POST', '/api/members/{id}/delete',                'MemberApiController@delete',       'members.delete'],
    ['POST', '/api/members/{id}/status',                'MemberApiController@changeStatus', 'members.status'],
    ['GET',  '/api/members/{id}/financial',             'MemberApiController@financial',    'members.financial'],
    ['GET',  '/api/members/export',                     'MemberApiController@export',       'members.view'],
    ['POST', '/api/members/{id}/notes',                 'MemberApiController@addNote',      'members.edit'],
    ['POST', '/api/members/{id}/notes/{noteId}/delete', 'MemberApiController@deleteNote',   'members.edit'],
    ['POST', '/api/members/{id}/tags',                  'MemberApiController@addTag',       'members.edit'],
    ['POST', '/api/members/{id}/tags/{tagId}/delete',   'MemberApiController@deleteTag',    'members.edit'],
    ['POST', '/api/members/{id}/documents/upload',      'MemberApiController@uploadDocument','members.edit'],
    ['POST', '/api/members/{id}/documents/{docId}/verify','MemberApiController@verifyDocument','members.edit'],
    ['POST', '/api/members/{id}/documents/{docId}/delete','MemberApiController@deleteDocument','members.edit'],

    // â”€â”€â”€ Dependent API Routes â”€â”€â”€
    ['GET',  '/api/members/{id}/dependents/datatable',           'DependentApiController@datatable', 'dependents.view'],
    ['POST', '/api/members/{id}/dependents/store',               'DependentApiController@store',     'dependents.manage'],
    ['GET',  '/api/members/{id}/dependents/{depId}',             'DependentApiController@get',       'dependents.view'],
    ['POST', '/api/members/{id}/dependents/{depId}/update',      'DependentApiController@update',    'dependents.manage'],
    ['POST', '/api/members/{id}/dependents/{depId}/delete',      'DependentApiController@delete',    'dependents.manage'],

    // â”€â”€â”€ Membership Type API Routes â”€â”€â”€
    ['GET',  '/api/membership-types/datatable',         'MembershipTypeApiController@datatable','members.view'],
    ['GET',  '/api/membership-types/list',              'MembershipTypeApiController@list',     'members.view'],
    ['POST', '/api/membership-types/store',             'MembershipTypeApiController@store',    'members.create'],
    ['POST', '/api/membership-types/{id}/update',       'MembershipTypeApiController@update',   'members.edit'],
    ['POST', '/api/membership-types/{id}/delete',       'MembershipTypeApiController@delete',   'members.delete'],

    // â”€â”€â”€ Membership Category API Routes â”€â”€â”€
    ['GET',  '/api/membership-categories/datatable',    'MembershipCategoryApiController@datatable','members.view'],
    ['GET',  '/api/membership-categories/list',         'MembershipCategoryApiController@list',     'members.view'],
    ['POST', '/api/membership-categories/store',        'MembershipCategoryApiController@store',    'members.create'],
    ['POST', '/api/membership-categories/{id}/update',  'MembershipCategoryApiController@update',   'members.edit'],
    ['POST', '/api/membership-categories/{id}/delete',  'MembershipCategoryApiController@delete',   'members.delete'],

    // â”€â”€â”€ Endorsement API Routes â”€â”€â”€
    ['POST', '/api/members/{id}/endorsements',          'EndorsementApiController@store',  'members.edit'],
];
```

## File: modules\members\views\create.php

```php
<div class="row justify-content-center">
    <div class="col-xl-10">
        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h5 class="mb-0"><i class="fas fa-user-plus me-2"></i><?= lang('members.create') ?></h5>
                <a href="/members" class="btn btn-sm btn-outline-secondary"><i class="fas fa-arrow-left me-1"></i><?= lang('common.back') ?></a>
            </div>
            <div class="card-body">
                <form id="member-form" method="POST" action="/api/members/store" enctype="multipart/form-data" data-ajax="true">
                    <?= csrf_field() ?>

                    <!-- Tabs -->
                    <ul class="nav nav-tabs mb-4" role="tablist">
                        <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-personal"><i class="fas fa-user me-1"></i> <?= lang('members.personal_info') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-contact"><i class="fas fa-phone me-1"></i> <?= lang('members.contact') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-address"><i class="fas fa-map-marker-alt me-1"></i> <?= lang('members.address') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-work"><i class="fas fa-briefcase me-1"></i> <?= lang('members.employment') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-extra"><i class="fas fa-cog me-1"></i> <?= lang('members.additional') ?></a></li>
                    </ul>

                    <div class="tab-content">
                        <!-- Personal Info Tab -->
                        <div class="tab-pane fade show active" id="tab-personal">
                            <div class="row g-3">
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.membership_type') ?> <span class="text-danger">*</span></label>
                                    <select name="membership_type_id" class="form-select" required>
                                        <option value="">â€” <?= lang('common.select') ?> â€”</option>
                                        <?php foreach ($types as $t): ?>
                                        <option value="<?= $t['id'] ?>" data-family="<?= $t['is_family_membership'] ?>"><?= e(current_lang() === 'ar' ? $t['name_ar'] : $t['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                    <div class="invalid-feedback" data-field="membership_type_id"></div>
                                </div>
                                <div class="col-md-2">
                                    <label class="form-label"><?= lang('members.title_field') ?></label>
                                    <select name="title" class="form-select">
                                        <option value="">â€”</option>
                                        <option value="mr">Mr</option><option value="mrs">Mrs</option><option value="ms">Ms</option>
                                        <option value="dr">Dr</option><option value="eng">Eng</option><option value="prof">Prof</option>
                                    </select>
                                </div>
                                <div class="col-md-7">
                                    <label class="form-label"><?= lang('members.gender') ?> <span class="text-danger">*</span></label>
                                    <div class="d-flex gap-3 mt-1">
                                        <div class="form-check"><input class="form-check-input" type="radio" name="gender" value="male" id="g-m" required><label class="form-check-label" for="g-m"><?= lang('members.male') ?></label></div>
                                        <div class="form-check"><input class="form-check-input" type="radio" name="gender" value="female" id="g-f"><label class="form-check-label" for="g-f"><?= lang('members.female') ?></label></div>
                                    </div>
                                    <div class="invalid-feedback" data-field="gender"></div>
                                </div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.first_name_en') ?> <span class="text-danger">*</span></label><input type="text" name="first_name_en" class="form-control" required><div class="invalid-feedback" data-field="first_name_en"></div></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.second_name_en') ?></label><input type="text" name="second_name_en" class="form-control"><div class="invalid-feedback" data-field="second_name_en"></div></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.third_name_en') ?></label><input type="text" name="third_name_en" class="form-control"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.last_name_en') ?> <span class="text-danger">*</span></label><input type="text" name="last_name_en" class="form-control" required><div class="invalid-feedback" data-field="last_name_en"></div></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.first_name_ar') ?></label><input type="text" name="first_name_ar" class="form-control" dir="rtl"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.second_name_ar') ?></label><input type="text" name="second_name_ar" class="form-control" dir="rtl"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.third_name_ar') ?></label><input type="text" name="third_name_ar" class="form-control" dir="rtl"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.last_name_ar') ?></label><input type="text" name="last_name_ar" class="form-control" dir="rtl"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.national_id') ?></label><input type="text" name="national_id" class="form-control" maxlength="20"><div class="invalid-feedback" data-field="national_id"></div></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.passport') ?></label><input type="text" name="passport_number" class="form-control" maxlength="30"></div>
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.nationality') ?></label>
                                    <select name="nationality_id" class="form-select select2">
                                        <option value="">â€”</option>
                                        <?php foreach ($nationalities as $n): ?>
                                        <option value="<?= $n['id'] ?>"><?= e(current_lang() === 'ar' ? $n['name_ar'] : $n['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.dob') ?></label><input type="date" name="date_of_birth" class="form-control"></div>
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.marital_status') ?></label>
                                    <select name="marital_status" class="form-select">
                                        <option value="">â€”</option>
                                        <option value="single"><?= lang('members.single') ?></option>
                                        <option value="married"><?= lang('members.married') ?></option>
                                        <option value="divorced"><?= lang('members.divorced') ?></option>
                                        <option value="widowed"><?= lang('members.widowed') ?></option>
                                    </select>
                                </div>
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.photo') ?></label>
                                    <input type="file" name="photo" class="form-control" accept="image/*">
                                </div>
                            </div>
                        </div>

                        <!-- Contact Tab -->
                        <div class="tab-pane fade" id="tab-contact">
                            <div class="row g-3">
                                <div class="col-md-4"><label class="form-label"><?= lang('members.primary_phone') ?></label><input type="text" name="primary_phone" class="form-control"><div class="invalid-feedback" data-field="primary_phone"></div></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.secondary_phone') ?></label><input type="text" name="secondary_phone" class="form-control"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.email') ?></label><input type="email" name="primary_email" class="form-control"><div class="invalid-feedback" data-field="primary_email"></div></div>
                            </div>
                        </div>

                        <!-- Address Tab -->
                        <div class="tab-pane fade" id="tab-address">
                            <div class="row g-3">
                                <div class="col-md-8"><label class="form-label"><?= lang('members.address_line1') ?></label><input type="text" name="address_line1" class="form-control"></div>
                                <div class="col-md-4">
                                    <label class="form-label"><?= lang('members.country') ?></label>
                                    <select name="country_id" class="form-select select2" id="country-select">
                                        <option value="">â€”</option>
                                        <?php foreach ($countries as $co): ?>
                                        <option value="<?= $co['id'] ?>"><?= e(current_lang() === 'ar' ? $co['name_ar'] : $co['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-4">
                                    <label class="form-label"><?= lang('members.city') ?></label>
                                    <select name="city_id" class="form-select" id="city-select"><option value="">â€”</option></select>
                                </div>
                            </div>
                        </div>

                        <!-- Employment Tab -->
                        <div class="tab-pane fade" id="tab-work">
                            <div class="row g-3">
                                <div class="col-md-4"><label class="form-label"><?= lang('members.occupation') ?></label><input type="text" name="occupation" class="form-control"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.employer') ?></label><input type="text" name="employer" class="form-control"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.job_title') ?></label><input type="text" name="job_title" class="form-control"></div>
                            </div>
                        </div>

                        <!-- Additional Tab -->
                        <div class="tab-pane fade" id="tab-extra">
                            <div class="row g-3">
                                <div class="col-md-4"><label class="form-label"><?= lang('members.emergency_name') ?></label><input type="text" name="emergency_contact_name" class="form-control"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.emergency_phone') ?></label><input type="text" name="emergency_contact_phone" class="form-control"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.emergency_relation') ?></label><input type="text" name="emergency_contact_relation" class="form-control"></div>
                                <div class="col-md-3">
                                    <div class="form-check mt-4"><input class="form-check-input" type="checkbox" name="vip" value="1" id="vip-check"><label class="form-check-label" for="vip-check"><i class="fas fa-star text-warning me-1"></i> VIP</label></div>
                                </div>
                                <div class="col-md-12"><label class="form-label"><?= lang('members.internal_notes') ?></label><textarea name="internal_notes" class="form-control" rows="3"></textarea></div>
                            </div>
                        </div>
                    </div>

                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/members" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Country â†’ City cascade
    document.getElementById('country-select')?.addEventListener('change', function() {
        const countryId = this.value;
        const citySelect = document.getElementById('city-select');
        citySelect.innerHTML = '<option value="">â€”</option>';
        if (!countryId) return;
        $.get('/api/membership-categories/list?country_id=' + countryId, function(r) {
            // The cities would need a separate API endpoint in a full implementation
            // For now, we handle this via a generic lookup
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\members\views\edit.php

```php
<div class="row justify-content-center">
    <div class="col-xl-10">
        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h5 class="mb-0"><i class="fas fa-edit me-2"></i><?= lang('members.edit') ?>: <?= e($member['membership_number']) ?></h5>
                <a href="/members/<?= $member['id'] ?>" class="btn btn-sm btn-outline-secondary"><i class="fas fa-arrow-left me-1"></i><?= lang('common.back') ?></a>
            </div>
            <div class="card-body">
                <form id="member-form" method="POST" action="/api/members/<?= $member['id'] ?>/update" enctype="multipart/form-data" data-ajax="true">
                    <?= csrf_field() ?>

                    <ul class="nav nav-tabs mb-4" role="tablist">
                        <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-personal"><i class="fas fa-user me-1"></i> <?= lang('members.personal_info') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-contact"><i class="fas fa-phone me-1"></i> <?= lang('members.contact') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-address"><i class="fas fa-map-marker-alt me-1"></i> <?= lang('members.address') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-work"><i class="fas fa-briefcase me-1"></i> <?= lang('members.employment') ?></a></li>
                        <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-extra"><i class="fas fa-cog me-1"></i> <?= lang('members.additional') ?></a></li>
                    </ul>

                    <div class="tab-content">
                        <div class="tab-pane fade show active" id="tab-personal">
                            <div class="row g-3">
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.membership_type') ?> <span class="text-danger">*</span></label>
                                    <select name="membership_type_id" class="form-select" required>
                                        <?php foreach ($types as $t): ?>
                                        <option value="<?= $t['id'] ?>" <?= selected($t['id'], $member['membership_type_id']) ?>><?= e(current_lang() === 'ar' ? $t['name_ar'] : $t['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-2">
                                    <label class="form-label"><?= lang('members.title_field') ?></label>
                                    <select name="title" class="form-select">
                                        <option value="">â€”</option>
                                        <?php foreach (['mr','mrs','ms','dr','eng','prof'] as $t): ?>
                                        <option value="<?= $t ?>" <?= selected($t, $member['title']) ?>><?= ucfirst($t) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-7">
                                    <label class="form-label"><?= lang('members.gender') ?> <span class="text-danger">*</span></label>
                                    <div class="d-flex gap-3 mt-1">
                                        <div class="form-check"><input class="form-check-input" type="radio" name="gender" value="male" <?= checked('male', $member['gender']) ?> required><label class="form-check-label"><?= lang('members.male') ?></label></div>
                                        <div class="form-check"><input class="form-check-input" type="radio" name="gender" value="female" <?= checked('female', $member['gender']) ?>><label class="form-check-label"><?= lang('members.female') ?></label></div>
                                    </div>
                                </div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.first_name_en') ?> *</label><input type="text" name="first_name_en" class="form-control" value="<?= e($member['first_name_en']) ?>" required></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.second_name_en') ?></label><input type="text" name="second_name_en" class="form-control" value="<?= e($member['second_name_en'] ?? '') ?>"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.third_name_en') ?></label><input type="text" name="third_name_en" class="form-control" value="<?= e($member['third_name_en'] ?? '') ?>"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.last_name_en') ?> *</label><input type="text" name="last_name_en" class="form-control" value="<?= e($member['last_name_en']) ?>" required></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.first_name_ar') ?></label><input type="text" name="first_name_ar" class="form-control" dir="rtl" value="<?= e($member['first_name_ar'] ?? '') ?>"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.second_name_ar') ?></label><input type="text" name="second_name_ar" class="form-control" dir="rtl" value="<?= e($member['second_name_ar'] ?? '') ?>"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.third_name_ar') ?></label><input type="text" name="third_name_ar" class="form-control" dir="rtl" value="<?= e($member['third_name_ar'] ?? '') ?>"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.last_name_ar') ?></label><input type="text" name="last_name_ar" class="form-control" dir="rtl" value="<?= e($member['last_name_ar'] ?? '') ?>"></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.national_id') ?></label><input type="text" name="national_id" class="form-control" value="<?= e($member['national_id'] ?? '') ?>"><div class="invalid-feedback" data-field="national_id"></div></div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.passport') ?></label><input type="text" name="passport_number" class="form-control" value="<?= e($member['passport_number'] ?? '') ?>"></div>
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.nationality') ?></label>
                                    <select name="nationality_id" class="form-select select2">
                                        <option value="">â€”</option>
                                        <?php foreach ($nationalities as $n): ?>
                                        <option value="<?= $n['id'] ?>" <?= selected($n['id'], $member['nationality_id']) ?>><?= e(current_lang() === 'ar' ? $n['name_ar'] : $n['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-3"><label class="form-label"><?= lang('members.dob') ?></label><input type="date" name="date_of_birth" class="form-control" value="<?= e($member['date_of_birth'] ?? '') ?>"></div>
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.marital_status') ?></label>
                                    <select name="marital_status" class="form-select">
                                        <option value="">â€”</option>
                                        <?php foreach (['single','married','divorced','widowed'] as $ms): ?>
                                        <option value="<?= $ms ?>" <?= selected($ms, $member['marital_status']) ?>><?= ucfirst($ms) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-3">
                                    <label class="form-label"><?= lang('members.photo') ?></label>
                                    <input type="file" name="photo" class="form-control" accept="image/*">
                                    <?php if ($member['photo_path']): ?><small class="text-muted">Current photo set</small><?php endif; ?>
                                </div>
                            </div>
                        </div>

                        <div class="tab-pane fade" id="tab-contact">
                            <div class="row g-3">
                                <div class="col-md-4"><label class="form-label"><?= lang('members.primary_phone') ?></label><input type="text" name="primary_phone" class="form-control" value="<?= e($member['primary_phone'] ?? '') ?>"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.secondary_phone') ?></label><input type="text" name="secondary_phone" class="form-control" value="<?= e($member['secondary_phone'] ?? '') ?>"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.email') ?></label><input type="email" name="primary_email" class="form-control" value="<?= e($member['primary_email'] ?? '') ?>"></div>
                            </div>
                        </div>

                        <div class="tab-pane fade" id="tab-address">
                            <div class="row g-3">
                                <div class="col-md-8"><label class="form-label"><?= lang('members.address_line1') ?></label><input type="text" name="address_line1" class="form-control" value="<?= e($member['address_line1'] ?? '') ?>"></div>
                                <div class="col-md-4">
                                    <label class="form-label"><?= lang('members.country') ?></label>
                                    <select name="country_id" class="form-select select2">
                                        <option value="">â€”</option>
                                        <?php foreach ($countries as $co): ?>
                                        <option value="<?= $co['id'] ?>" <?= selected($co['id'], $member['country_id']) ?>><?= e(current_lang() === 'ar' ? $co['name_ar'] : $co['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="col-md-4">
                                    <label class="form-label"><?= lang('members.city') ?></label>
                                    <select name="city_id" class="form-select">
                                        <option value="">â€”</option>
                                        <?php foreach ($cities as $ci): ?>
                                        <option value="<?= $ci['id'] ?>" <?= selected($ci['id'], $member['city_id']) ?>><?= e(current_lang() === 'ar' ? $ci['name_ar'] : $ci['name_en']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                            </div>
                        </div>

                        <div class="tab-pane fade" id="tab-work">
                            <div class="row g-3">
                                <div class="col-md-4"><label class="form-label"><?= lang('members.occupation') ?></label><input type="text" name="occupation" class="form-control" value="<?= e($member['occupation'] ?? '') ?>"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.employer') ?></label><input type="text" name="employer" class="form-control" value="<?= e($member['employer'] ?? '') ?>"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.job_title') ?></label><input type="text" name="job_title" class="form-control" value="<?= e($member['job_title'] ?? '') ?>"></div>
                            </div>
                        </div>

                        <div class="tab-pane fade" id="tab-extra">
                            <div class="row g-3">
                                <div class="col-md-4"><label class="form-label"><?= lang('members.emergency_name') ?></label><input type="text" name="emergency_contact_name" class="form-control" value="<?= e($member['emergency_contact_name'] ?? '') ?>"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.emergency_phone') ?></label><input type="text" name="emergency_contact_phone" class="form-control" value="<?= e($member['emergency_contact_phone'] ?? '') ?>"></div>
                                <div class="col-md-4"><label class="form-label"><?= lang('members.emergency_relation') ?></label><input type="text" name="emergency_contact_relation" class="form-control" value="<?= e($member['emergency_contact_relation'] ?? '') ?>"></div>
                                <div class="col-md-3"><div class="form-check mt-4"><input class="form-check-input" type="checkbox" name="vip" value="1" <?= checked(1, $member['vip']) ?>><label class="form-check-label"><i class="fas fa-star text-warning me-1"></i> VIP</label></div></div>
                                <div class="col-md-12"><label class="form-label"><?= lang('members.internal_notes') ?></label><textarea name="internal_notes" class="form-control" rows="3"><?= e($member['internal_notes'] ?? '') ?></textarea></div>
                            </div>
                        </div>
                    </div>

                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/members/<?= $member['id'] ?>" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\members\views\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-users me-2"></i><?= lang('members.title') ?></h4>
    <div class="d-flex gap-2">
        <?php if (auth_can('members.view')): ?>
        <div class="dropdown">
            <button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown"><i class="fas fa-download me-1"></i><?= lang('common.export_csv') ?></button>
            <ul class="dropdown-menu">
                <li><a class="dropdown-item" href="/api/members/export?format=csv"><i class="fas fa-file-csv me-1"></i> CSV</a></li>
                <li><a class="dropdown-item" href="/api/members/export?format=excel"><i class="fas fa-file-excel me-1"></i> Excel</a></li>
            </ul>
        </div>
        <?php endif; ?>
        <?php if (auth_can('members.create')): ?>
        <a href="/members/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('members.add_member') ?></a>
        <?php endif; ?>
    </div>
</div>

<!-- Stats Cards -->
<div class="row g-3 mb-4" id="stats-cards">
    <div class="col-md-3"><div class="card border-success h-100"><div class="card-body"><h6 class="text-muted mb-1"><?= lang('members.total_active') ?></h6><h3 class="mb-0" id="stat-active">â€”</h3></div></div></div>
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><h6 class="text-muted mb-1"><?= lang('members.new_this_month') ?></h6><h3 class="mb-0" id="stat-new">â€”</h3></div></div></div>
    <div class="col-md-3"><div class="card border-info h-100"><div class="card-body"><h6 class="text-muted mb-1"><?= lang('members.frozen') ?></h6><h3 class="mb-0" id="stat-frozen">â€”</h3></div></div></div>
    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body"><h6 class="text-muted mb-1"><?= lang('members.suspended') ?></h6><h3 class="mb-0" id="stat-suspended">â€”</h3></div></div></div>
</div>

<!-- Filters -->
<div class="card mb-4">
    <div class="card-body py-3">
        <form id="filter-form" data-filter-table="members-table">
            <div class="row g-2 align-items-end">
                <div class="col-md-2">
                    <label class="form-label form-label-sm"><?= lang('common.status') ?></label>
                    <select name="filter_status" class="form-select form-select-sm">
                        <option value=""><?= lang('common.all') ?></option>
                        <option value="active"><?= lang('members.status_active') ?></option>
                        <option value="suspended"><?= lang('members.status_suspended') ?></option>
                        <option value="frozen"><?= lang('members.status_frozen') ?></option>
                        <option value="terminated"><?= lang('members.status_terminated') ?></option>
                        <option value="pending_approval"><?= lang('members.status_pending') ?></option>
                    </select>
                </div>
                <div class="col-md-2">
                    <label class="form-label form-label-sm"><?= lang('members.type') ?></label>
                    <select name="filter_type" class="form-select form-select-sm">
                        <option value=""><?= lang('common.all') ?></option>
                        <?php foreach ($types as $t): ?>
                        <option value="<?= $t['id'] ?>"><?= e(current_lang() === 'ar' ? $t['name_ar'] : $t['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-2">
                    <label class="form-label form-label-sm"><?= lang('members.gender') ?></label>
                    <select name="filter_gender" class="form-select form-select-sm">
                        <option value=""><?= lang('common.all') ?></option>
                        <option value="male"><?= lang('members.male') ?></option>
                        <option value="female"><?= lang('members.female') ?></option>
                    </select>
                </div>
                <div class="col-md-2">
                    <label class="form-label form-label-sm"><?= lang('members.join_from') ?></label>
                    <input type="date" name="filter_date_from" class="form-control form-control-sm">
                </div>
                <div class="col-md-2">
                    <label class="form-label form-label-sm"><?= lang('members.join_to') ?></label>
                    <input type="date" name="filter_date_to" class="form-control form-control-sm">
                </div>
                <div class="col-md-2">
                    <button type="button" class="btn btn-sm btn-primary w-100" id="btn-apply-filter"><i class="fas fa-search me-1"></i><?= lang('common.filter') ?></button>
                </div>
            </div>
        </form>
    </div>
</div>

<!-- DataTable -->
<div class="card">
    <div class="card-body">
        <table id="members-table" class="table table-striped table-hover w-100"
               data-url="/api/members/datatable"
               data-columns='[
                   {"data":"photo","orderable":false,"searchable":false,"title":""},
                   {"data":"membership_number","title":"<?= lang('members.membership_no') ?>"},
                   {"data":"name","title":"<?= lang('members.name') ?>"},
                   {"data":"national_id","title":"<?= lang('members.national_id') ?>"},
                   {"data":"phone","title":"<?= lang('members.phone') ?>"},
                   {"data":"type","title":"<?= lang('members.type') ?>"},
                   {"data":"status","title":"<?= lang('common.status') ?>"},
                   {"data":"join_date","title":"<?= lang('members.join_date') ?>"},
                   {"data":"actions","orderable":false,"searchable":false,"title":"<?= lang('common.actions') ?>"}
               ]'>
            <thead>
                <tr>
                    <th></th>
                    <th><?= lang('members.membership_no') ?></th>
                    <th><?= lang('members.name') ?></th>
                    <th><?= lang('members.national_id') ?></th>
                    <th><?= lang('members.phone') ?></th>
                    <th><?= lang('members.type') ?></th>
                    <th><?= lang('common.status') ?></th>
                    <th><?= lang('members.join_date') ?></th>
                    <th><?= lang('common.actions') ?></th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
    const dt = DataTableHelper.init('members-table');

    // Load stats
    $.get('/api/members/stats', function(r) {
        if (r.success !== false) {
            document.getElementById('stat-active').textContent = r.active?.toLocaleString() ?? '0';
            document.getElementById('stat-new').textContent = r.new_month?.toLocaleString() ?? '0';
            document.getElementById('stat-frozen').textContent = r.frozen?.toLocaleString() ?? '0';
            document.getElementById('stat-suspended').textContent = r.suspended?.toLocaleString() ?? '0';
        }
    });

    // Filter button
    document.getElementById('btn-apply-filter')?.addEventListener('click', function() {
        DataTableHelper.reload('members-table');
    });

    // Delete member
    $(document).on('click', '.btn-delete-member', function() {
        const id = $(this).data('id');
        Swal.fire({
            title: '<?= lang('common.are_you_sure') ?>',
            text: '<?= lang('common.cannot_undo') ?>',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#e53e3e',
            confirmButtonText: '<?= lang('common.yes') ?>, <?= lang('common.delete') ?>',
            cancelButtonText: '<?= lang('common.cancel') ?>'
        }).then((result) => {
            if (result.isConfirmed) {
                $.post('/api/members/' + id + '/delete', {_token: '<?= csrf_token() ?>'}, function(r) {
                    if (r.success) {
                        FormHelper.showToast('success', r.message);
                        DataTableHelper.reload('members-table');
                    } else {
                        FormHelper.showToast('error', r.message);
                    }
                });
            }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\members\views\membership-categories\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tags me-2"></i><?= lang('members.categories') ?></h4>
    <?php if (auth_can('members.create')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#catModal" id="btn-add-cat"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></button>
    <?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="cats-table" class="table table-striped w-100"
           data-url="/api/membership-categories/datatable"
           data-columns='[{"data":"code","title":"Code"},{"data":"name_en","title":"Name (EN)"},{"data":"name_ar","title":"Name (AR)"},{"data":"type_count","title":"Types"},{"data":"is_active","title":"Status"},{"data":"sort_order","title":"Sort"},{"data":"actions","title":"","orderable":false}]'>
        <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Types</th><th>Status</th><th>Sort</th><th></th></tr></thead>
    </table>
</div></div>

<div class="modal fade" id="catModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title" id="catModalTitle">Add Category</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="cat-form"><?= csrf_field() ?><input type="hidden" name="cat_id" id="cat-edit-id" value="">
            <div class="mb-3"><label class="form-label">Code *</label><input type="text" name="category_code" class="form-control" id="cat-code" required><div class="invalid-feedback" data-field="category_code"></div></div>
            <div class="mb-3"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" id="cat-name-en" required><div class="invalid-feedback" data-field="name_en"></div></div>
            <div class="mb-3"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" id="cat-name-ar" dir="rtl" required><div class="invalid-feedback" data-field="name_ar"></div></div>
            <div class="mb-3"><label class="form-label">Sort Order</label><input type="number" name="sort_order" class="form-control" id="cat-sort" value="0"></div>
        </form>
    </div>
    <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button><button type="button" class="btn btn-primary" id="btn-save-cat">Save</button></div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
DataTableHelper.init('cats-table');
document.getElementById('btn-add-cat')?.addEventListener('click', function() {
    document.getElementById('cat-edit-id').value = '';
    document.getElementById('cat-code').disabled = false;
    document.getElementById('cat-form').reset();
    document.getElementById('catModalTitle').textContent = 'Add Category';
});
$(document).on('click', '.btn-edit-cat', function() {
    document.getElementById('cat-edit-id').value = $(this).data('id');
    document.getElementById('cat-code').value = $(this).data('code');
    document.getElementById('cat-code').disabled = true;
    document.getElementById('cat-name-en').value = $(this).data('name-en');
    document.getElementById('cat-name-ar').value = $(this).data('name-ar');
    document.getElementById('cat-sort').value = $(this).data('sort');
    document.getElementById('catModalTitle').textContent = 'Edit Category';
    new bootstrap.Modal(document.getElementById('catModal')).show();
});
document.getElementById('btn-save-cat')?.addEventListener('click', function() {
    const id = document.getElementById('cat-edit-id').value;
    const url = id ? '/api/membership-categories/' + id + '/update' : '/api/membership-categories/store';
    $.post(url, $('#cat-form').serialize(), function(r) {
        if (r.success) { FormHelper.showToast('success', r.message); bootstrap.Modal.getInstance(document.getElementById('catModal')).hide(); DataTableHelper.reload('cats-table'); }
        else { FormHelper.showToast('error', r.message); if (r.errors) FormHelper.showErrors(document.getElementById('cat-form'), r.errors); }
    });
});
$(document).on('click', '.btn-delete-cat', function() {
    const id = $(this).data('id');
    Swal.fire({ title: 'Delete category?', icon: 'warning', showCancelButton: true, confirmButtonColor: '#e53e3e' }).then((r) => {
        if (r.isConfirmed) $.post('/api/membership-categories/' + id + '/delete', {_token: '<?= csrf_token() ?>'}, function(r) {
            if (r.success) { FormHelper.showToast('success', r.message); DataTableHelper.reload('cats-table'); } else { FormHelper.showToast('error', r.message); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\members\views\membership-types\create.php

```php
<div class="row justify-content-center"><div class="col-xl-8">
    <div class="card"><div class="card-header"><h5 class="mb-0"><?= lang('members.create_type') ?></h5></div><div class="card-body">
        <form method="POST" action="/api/membership-types/store" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Type Code *</label><input type="text" name="type_code" class="form-control" required><div class="invalid-feedback" data-field="type_code"></div></div>
                <div class="col-md-4"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" required><div class="invalid-feedback" data-field="name_en"></div></div>
                <div class="col-md-4"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" dir="rtl" required><div class="invalid-feedback" data-field="name_ar"></div></div>
                <div class="col-md-4"><label class="form-label">Category</label><select name="category_id" class="form-select"><option value="">â€”</option><?php foreach ($categories as $c): ?><option value="<?= $c['id'] ?>"><?= e($c['name_en']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Gender Restriction</label><select name="gender_restriction" class="form-select"><option value="both">Both</option><option value="male">Male Only</option><option value="female">Female Only</option></select></div>
                <div class="col-md-4"><label class="form-label">Grace Period (days)</label><input type="number" name="grace_period_days" class="form-control" value="30"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="is_family_membership" value="1" class="form-check-input" id="ck-fam"><label class="form-check-label" for="ck-fam">Family Membership</label></div></div>
                <div class="col-md-3"><label class="form-label">Max Dependents</label><input type="number" name="max_dependents" class="form-control" min="0"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="requires_sponsor" value="1" class="form-check-input" id="ck-sp"><label class="form-check-label" for="ck-sp">Requires Sponsor</label></div></div>
                <div class="col-md-3"><label class="form-label">Sponsor Count</label><input type="number" name="sponsor_count" class="form-control" min="0" value="0"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="requires_board_approval" value="1" class="form-check-input"><label class="form-check-label">Board Approval</label></div></div>
                <div class="col-md-3"><label class="form-label">Sort Order</label><input type="number" name="sort_order" class="form-control" value="10"></div>
            </div>
            <div class="mt-4"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button> <a href="/membership-types" class="btn btn-secondary">Cancel</a></div>
        </form>
    </div></div>
</div></div>
```

## File: modules\members\views\membership-types\edit.php

```php
<div class="row justify-content-center"><div class="col-xl-8">
    <div class="card"><div class="card-header"><h5 class="mb-0">Edit: <?= e($type['name_en']) ?></h5></div><div class="card-body">
        <form method="POST" action="/api/membership-types/<?= $type['id'] ?>/update" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Type Code</label><input type="text" class="form-control" value="<?= e($type['type_code']) ?>" disabled></div>
                <div class="col-md-4"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" value="<?= e($type['name_en']) ?>" required></div>
                <div class="col-md-4"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" dir="rtl" value="<?= e($type['name_ar']) ?>" required></div>
                <div class="col-md-4"><label class="form-label">Category</label><select name="category_id" class="form-select"><option value="">â€”</option><?php foreach ($categories as $c): ?><option value="<?= $c['id'] ?>" <?= selected($c['id'], $type['category_id']) ?>><?= e($c['name_en']) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Gender Restriction</label><select name="gender_restriction" class="form-select"><?php foreach (['both','male','female'] as $g): ?><option value="<?= $g ?>" <?= selected($g, $type['gender_restriction']) ?>><?= ucfirst($g) ?></option><?php endforeach; ?></select></div>
                <div class="col-md-4"><label class="form-label">Grace Period (days)</label><input type="number" name="grace_period_days" class="form-control" value="<?= $type['grace_period_days'] ?>"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="is_family_membership" value="1" class="form-check-input" <?= checked(1, $type['is_family_membership']) ?>><label class="form-check-label">Family</label></div></div>
                <div class="col-md-3"><label class="form-label">Max Deps</label><input type="number" name="max_dependents" class="form-control" value="<?= $type['max_dependents'] ?>"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="requires_sponsor" value="1" class="form-check-input" <?= checked(1, $type['requires_sponsor']) ?>><label class="form-check-label">Sponsor</label></div></div>
                <div class="col-md-3"><label class="form-label">Sponsor Count</label><input type="number" name="sponsor_count" class="form-control" value="<?= $type['sponsor_count'] ?>"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="requires_board_approval" value="1" class="form-check-input" <?= checked(1, $type['requires_board_approval']) ?>><label class="form-check-label">Board Approval</label></div></div>
                <div class="col-md-3"><label class="form-label">Sort</label><input type="number" name="sort_order" class="form-control" value="<?= $type['sort_order'] ?>"></div>
                <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="is_active" value="1" class="form-check-input" <?= checked(1, $type['is_active']) ?>><label class="form-check-label">Active</label></div></div>
            </div>
            <div class="mt-4"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button> <a href="/membership-types" class="btn btn-secondary">Cancel</a></div>
        </form>
    </div></div>
</div></div>
```

## File: modules\members\views\membership-types\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-layer-group me-2"></i><?= lang('members.membership_types') ?></h4>
    <?php if (auth_can('members.create')): ?>
    <a href="/membership-types/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
    <?php endif; ?>
</div>
<div class="card"><div class="card-body">
    <table id="types-table" class="table table-striped w-100"
           data-url="/api/membership-types/datatable"
           data-columns='[{"data":"type_code","title":"Code"},{"data":"name_en","title":"Name (EN)"},{"data":"name_ar","title":"Name (AR)"},{"data":"category","title":"Category"},{"data":"is_family","title":"Family","orderable":false},{"data":"max_dependents","title":"Max Deps"},{"data":"active_count","title":"Members"},{"data":"is_active","title":"Status"},{"data":"actions","title":"","orderable":false}]'>
        <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Category</th><th>Family</th><th>Max Deps</th><th>Members</th><th>Status</th><th></th></tr></thead>
    </table>
</div></div>
<?php $__view->push('scripts') ?><script>DataTableHelper.init('types-table');</script><?php $__view->endPush() ?>
```

## File: modules\members\views\print.php

```php
<div style="max-width:800px;margin:0 auto;">
    <div class="text-center mb-4">
        <h3><?= e(Config::get('club_name_en', 'The Club')) ?></h3>
        <h5>Member Profile</h5>
        <hr>
    </div>
    <table class="table table-borderless">
        <tr><td><strong>Membership #:</strong></td><td><?= e($member['membership_number']) ?></td><td><strong>Status:</strong></td><td><?= ucfirst($member['status']) ?></td></tr>
        <tr><td><strong>Full Name (EN):</strong></td><td><?= e($member['full_name_en']) ?></td><td><strong>Type:</strong></td><td><?= e($member['type_name_en']) ?></td></tr>
        <tr><td><strong>National ID:</strong></td><td><?= e($member['national_id'] ?? 'â€”') ?></td><td><strong>Gender:</strong></td><td><?= ucfirst($member['gender']) ?></td></tr>
        <tr><td><strong>Phone:</strong></td><td><?= e($member['primary_phone'] ?? 'â€”') ?></td><td><strong>Email:</strong></td><td><?= e($member['primary_email'] ?? 'â€”') ?></td></tr>
        <tr><td><strong>DOB:</strong></td><td><?= format_date($member['date_of_birth']) ?></td><td><strong>Join Date:</strong></td><td><?= format_date($member['join_date']) ?></td></tr>
        <tr><td><strong>Nationality:</strong></td><td><?= e($member['nationality_en'] ?? 'â€”') ?></td><td><strong>City:</strong></td><td><?= e($member['city_en'] ?? 'â€”') ?></td></tr>
    </table>
    <?php if (!empty($dependents)): ?>
    <h6 class="mt-4">Dependents</h6>
    <table class="table table-sm"><thead><tr><th>Name</th><th>Relationship</th><th>Gender</th><th>DOB</th></tr></thead><tbody>
    <?php foreach ($dependents as $d): ?>
    <tr><td><?= e($d['full_name_en']) ?></td><td><?= e($d['rel_type_en']) ?></td><td><?= ucfirst($d['gender']) ?></td><td><?= format_date($d['date_of_birth']) ?></td></tr>
    <?php endforeach; ?>
    </tbody></table>
    <?php endif; ?>
    <div class="text-center mt-5 small text-muted">
        <p>Printed on <?= date('Y-m-d H:i:s') ?> by <?= e(auth_user('full_name_en')) ?></p>
    </div>
</div>
```

## File: modules\members\views\show.php

```php
<?php $m = $member; ?>
<div class="row g-4">
    <!-- Left Sidebar -->
    <div class="col-lg-3">
        <div class="card text-center">
            <div class="card-body">
                <?php if ($m['photo_path']): ?>
                <img src="/uploads/<?= e($m['photo_path']) ?>" class="rounded-circle mb-3" style="width:120px;height:120px;object-fit:cover" alt="">
                <?php else: ?>
                <div class="rounded-circle bg-secondary d-inline-flex align-items-center justify-content-center mb-3" style="width:120px;height:120px"><i class="fas fa-user fa-3x text-white"></i></div>
                <?php endif; ?>
                <h5><?= e($m['full_name_en']) ?><?php if ($m['vip']): ?> <i class="fas fa-star text-warning"></i><?php endif; ?></h5>
                <?php if ($m['full_name_ar']): ?><p class="text-muted mb-1"><?= e($m['full_name_ar']) ?></p><?php endif; ?>
                <p class="mb-1"><strong><?= e($m['membership_number']) ?></strong></p>
                <p class="mb-2"><?= status_badge($m['status']) ?></p>
                <p class="small text-muted mb-0"><?= e($m['type_name_en']) ?></p>
            </div>
            <div class="card-footer">
                <div class="d-flex gap-1 justify-content-center flex-wrap">
                    <?php if (auth_can('members.edit')): ?>
                    <a href="/members/<?= $m['id'] ?>/edit" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i></a>
                    <?php endif; ?>
                    <a href="/members/<?= $m['id'] ?>/print" class="btn btn-sm btn-outline-info" target="_blank"><i class="fas fa-print"></i></a>
                    <?php if (auth_can('members.status')): ?>
                    <button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#statusModal"><i class="fas fa-exchange-alt"></i></button>
                    <?php endif; ?>
                </div>
            </div>
        </div>

        <!-- Quick Info -->
        <div class="card mt-3">
            <div class="card-body small">
                <?php if ($m['primary_phone']): ?><p class="mb-1"><i class="fas fa-phone me-2 text-muted"></i><?= e($m['primary_phone']) ?></p><?php endif; ?>
                <?php if ($m['primary_email']): ?><p class="mb-1"><i class="fas fa-envelope me-2 text-muted"></i><?= e($m['primary_email']) ?></p><?php endif; ?>
                <?php if ($m['nationality_en']): ?><p class="mb-1"><i class="fas fa-flag me-2 text-muted"></i><?= e($m['nationality_en']) ?></p><?php endif; ?>
                <?php if ($m['date_of_birth']): ?><p class="mb-1"><i class="fas fa-birthday-cake me-2 text-muted"></i><?= format_date($m['date_of_birth']) ?></p><?php endif; ?>
                <p class="mb-1"><i class="fas fa-calendar me-2 text-muted"></i>Joined: <?= format_date($m['join_date']) ?></p>
                <p class="mb-0"><i class="fas fa-walking me-2 text-muted"></i>Visits: <?= number_format($m['visit_count']) ?></p>
            </div>
        </div>

        <!-- Tags -->
        <div class="card mt-3">
            <div class="card-header py-2"><small class="fw-bold">Tags</small></div>
            <div class="card-body" id="tags-container">
                <?php foreach ($tags as $tag): ?>
                <span class="badge me-1 mb-1" style="background:<?= e($tag['tag_color'] ?? '#6c757d') ?>"><?= e($tag['tag_name']) ?>
                    <?php if (auth_can('members.edit')): ?>
                    <i class="fas fa-times ms-1 btn-remove-tag" data-id="<?= $tag['id'] ?>" style="cursor:pointer"></i>
                    <?php endif; ?>
                </span>
                <?php endforeach; ?>
                <?php if (auth_can('members.edit')): ?>
                <div class="input-group input-group-sm mt-2">
                    <input type="text" class="form-control" id="new-tag-input" placeholder="Add tag...">
                    <button class="btn btn-outline-primary" id="btn-add-tag"><i class="fas fa-plus"></i></button>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>

    <!-- Main Content -->
    <div class="col-lg-9">
        <ul class="nav nav-tabs mb-3" role="tablist">
            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#t-overview">Overview</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#t-family">Family & Deps</a></li>
            <?php if (auth_can('members.financial')): ?>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#t-financial">Financial</a></li>
            <?php endif; ?>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#t-docs">Documents</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#t-subs">Subscriptions</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#t-notes">Notes</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#t-history">History</a></li>
        </ul>

        <div class="tab-content">
            <!-- Overview -->
            <div class="tab-pane fade show active" id="t-overview">
                <div class="card"><div class="card-body">
                    <div class="row">
                        <div class="col-md-6">
                            <table class="table table-sm table-borderless">
                                <tr><td class="text-muted w-40">National ID</td><td><?= e($m['national_id'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">Passport</td><td><?= e($m['passport_number'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">Gender</td><td><?= ucfirst($m['gender']) ?></td></tr>
                                <tr><td class="text-muted">Marital Status</td><td><?= ucfirst($m['marital_status'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">Occupation</td><td><?= e($m['occupation'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">Employer</td><td><?= e($m['employer'] ?? 'â€”') ?></td></tr>
                            </table>
                        </div>
                        <div class="col-md-6">
                            <table class="table table-sm table-borderless">
                                <tr><td class="text-muted w-40">Address</td><td><?= e($m['address_line1'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">City</td><td><?= e($m['city_en'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">Country</td><td><?= e($m['country_en'] ?? 'â€”') ?></td></tr>
                                <tr><td class="text-muted">Emergency</td><td><?= e($m['emergency_contact_name'] ?? 'â€”') ?> <?= $m['emergency_contact_phone'] ? '(' . e($m['emergency_contact_phone']) . ')' : '' ?></td></tr>
                                <tr><td class="text-muted">Last Visit</td><td><?= format_date($m['last_visit_date']) ?: 'â€”' ?></td></tr>
                                <tr><td class="text-muted">Balance</td><td class="<?= $m['outstanding_balance'] > 0 ? 'text-danger fw-bold' : 'text-success' ?>"><?= money($m['outstanding_balance']) ?></td></tr>
                            </table>
                        </div>
                    </div>
                </div></div>

                <!-- Status Timeline -->
                <?php if (!empty($statusHistory)): ?>
                <div class="card mt-3"><div class="card-header py-2"><small class="fw-bold">Status History</small></div><div class="card-body">
                    <div class="table-responsive"><table class="table table-sm">
                        <thead><tr><th>Date</th><th>From</th><th>To</th><th>Reason</th><th>By</th></tr></thead>
                        <tbody>
                        <?php foreach ($statusHistory as $sh): ?>
                        <tr>
                            <td><?= format_datetime($sh['changed_at']) ?></td>
                            <td><?= $sh['from_status'] ? status_badge($sh['from_status']) : 'â€”' ?></td>
                            <td><?= status_badge($sh['to_status']) ?></td>
                            <td><?= e($sh['reason'] ?? 'â€”') ?></td>
                            <td><?= e($sh['changed_by_name'] ?? 'â€”') ?></td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table></div>
                </div></div>
                <?php endif; ?>
            </div>

            <!-- Family & Dependents -->
            <div class="tab-pane fade" id="t-family">
                <div class="card"><div class="card-header d-flex justify-content-between align-items-center py-2">
                    <small class="fw-bold">Dependents (<?= count($dependents) ?>)</small>
                    <?php if (auth_can('dependents.manage')): ?>
                    <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#depModal" id="btn-add-dep"><i class="fas fa-plus me-1"></i>Add</button>
                    <?php endif; ?>
                </div><div class="card-body">
                    <?php if (empty($dependents)): ?>
                    <p class="text-muted text-center py-3">No dependents</p>
                    <?php else: ?>
                    <div class="table-responsive"><table class="table table-sm">
                        <thead><tr><th>Name</th><th>Relationship</th><th>Gender</th><th>DOB</th><th>Status</th><th>Actions</th></tr></thead>
                        <tbody>
                        <?php foreach ($dependents as $dep): ?>
                        <tr>
                            <td><?= e($dep['full_name_en']) ?></td>
                            <td><?= e($dep['rel_type_en']) ?></td>
                            <td><?= ucfirst($dep['gender']) ?></td>
                            <td><?= format_date($dep['date_of_birth']) ?></td>
                            <td><?= status_badge($dep['status']) ?></td>
                            <td>
                                <?php if (auth_can('dependents.manage')): ?>
                                <button class="btn btn-sm btn-outline-warning btn-edit-dep" data-id="<?= $dep['id'] ?>"><i class="fas fa-edit"></i></button>
                                <button class="btn btn-sm btn-outline-danger btn-delete-dep" data-id="<?= $dep['id'] ?>"><i class="fas fa-trash"></i></button>
                                <?php endif; ?>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table></div>
                    <?php endif; ?>
                </div></div>
            </div>

            <!-- Financial -->
            <?php if (auth_can('members.financial')): ?>
            <div class="tab-pane fade" id="t-financial">
                <div class="row g-3 mb-3">
                    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body text-center"><small class="text-muted">Total Invoiced</small><h5><?= money($balances['total_invoiced'] ?? 0) ?></h5></div></div></div>
                    <div class="col-md-3"><div class="card border-success h-100"><div class="card-body text-center"><small class="text-muted">Total Paid</small><h5><?= money($balances['total_paid'] ?? 0) ?></h5></div></div></div>
                    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body text-center"><small class="text-muted">Outstanding</small><h5 class="text-danger"><?= money($balances['outstanding_balance'] ?? 0) ?></h5></div></div></div>
                    <div class="col-md-3"><div class="card border-info h-100"><div class="card-body text-center"><small class="text-muted">Credit</small><h5 class="text-success"><?= money($balances['credit_balance'] ?? 0) ?></h5></div></div></div>
                </div>
                <div class="card"><div class="card-header py-2"><small class="fw-bold">Recent Invoices</small></div><div class="card-body">
                    <div class="table-responsive"><table class="table table-sm">
                        <thead><tr><th>Invoice #</th><th>Type</th><th>Date</th><th>Total</th><th>Paid</th><th>Balance</th><th>Status</th></tr></thead>
                        <tbody>
                        <?php foreach ($recentInvoices as $inv): ?>
                        <tr>
                            <td><?= e($inv['invoice_number']) ?></td><td><?= ucfirst(str_replace('_', ' ', $inv['invoice_type'])) ?></td>
                            <td><?= format_date($inv['invoice_date']) ?></td><td><?= money($inv['total_amount']) ?></td>
                            <td><?= money($inv['paid_amount']) ?></td><td><?= money($inv['balance_due']) ?></td><td><?= status_badge($inv['status']) ?></td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table></div>
                </div></div>
            </div>
            <?php endif; ?>

            <!-- Documents -->
            <div class="tab-pane fade" id="t-docs">
                <div class="card"><div class="card-header d-flex justify-content-between align-items-center py-2">
                    <small class="fw-bold">Documents (<?= count($documents) ?>)</small>
                    <?php if (auth_can('members.edit')): ?>
                    <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#docUploadModal"><i class="fas fa-upload me-1"></i>Upload</button>
                    <?php endif; ?>
                </div><div class="card-body">
                    <div class="table-responsive"><table class="table table-sm">
                        <thead><tr><th>Name</th><th>Type</th><th>Uploaded</th><th>Expiry</th><th>Verified</th><th>Actions</th></tr></thead>
                        <tbody>
                        <?php foreach ($documents as $doc): ?>
                        <tr>
                            <td><?= e($doc['document_name']) ?></td>
                            <td><?= e($doc['document_type']) ?></td>
                            <td><?= format_date($doc['uploaded_at']) ?></td>
                            <td><?= format_date($doc['expiry_date']) ?: 'â€”' ?></td>
                            <td><?= $doc['is_verified'] ? '<i class="fas fa-check-circle text-success"></i>' : '<i class="fas fa-clock text-warning"></i>' ?></td>
                            <td>
                                <a href="/uploads/<?= e($doc['file_path']) ?>" class="btn btn-sm btn-outline-primary" target="_blank"><i class="fas fa-download"></i></a>
                                <?php if (auth_can('members.edit') && !$doc['is_verified']): ?>
                                <button class="btn btn-sm btn-outline-success btn-verify-doc" data-id="<?= $doc['id'] ?>"><i class="fas fa-check"></i></button>
                                <?php endif; ?>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table></div>
                </div></div>
            </div>

            <!-- Subscriptions -->
            <div class="tab-pane fade" id="t-subs">
                <div class="card"><div class="card-body">
                    <div class="table-responsive"><table class="table table-sm">
                        <thead><tr><th>Period</th><th>Type</th><th>Start</th><th>End</th><th>Amount</th><th>Status</th></tr></thead>
                        <tbody>
                        <?php foreach ($subscriptions as $sub): ?>
                        <tr>
                            <td><?= e($sub['period_name_en'] ?? 'â€”') ?></td><td><?= e($sub['sub_type_en'] ?? 'â€”') ?></td>
                            <td><?= format_date($sub['start_date']) ?></td><td><?= format_date($sub['end_date']) ?></td>
                            <td><?= money($sub['net_amount']) ?></td><td><?= status_badge($sub['status']) ?></td>
                        </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table></div>
                </div></div>
            </div>

            <!-- Notes -->
            <div class="tab-pane fade" id="t-notes">
                <div class="card"><div class="card-header d-flex justify-content-between align-items-center py-2">
                    <small class="fw-bold">Notes</small>
                    <?php if (auth_can('members.edit')): ?>
                    <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#noteModal"><i class="fas fa-plus me-1"></i>Add Note</button>
                    <?php endif; ?>
                </div><div class="card-body">
                    <?php foreach ($notes as $note): ?>
                    <div class="border rounded p-3 mb-2 <?= $note['is_pinned'] ? 'border-warning' : '' ?>">
                        <div class="d-flex justify-content-between">
                            <div><span class="badge bg-<?= $note['note_type'] === 'general' ? 'secondary' : ($note['note_type'] === 'financial' ? 'info' : 'warning') ?>"><?= ucfirst($note['note_type']) ?></span>
                            <?php if ($note['is_pinned']): ?><i class="fas fa-thumbtack text-warning ms-1"></i><?php endif; ?>
                            <?php if ($note['subject']): ?><strong class="ms-2"><?= e($note['subject']) ?></strong><?php endif; ?>
                            </div>
                            <small class="text-muted"><?= time_ago($note['created_at']) ?> by <?= e($note['author_name'] ?? 'â€”') ?></small>
                        </div>
                        <p class="mb-0 mt-1"><?= nl2br(e($note['note_text'])) ?></p>
                    </div>
                    <?php endforeach; ?>
                    <?php if (empty($notes)): ?><p class="text-muted text-center">No notes yet</p><?php endif; ?>
                </div></div>
            </div>

            <!-- History -->
            <div class="tab-pane fade" id="t-history">
                <div class="card"><div class="card-header py-2"><small class="fw-bold">Recent Access</small></div><div class="card-body">
                    <div class="table-responsive"><table class="table table-sm">
                        <thead><tr><th>Time</th><th>Direction</th><th>Access Point</th><th>Granted</th></tr></thead>
                        <tbody>
                        <?php foreach ($accessLog as $al): ?>
                        <tr><td><?= format_datetime($al['access_time']) ?></td><td><?= ucfirst($al['direction']) ?></td><td><?= e($al['point_name'] ?? 'â€”') ?></td><td><?= $al['granted'] ? '<i class="fas fa-check text-success"></i>' : '<i class="fas fa-times text-danger"></i>' ?></td></tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table></div>
                </div></div>
            </div>
        </div>
    </div>
</div>

<!-- Status Change Modal -->
<div class="modal fade" id="statusModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Change Status</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="status-form">
            <?= csrf_field() ?>
            <div class="mb-3"><label class="form-label">New Status *</label>
                <select name="status" class="form-select" required>
                    <option value="">Select...</option>
                    <option value="active">Active</option><option value="suspended">Suspended</option>
                    <option value="terminated">Terminated</option><option value="deceased">Deceased</option>
                </select>
            </div>
            <div class="mb-3"><label class="form-label">Reason *</label><textarea name="reason" class="form-control" rows="3" required></textarea></div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary" id="btn-save-status">Save</button>
    </div>
</div></div></div>

<!-- Dependent Modal -->
<div class="modal fade" id="depModal" tabindex="-1"><div class="modal-dialog modal-lg"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title" id="depModalTitle">Add Dependent</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="dep-form">
            <?= csrf_field() ?>
            <input type="hidden" name="dep_id" id="dep-id" value="">
            <div class="row g-3">
                <div class="col-md-4"><label class="form-label">Relationship *</label><select name="relationship_type_id" class="form-select" required><option value="">â€”</option><?php foreach ($relTypes as $rt): ?><option value="<?= $rt['id'] ?>"><?= e(current_lang() === 'ar' ? $rt['name_ar'] : $rt['name_en']) ?></option><?php endforeach; ?></select><div class="invalid-feedback" data-field="relationship_type_id"></div></div>
                <div class="col-md-4"><label class="form-label">First Name (EN) *</label><input type="text" name="first_name_en" class="form-control" required><div class="invalid-feedback" data-field="first_name_en"></div></div>
                <div class="col-md-4"><label class="form-label">Last Name (EN) *</label><input type="text" name="last_name_en" class="form-control" required><div class="invalid-feedback" data-field="last_name_en"></div></div>
                <div class="col-md-4"><label class="form-label">Gender *</label><select name="gender" class="form-select" required><option value="male">Male</option><option value="female">Female</option></select></div>
                <div class="col-md-4"><label class="form-label">Date of Birth</label><input type="date" name="date_of_birth" class="form-control"></div>
                <div class="col-md-4"><label class="form-label">National ID</label><input type="text" name="national_id" class="form-control"></div>
                <div class="col-md-4"><label class="form-label">Phone</label><input type="text" name="phone" class="form-control"></div>
            </div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary" id="btn-save-dep">Save</button>
    </div>
</div></div></div>

<!-- Note Modal -->
<div class="modal fade" id="noteModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Add Note</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="note-form">
            <?= csrf_field() ?>
            <div class="mb-3"><label class="form-label">Type</label><select name="note_type" class="form-select"><option value="general">General</option><option value="financial">Financial</option><option value="disciplinary">Disciplinary</option><option value="medical">Medical</option><option value="internal">Internal</option></select></div>
            <div class="mb-3"><label class="form-label">Subject</label><input type="text" name="subject" class="form-control"></div>
            <div class="mb-3"><label class="form-label">Note *</label><textarea name="note_text" class="form-control" rows="4" required></textarea></div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary" id="btn-save-note">Save</button>
    </div>
</div></div></div>

<!-- Doc Upload Modal -->
<div class="modal fade" id="docUploadModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
    <div class="modal-header"><h5 class="modal-title">Upload Document</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
    <div class="modal-body">
        <form id="doc-form" enctype="multipart/form-data">
            <?= csrf_field() ?>
            <div class="mb-3"><label class="form-label">Document Name *</label><input type="text" name="document_name" class="form-control" required></div>
            <div class="mb-3"><label class="form-label">Type</label><select name="document_type" class="form-select"><option value="national_id">National ID</option><option value="passport">Passport</option><option value="photo">Photo</option><option value="birth_certificate">Birth Certificate</option><option value="other">Other</option></select></div>
            <div class="mb-3"><label class="form-label">Expiry Date</label><input type="date" name="expiry_date" class="form-control"></div>
            <div class="mb-3"><label class="form-label">File *</label><input type="file" name="document" class="form-control" required accept=".jpg,.jpeg,.png,.pdf,.doc,.docx"></div>
        </form>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary" id="btn-upload-doc">Upload</button>
    </div>
</div></div></div>

<?php $__view->push('scripts') ?>
<script>
const memberId = <?= $m['id'] ?>;
const csrfToken = '<?= csrf_token() ?>';

// Status change
document.getElementById('btn-save-status')?.addEventListener('click', function() {
    const form = document.getElementById('status-form');
    const data = new FormData(form);
    $.post('/api/members/' + memberId + '/status', Object.fromEntries(data), function(r) {
        if (r.success) { FormHelper.showToast('success', r.message); setTimeout(() => location.reload(), 1000); }
        else { FormHelper.showToast('error', r.message); }
    });
});

// Add dependent
document.getElementById('btn-save-dep')?.addEventListener('click', function() {
    const form = document.getElementById('dep-form');
    const depId = document.getElementById('dep-id').value;
    const url = depId ? '/api/members/' + memberId + '/dependents/' + depId + '/update' : '/api/members/' + memberId + '/dependents/store';
    $.post(url, $(form).serialize(), function(r) {
        if (r.success) { FormHelper.showToast('success', r.message); setTimeout(() => location.reload(), 1000); }
        else { FormHelper.showToast('error', r.message || 'Error'); if (r.errors) FormHelper.showErrors(form, r.errors); }
    });
});

// Edit dependent
$(document).on('click', '.btn-edit-dep', function() {
    const id = $(this).data('id');
    $.get('/api/members/' + memberId + '/dependents/' + id, function(r) {
        if (r.success) {
            const d = r.data;
            document.getElementById('dep-id').value = d.id;
            document.getElementById('depModalTitle').textContent = 'Edit Dependent';
            const form = document.getElementById('dep-form');
            form.querySelector('[name=relationship_type_id]').value = d.relationship_type_id;
            form.querySelector('[name=first_name_en]').value = d.first_name_en;
            form.querySelector('[name=last_name_en]').value = d.last_name_en;
            form.querySelector('[name=gender]').value = d.gender;
            form.querySelector('[name=date_of_birth]').value = d.date_of_birth || '';
            form.querySelector('[name=national_id]').value = d.national_id || '';
            form.querySelector('[name=phone]').value = d.phone || '';
            new bootstrap.Modal(document.getElementById('depModal')).show();
        }
    });
});

// Delete dependent
$(document).on('click', '.btn-delete-dep', function() {
    const id = $(this).data('id');
    Swal.fire({ title: 'Remove dependent?', icon: 'warning', showCancelButton: true, confirmButtonColor: '#e53e3e', confirmButtonText: 'Yes, remove' }).then((result) => {
        if (result.isConfirmed) {
            $.post('/api/members/' + memberId + '/dependents/' + id + '/delete', {_token: csrfToken}, function(r) {
                if (r.success) { FormHelper.showToast('success', r.message); setTimeout(() => location.reload(), 800); }
            });
        }
    });
});

// Add note
document.getElementById('btn-save-note')?.addEventListener('click', function() {
    const form = document.getElementById('note-form');
    $.post('/api/members/' + memberId + '/notes', $(form).serialize(), function(r) {
        if (r.success) { FormHelper.showToast('success', r.message); setTimeout(() => location.reload(), 800); }
        else { FormHelper.showToast('error', r.message); }
    });
});

// Add tag
document.getElementById('btn-add-tag')?.addEventListener('click', function() {
    const input = document.getElementById('new-tag-input');
    if (!input.value.trim()) return;
    $.post('/api/members/' + memberId + '/tags', {_token: csrfToken, tag_name: input.value.trim()}, function(r) {
        if (r.success) { FormHelper.showToast('success', 'Tag added'); setTimeout(() => location.reload(), 500); }
        else { FormHelper.showToast('error', r.message); }
    });
});

// Remove tag
$(document).on('click', '.btn-remove-tag', function() {
    const id = $(this).data('id');
    $.post('/api/members/' + memberId + '/tags/' + id + '/delete', {_token: csrfToken}, function(r) {
        if (r.success) { location.reload(); }
    });
});

// Upload document
document.getElementById('btn-upload-doc')?.addEventListener('click', function() {
    const form = document.getElementById('doc-form');
    const fd = new FormData(form);
    $.ajax({ url: '/api/members/' + memberId + '/documents/upload', type: 'POST', data: fd, processData: false, contentType: false,
        success: function(r) { if (r.success) { FormHelper.showToast('success', r.message); setTimeout(() => location.reload(), 800); } else { FormHelper.showToast('error', r.message); } }
    });
});

// Verify document
$(document).on('click', '.btn-verify-doc', function() {
    const id = $(this).data('id');
    $.post('/api/members/' + memberId + '/documents/' + id + '/verify', {_token: csrfToken}, function(r) {
        if (r.success) { FormHelper.showToast('success', 'Verified'); setTimeout(() => location.reload(), 500); }
    });
});

// Reset dep modal on open
document.getElementById('btn-add-dep')?.addEventListener('click', function() {
    document.getElementById('dep-id').value = '';
    document.getElementById('depModalTitle').textContent = 'Add Dependent';
    document.getElementById('dep-form').reset();
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\members\views\widgets\active_members.php

```php
<?php
$db = Database::getInstance();
$count = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0");
$lastMonth = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0 AND activation_date < DATE_SUB(CURDATE(), INTERVAL 1 MONTH)");
$trend = $lastMonth > 0 ? round((($count - $lastMonth) / $lastMonth) * 100, 1) : 0;
?>
<div class="card border-success h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between">
            <div>
                <h6 class="text-muted mb-1"><?= lang('members.total_active') ?></h6>
                <h3 class="mb-0"><?= number_format($count) ?></h3>
                <?php if ($trend != 0): ?>
                <small class="<?= $trend > 0 ? 'text-success' : 'text-danger' ?>"><?= $trend > 0 ? '+' : '' ?><?= $trend ?>%</small>
                <?php endif; ?>
            </div>
            <div class="align-self-center"><i class="fas fa-users fa-2x text-success opacity-50"></i></div>
        </div>
    </div>
</div>
```

## File: modules\members\views\widgets\distribution.php

```php
<?php
$db = Database::getInstance();
$dist = $db->fetchAll(
    "SELECT mt.name_en, mt.color_hex, COUNT(m.id) AS cnt
     FROM membership_types mt LEFT JOIN members m ON m.membership_type_id = mt.id AND m.status = 'active' AND m.is_deleted = 0
     WHERE mt.is_active = 1 GROUP BY mt.id ORDER BY cnt DESC"
);
$labels = array_column($dist, 'name_en');
$values = array_column($dist, 'cnt');
$colors = array_map(fn($d) => $d['color_hex'] ?: '#' . substr(md5($d['name_en']), 0, 6), $dist);
?>
<div class="card h-100">
    <div class="card-body">
        <h6 class="text-muted mb-3"><?= lang('members.distribution') ?></h6>
        <canvas id="chart-distribution" height="200"></canvas>
    </div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
    new Chart(document.getElementById('chart-distribution'), {
        type: 'doughnut',
        data: {
            labels: <?= json_encode($labels) ?>,
            datasets: [{ data: <?= json_encode(array_map('intval', $values)) ?>, backgroundColor: <?= json_encode($colors) ?> }]
        },
        options: { responsive: true, plugins: { legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 11 } } } } }
    });
});
</script>
```

## File: modules\members\views\widgets\new_members.php

```php
<?php
$db = Database::getInstance();
$newCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0 AND MONTH(activation_date) = MONTH(CURDATE()) AND YEAR(activation_date) = YEAR(CURDATE())");
$recent = $db->fetchAll("SELECT membership_number, full_name_en, activation_date FROM members WHERE status = 'active' AND is_deleted = 0 AND MONTH(activation_date) = MONTH(CURDATE()) AND YEAR(activation_date) = YEAR(CURDATE()) ORDER BY activation_date DESC LIMIT 5");
?>
<div class="card border-primary h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between mb-2">
            <h6 class="text-muted mb-0"><?= lang('members.new_this_month') ?></h6>
            <h3 class="mb-0 text-primary"><?= $newCount ?></h3>
        </div>
        <?php foreach ($recent as $r): ?>
        <div class="small border-bottom py-1">
            <a href="/members" class="text-decoration-none"><?= e($r['membership_number']) ?></a> â€” <?= e(str_limit($r['full_name_en'], 25)) ?>
        </div>
        <?php endforeach; ?>
    </div>
</div>
```

## File: modules\reports\Controllers\CustomReportApiController.php

```php
<?php

class CustomReportApiController extends Controller
{
    private static array $allowedSources = [
        'vw_active_members'             => 'Active Members',
        'vw_pending_applications'       => 'Pending Applications',
        'vw_overdue_invoices'           => 'Overdue Invoices',
        'vw_member_balance_summary'     => 'Member Balances',
        'vw_member_family_overview'     => 'Family Overview',
        'vw_subscription_renewal_status'=> 'Subscription Status',
        'vw_active_freezes'             => 'Active Freezes',
        'vw_violation_summary'          => 'Violations',
        'vw_complaint_dashboard'        => 'Complaints',
        'vw_installment_overview'       => 'Installments',
        'vw_expiring_cards'             => 'Expiring Cards',
        'vw_expiring_documents'         => 'Expiring Documents',
        'vw_sport_class_summary'        => 'Sport Classes',
        'vw_waiting_list_status'        => 'Waiting List',
    ];

    public function sources(Request $request): void
    {
        $result = [];
        foreach (self::$allowedSources as $view => $label) {
            $result[] = ['value' => $view, 'label' => $label];
        }
        $this->json(['success' => true, 'data' => $result]);
    }

    public function columns(Request $request): void
    {
        $db = $this->db();
        $source = $request->post('source', '');

        if (!isset(self::$allowedSources[$source])) {
            $this->json(['success' => false, 'message' => 'Invalid source'], 400);
            return;
        }

        try {
            $cols = $db->fetchAll("DESCRIBE `{$source}`");
            $result = [];
            foreach ($cols as $col) {
                $result[] = [
                    'name'    => $col['Field'],
                    'type'    => $col['Type'],
                    'null'    => $col['Null'],
                    'default' => $col['Default'],
                ];
            }
            $this->json(['success' => true, 'data' => $result]);
        } catch (\Exception $e) {
            $row = $db->fetchOne("SELECT * FROM `{$source}` LIMIT 1");
            if ($row) {
                $result = [];
                foreach (array_keys($row) as $colName) {
                    $result[] = ['name' => $colName, 'type' => 'varchar', 'null' => 'YES', 'default' => null];
                }
                $this->json(['success' => true, 'data' => $result]);
            } else {
                $this->json(['success' => true, 'data' => []]);
            }
        }
    }

    public function preview(Request $request): void
    {
        $db = $this->db();
        $source  = $request->post('source', '');
        $columns = $request->post('columns', []);
        $filters = $request->post('filters', []);
        $sortBy  = $request->post('sort_by', '');
        $sortDir = strtoupper($request->post('sort_dir', 'ASC')) === 'DESC' ? 'DESC' : 'ASC';

        if (!isset(self::$allowedSources[$source])) {
            $this->json(['success' => false, 'message' => 'Invalid source'], 400);
            return;
        }

        if (!is_array($columns) || empty($columns)) {
            $this->json(['success' => false, 'message' => 'Select at least one column'], 422);
            return;
        }

        $safeCols = [];
        foreach ($columns as $col) {
            $col = preg_replace('/[^a-zA-Z0-9_]/', '', $col);
            if ($col) $safeCols[] = "`{$col}`";
        }

        if (empty($safeCols)) {
            $this->json(['success' => false, 'message' => 'No valid columns'], 422);
            return;
        }

        $sql = "SELECT " . implode(', ', $safeCols) . " FROM `{$source}` WHERE 1=1";
        $bindings = [];

        if (is_array($filters)) {
            foreach ($filters as $f) {
                $col = preg_replace('/[^a-zA-Z0-9_]/', '', $f['column'] ?? '');
                $op = $f['operator'] ?? '=';
                $val = $f['value'] ?? '';
                if (!$col || $val === '') continue;

                if (!in_array($op, ['=', '!=', '>', '<', '>=', '<=', 'LIKE'])) $op = '=';

                if ($op === 'LIKE') {
                    $sql .= " AND `{$col}` LIKE ?";
                    $bindings[] = "%{$val}%";
                } else {
                    $sql .= " AND `{$col}` {$op} ?";
                    $bindings[] = $val;
                }
            }
        }

        if ($sortBy) {
            $sortBy = preg_replace('/[^a-zA-Z0-9_]/', '', $sortBy);
            if ($sortBy) {
                $sql .= " ORDER BY `{$sortBy}` {$sortDir}";
            }
        }

        $sql .= " LIMIT 100";

        $rows = $db->fetchAll($sql, $bindings);

        $this->json(['success' => true, 'data' => $rows, 'total' => count($rows), 'columns' => $columns]);
    }

    public function save(Request $request): void
    {
        $db = $this->db();

        $validator = new Validator($request->all(), [
            'report_name_en' => 'required|string|max:300',
            'report_name_ar' => 'nullable|string|max:300',
            'source'         => 'required|string',
            'columns'        => 'required',
            'description_en' => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $source = $data['source'];

        if (!isset(self::$allowedSources[$source])) {
            $this->json(['success' => false, 'message' => 'Invalid source'], 400);
            return;
        }

        $columns = $data['columns'];
        if (is_string($columns)) {
            $columns = json_decode($columns, true) ?: [];
        }

        $safeCols = [];
        $colDefs = [];
        foreach ($columns as $col) {
            $colName = preg_replace('/[^a-zA-Z0-9_]/', '', is_array($col) ? ($col['name'] ?? '') : $col);
            if ($colName) {
                $safeCols[] = "`{$colName}`";
                $colDefs[] = ['key' => $colName, 'label' => ucfirst(str_replace('_', ' ', $colName)), 'db' => $colName, 'searchable' => true];
            }
        }

        $queryTemplate = "SELECT " . implode(', ', $safeCols) . " FROM `{$source}` WHERE 1=1";

        $code = 'CUSTOM_' . strtoupper(substr(md5(time() . Auth::id()), 0, 8));

        $id = $db->insert('report_definitions', [
            'report_code'     => $code,
            'report_name_en'  => $data['report_name_en'],
            'report_name_ar'  => $data['report_name_ar'] ?? $data['report_name_en'],
            'category'        => 'custom',
            'description_en'  => $data['description_en'] ?? '',
            'description_ar'  => '',
            'query_template'  => $queryTemplate,
            'default_columns' => json_encode($colDefs),
            'available_filters' => json_encode([]),
            'default_sort'    => '',
            'print_layout'    => 'landscape',
            'is_schedulable'  => 0,
            'is_active'       => 1,
            'version'         => 1,
            'created_by'      => Auth::id(),
        ]);

        $this->audit('create', 'report_definitions', $id, "Created custom report: {$data['report_name_en']}");
        $this->json(['success' => true, 'message' => 'Custom report saved', 'id' => $id, 'redirect' => "/reports/{$id}"]);
    }
}
```

## File: modules\reports\Controllers\DashboardConfigApiController.php

```php
<?php

class DashboardConfigApiController extends Controller
{
    public function load(Request $request): void
    {
        $db = $this->db();

        $layout = $db->fetchOne(
            "SELECT * FROM dashboard_user_layouts WHERE user_id = ? AND is_default = 1",
            [Auth::id()]
        );

        $allWidgets = $db->fetchAll("SELECT * FROM dashboard_widgets WHERE is_active = 1 ORDER BY sort_order");

        $this->json([
            'success'    => true,
            'layout'     => $layout,
            'widgets'    => $allWidgets,
        ]);
    }

    public function save(Request $request): void
    {
        $db = $this->db();
        $widgets = $request->post('widgets', '[]');

        if (is_string($widgets)) {
            $decoded = json_decode($widgets, true);
            if ($decoded === null) {
                $this->json(['success' => false, 'message' => 'Invalid JSON'], 422);
                return;
            }
            $widgets = $decoded;
        }

        $existing = $db->fetchOne(
            "SELECT id FROM dashboard_user_layouts WHERE user_id = ? AND is_default = 1",
            [Auth::id()]
        );

        if ($existing) {
            $db->update('dashboard_user_layouts', [
                'widgets' => json_encode($widgets, JSON_UNESCAPED_UNICODE),
            ], 'id = ?', [$existing['id']]);
        } else {
            $db->insert('dashboard_user_layouts', [
                'user_id'     => Auth::id(),
                'layout_name' => 'default',
                'widgets'     => json_encode($widgets, JSON_UNESCAPED_UNICODE),
                'is_default'  => 1,
            ]);
        }

        $this->json(['success' => true, 'message' => lang('reports.dashboard_saved')]);
    }
}
```

## File: modules\reports\Controllers\ReportApiController.php

```php
<?php

class ReportApiController extends Controller
{
    public function initReports(Request $request): void
    {
        ReportSeeder::seed($this->db());
        $this->json(['success' => true, 'message' => 'Reports initialized']);
    }

    public function execute(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $report = $db->fetchOne("SELECT * FROM report_definitions WHERE id = ? AND is_active = 1", [$id]);
        if (!$report) {
            $this->json(['success' => false, 'message' => 'Report not found'], 404);
            return;
        }

        $params     = $request->post('params', []);
        $draw       = (int) $request->post('draw', 1);
        $start      = (int) $request->post('start', 0);
        $length     = (int) $request->post('length', 25);
        $searchVal  = $request->post('search', [])['value'] ?? '';
        $orderCol   = (int) ($request->post('order', [])['0']['column'] ?? 0);
        $orderDir   = strtoupper($request->post('order', [])['0']['dir'] ?? 'ASC') === 'DESC' ? 'DESC' : 'ASC';

        if (!is_array($params)) {
            $params = [];
        }

        $result = ReportEngine::execute($db, $report, $params, $start, $length, $searchVal, $orderCol, $orderDir);

        $startTime = microtime(true);

        $db->insert('report_generated', [
            'report_id'       => $id,
            'generated_by'    => Auth::id(),
            'parameters_used' => json_encode($params, JSON_UNESCAPED_UNICODE),
            'columns_used'    => $report['default_columns'],
            'output_format'   => 'screen',
            'rows_count'      => $result['recordsFiltered'],
            'execution_time_ms' => (int) ((microtime(true) - $startTime) * 1000),
        ]);

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $result['recordsTotal'],
            'recordsFiltered' => $result['recordsFiltered'],
            'data'            => $result['data'],
        ]);
    }

    public function export(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $report = $db->fetchOne("SELECT * FROM report_definitions WHERE id = ? AND is_active = 1", [$id]);
        if (!$report) {
            $this->json(['success' => false, 'message' => 'Report not found'], 404);
            return;
        }

        $params = $request->post('params', []);
        $format = $request->post('format', 'csv');
        if (!is_array($params)) $params = [];

        $result = ReportEngine::execute($db, $report, $params, 0, 100000, '', 0, 'ASC');

        $columns = json_decode($report['default_columns'], true) ?: [];
        $headers = array_map(function ($c) {
            return $c['label'] ?? $c['key'] ?? '';
        }, $columns);

        $rows = [];
        foreach ($result['data'] as $row) {
            $r = [];
            foreach ($columns as $col) {
                $key = $col['key'] ?? '';
                $r[] = $row[$key] ?? '';
            }
            $rows[] = $r;
        }

        $db->insert('report_generated', [
            'report_id'       => $id,
            'generated_by'    => Auth::id(),
            'parameters_used' => json_encode($params, JSON_UNESCAPED_UNICODE),
            'columns_used'    => $report['default_columns'],
            'output_format'   => $format,
            'rows_count'      => count($rows),
            'execution_time_ms' => 0,
        ]);

        $this->audit('export', 'report_definitions', $id, "Exported report: {$report['report_code']} as {$format}");

        $filename = $report['report_code'] . '_' . date('Y-m-d_His');

        if ($format === 'excel') {
            Export::excel($filename . '.xls', $headers, $rows);
        } else {
            Export::csv($filename . '.csv', $headers, $rows);
        }
    }

    public function kpiData(Request $request): void
    {
        $db = $this->db();

        try {
            $kpi = $db->fetchOne("SELECT * FROM vw_kpi_summary LIMIT 1");
        } catch (\Exception $e) {
            $kpi = [
                'total_active_members'   => (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status='active' AND is_deleted=0"),
                'new_members_this_month' => (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status='active' AND is_deleted=0 AND MONTH(activation_date)=MONTH(CURDATE()) AND YEAR(activation_date)=YEAR(CURDATE())"),
                'terminated_this_month'  => 0,
                'pending_applications'   => (int) $db->fetchColumn("SELECT COUNT(*) FROM applications WHERE status NOT IN ('approved','rejected','cancelled','expired') AND is_deleted=0"),
                'revenue_this_month'     => (float) $db->fetchColumn("SELECT COALESCE(SUM(amount),0) FROM payments WHERE status='completed' AND MONTH(payment_date)=MONTH(CURDATE()) AND YEAR(payment_date)=YEAR(CURDATE()) AND is_deleted=0"),
                'total_outstanding'      => (float) $db->fetchColumn("SELECT COALESCE(SUM(balance_due),0) FROM invoices WHERE status IN ('issued','partially_paid','overdue') AND is_deleted=0"),
                'total_overdue'          => (float) $db->fetchColumn("SELECT COALESCE(SUM(balance_due),0) FROM invoices WHERE status IN ('issued','partially_paid','overdue') AND due_date < CURDATE() AND is_deleted=0"),
                'active_freezes'         => (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status='active'"),
                'open_violations'        => (int) $db->fetchColumn("SELECT COUNT(*) FROM violations WHERE status NOT IN ('closed','dismissed')"),
                'open_complaints'        => (int) $db->fetchColumn("SELECT COUNT(*) FROM complaints WHERE status NOT IN ('resolved','closed')"),
                'overdue_installments'   => (int) $db->fetchColumn("SELECT COUNT(*) FROM installment_plan_schedules WHERE status='overdue'"),
                'cards_expiring_30_days' => (int) $db->fetchColumn("SELECT COUNT(*) FROM member_cards WHERE status='active' AND expiry_date IS NOT NULL AND expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)"),
            ];
        }

        $this->json(['success' => true, 'data' => $kpi]);
    }

    public function revenueChart(Request $request): void
    {
        $db = $this->db();
        $days = (int) $request->get('days', 30);
        if ($days < 7) $days = 7;
        if ($days > 365) $days = 365;

        $rows = $db->fetchAll(
            "SELECT DATE(payment_date) AS day, COALESCE(SUM(amount), 0) AS total
             FROM payments
             WHERE status = 'completed' AND is_deleted = 0
               AND payment_date >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
             GROUP BY DATE(payment_date)
             ORDER BY day",
            [$days]
        );

        $labels = [];
        $values = [];
        foreach ($rows as $r) {
            $labels[] = $r['day'];
            $values[] = (float) $r['total'];
        }

        $this->json(['success' => true, 'labels' => $labels, 'values' => $values]);
    }

    public function memberGrowth(Request $request): void
    {
        $db = $this->db();

        $rows = $db->fetchAll(
            "SELECT DATE_FORMAT(activation_date, '%Y-%m') AS month, COUNT(*) AS count
             FROM members
             WHERE status = 'active' AND is_deleted = 0
               AND activation_date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
             GROUP BY DATE_FORMAT(activation_date, '%Y-%m')
             ORDER BY month"
        );

        $labels = [];
        $values = [];
        foreach ($rows as $r) {
            $labels[] = $r['month'];
            $values[] = (int) $r['count'];
        }

        $this->json(['success' => true, 'labels' => $labels, 'values' => $values]);
    }

    public function saveFavorite(Request $request): void
    {
        $db = $this->db();

        $validator = new Validator($request->all(), [
            'report_id'     => 'required|integer',
            'favorite_name' => 'required|string|max:200',
            'saved_parameters' => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $reportId = (int) $data['report_id'];
        $savedParams = $data['saved_parameters'] ?? null;

        $id = $db->insert('report_favorites', [
            'user_id'          => Auth::id(),
            'report_id'        => $reportId,
            'favorite_name'    => $data['favorite_name'],
            'saved_parameters' => $savedParams ? $savedParams : null,
            'is_default'       => 0,
        ]);

        $this->json(['success' => true, 'message' => 'Favorite saved', 'id' => $id]);
    }

    public function deleteFavorite(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->post('id', 0);

        $fav = $db->fetchOne("SELECT * FROM report_favorites WHERE id = ? AND user_id = ?", [$id, Auth::id()]);
        if (!$fav) {
            $this->json(['success' => false, 'message' => 'Favorite not found'], 404);
            return;
        }

        $db->delete('report_favorites', 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => 'Favorite removed']);
    }

    public function listFavorites(Request $request): void
    {
        $db = $this->db();
        $reportId = (int) $request->get('report_id', 0);

        $favs = $db->fetchAll(
            "SELECT * FROM report_favorites WHERE user_id = ? AND report_id = ? ORDER BY favorite_name",
            [Auth::id(), $reportId]
        );

        $this->json(['success' => true, 'data' => $favs]);
    }
}
```

## File: modules\reports\Controllers\ReportController.php

```php
<?php

class ReportController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();

        $this->ensureReportsSeeded($db);

        $reports = $db->fetchAll(
            "SELECT r.*, 
                    (SELECT COUNT(*) FROM report_favorites rf WHERE rf.report_id = r.id AND rf.user_id = ?) AS is_favorite
             FROM report_definitions r 
             WHERE r.is_active = 1 
             ORDER BY r.category, r.report_name_en",
            [Auth::id()]
        );

        $categories = [];
        foreach ($reports as $r) {
            $cat = $r['category'] ?: 'other';
            $categories[$cat][] = $r;
        }

        $favorites = $db->fetchAll(
            "SELECT rf.*, rd.report_code, rd.report_name_en, rd.report_name_ar, rd.category, rd.description_en, rd.description_ar
             FROM report_favorites rf
             JOIN report_definitions rd ON rf.report_id = rd.id
             WHERE rf.user_id = ? AND rd.is_active = 1
             ORDER BY rf.created_at DESC",
            [Auth::id()]
        );

        $this->view('index', [
            'title'       => lang('reports.browser_title'),
            'breadcrumbs' => [['label' => lang('reports.reports')]],
            'categories'  => $categories,
            'favorites'   => $favorites,
        ]);
    }

    public function view(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $report = $db->fetchOne("SELECT * FROM report_definitions WHERE id = ? AND is_active = 1", [$id]);
        if (!$report) {
            $this->abort(404, 'Report not found');
        }

        $parameters = $db->fetchAll(
            "SELECT * FROM report_parameters WHERE report_id = ? ORDER BY sort_order, id",
            [$id]
        );

        $favorites = $db->fetchAll(
            "SELECT * FROM report_favorites WHERE report_id = ? AND user_id = ? ORDER BY favorite_name",
            [$id, Auth::id()]
        );

        $recentRuns = $db->fetchAll(
            "SELECT * FROM report_generated WHERE report_id = ? AND generated_by = ? ORDER BY generated_at DESC LIMIT 10",
            [$id, Auth::id()]
        );

        $this->view('view', [
            'title'       => ($report['report_name_' . Lang::current()] ?: $report['report_name_en']),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => ($report['report_name_' . Lang::current()] ?: $report['report_name_en'])],
            ],
            'report'     => $report,
            'parameters' => $parameters,
            'favorites'  => $favorites,
            'recentRuns' => $recentRuns,
        ]);
    }

    public function kpis(Request $request): void
    {
        $this->view('kpis', [
            'title'       => lang('reports.kpi_dashboard'),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => lang('reports.kpi_dashboard')],
            ],
        ]);
    }

    public function custom(Request $request): void
    {
        $this->view('custom', [
            'title'       => lang('reports.custom_builder'),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => lang('reports.custom_builder')],
            ],
        ]);
    }

    public function dashboardConfig(Request $request): void
    {
        $db = $this->db();

        $allWidgets = $db->fetchAll("SELECT * FROM dashboard_widgets WHERE is_active = 1 ORDER BY sort_order");

        $userLayout = $db->fetchOne(
            "SELECT * FROM dashboard_user_layouts WHERE user_id = ? AND is_default = 1",
            [Auth::id()]
        );

        $this->view('dashboard_config', [
            'title'       => lang('reports.dashboard_config'),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => lang('reports.dashboard_config')],
            ],
            'allWidgets'  => $allWidgets,
            'userLayout'  => $userLayout,
        ]);
    }

    private function ensureReportsSeeded(Database $db): void
    {
        $count = (int) $db->fetchColumn("SELECT COUNT(*) FROM report_definitions");
        if ($count === 0) {
            ReportSeeder::seed($db);
        }
    }
}
```

## File: modules\reports\Controllers\ReportEngine.php

```php
<?php

class ReportEngine
{
    public static function execute(
        Database $db,
        array $report,
        array $params,
        int $start,
        int $length,
        string $search,
        int $orderCol,
        string $orderDir
    ): array {
        $baseSql = $report['query_template'];
        if (empty($baseSql)) {
            return ['data' => [], 'recordsTotal' => 0, 'recordsFiltered' => 0];
        }

        $bindings = [];
        $filters = json_decode($report['available_filters'] ?? '[]', true) ?: [];
        $columns = json_decode($report['default_columns'] ?? '[]', true) ?: [];

        $whereClauses = [];
        foreach ($filters as $filter) {
            $name = $filter['name'] ?? '';
            $column = $filter['column'] ?? '';
            $operator = strtoupper($filter['operator'] ?? '=');

            if (!isset($params[$name]) || $params[$name] === '' || $params[$name] === null) {
                continue;
            }

            $val = $params[$name];

            if ($operator === 'LIKE') {
                $searchCols = explode(',', $column);
                $likeParts = [];
                foreach ($searchCols as $sc) {
                    $sc = trim($sc);
                    $likeParts[] = "{$sc} LIKE ?";
                    $bindings[] = "%{$val}%";
                }
                $whereClauses[] = '(' . implode(' OR ', $likeParts) . ')';
            } elseif ($operator === 'IN' && is_array($val)) {
                $placeholders = implode(',', array_fill(0, count($val), '?'));
                $whereClauses[] = "{$column} IN ({$placeholders})";
                $bindings = array_merge($bindings, $val);
            } elseif (in_array($operator, ['=', '!=', '>', '<', '>=', '<='])) {
                $whereClauses[] = "{$column} {$operator} ?";
                $bindings[] = $val;
            }
        }

        if (!empty($search)) {
            $searchParts = [];
            foreach ($columns as $col) {
                if (($col['searchable'] ?? true)) {
                    $key = $col['db'] ?? $col['key'] ?? '';
                    if ($key) {
                        $searchParts[] = "{$key} LIKE ?";
                        $bindings[] = "%{$search}%";
                    }
                }
            }
            if (!empty($searchParts)) {
                $whereClauses[] = '(' . implode(' OR ', $searchParts) . ')';
            }
        }

        $whereStr = '';
        if (!empty($whereClauses)) {
            if (stripos($baseSql, 'WHERE') !== false) {
                $whereStr = ' AND ' . implode(' AND ', $whereClauses);
            } else {
                $whereStr = ' WHERE ' . implode(' AND ', $whereClauses);
            }
        }

        $fullSql = $baseSql . $whereStr;

        $countSql = "SELECT COUNT(*) FROM ({$fullSql}) AS __cnt";
        $totalFiltered = (int) $db->fetchColumn($countSql, $bindings);

        $baseSqlNoFilter = $baseSql;
        $totalAll = (int) $db->fetchColumn("SELECT COUNT(*) FROM ({$baseSqlNoFilter}) AS __cnt_all", []);

        $orderClause = '';
        if (!empty($columns) && isset($columns[$orderCol])) {
            $orderKey = $columns[$orderCol]['db'] ?? $columns[$orderCol]['key'] ?? '';
            if ($orderKey) {
                $orderKey = preg_replace('/[^a-zA-Z0-9_.]/', '', $orderKey);
                $orderClause = " ORDER BY {$orderKey} {$orderDir}";
            }
        }
        if (empty($orderClause) && !empty($report['default_sort'])) {
            $orderClause = " ORDER BY " . $report['default_sort'];
        }

        $dataSql = $fullSql . $orderClause . " LIMIT " . (int)$length . " OFFSET " . (int)$start;
        $rows = $db->fetchAll($dataSql, $bindings);

        return [
            'data'            => $rows,
            'recordsTotal'    => $totalAll,
            'recordsFiltered' => $totalFiltered,
        ];
    }
}
```

## File: modules\reports\Controllers\ReportSeeder.php

```php
<?php

class ReportSeeder
{
    public static function seed(Database $db): void
    {
        $existing = (int) $db->fetchColumn("SELECT COUNT(*) FROM report_definitions");
        if ($existing > 0) return;

        $reports = self::getReportDefinitions();

        foreach ($reports as $rpt) {
            $params = $rpt['_params'] ?? [];
            unset($rpt['_params']);

            $reportId = $db->insert('report_definitions', $rpt);

            foreach ($params as $i => $p) {
                $p['report_id'] = $reportId;
                $p['sort_order'] = $i + 1;
                $db->insert('report_parameters', $p);
            }
        }
    }

    private static function getReportDefinitions(): array
    {
        return [
            // â”€â”€ 1. Members Report â”€â”€
            [
                'report_code'    => 'RPT_MEMBERS',
                'report_name_en' => 'Members Report',
                'report_name_ar' => 'ØªÙ‚Ø±ÙŠØ± Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡',
                'category'       => 'membership',
                'description_en' => 'Comprehensive member listing with flexible filters',
                'description_ar' => 'Ù‚Ø§Ø¦Ù…Ø© Ø´Ø§Ù…Ù„Ø© Ø¨Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ Ù…Ø¹ ÙÙ„Ø§ØªØ± Ù…Ø±Ù†Ø©',
                'query_template' => "SELECT m.id, m.membership_number, m.full_name_en, m.full_name_ar, m.national_id, m.gender, m.primary_phone, m.primary_email, m.status, m.join_date, m.activation_date, m.outstanding_balance, mt.name_en AS membership_type_en, mt.name_ar AS membership_type_ar, n.name_en AS nationality_en FROM members m LEFT JOIN membership_types mt ON m.membership_type_id = mt.id LEFT JOIN sys_nationalities n ON m.nationality_id = n.id WHERE m.is_deleted = 0",
                'default_columns' => json_encode([
                    ['key' => 'membership_number', 'label' => 'Membership #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Name (EN)', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'full_name_ar', 'label' => 'Name (AR)', 'db' => 'm.full_name_ar', 'searchable' => true],
                    ['key' => 'national_id', 'label' => 'National ID', 'db' => 'm.national_id', 'searchable' => true],
                    ['key' => 'primary_phone', 'label' => 'Phone', 'db' => 'm.primary_phone', 'searchable' => true],
                    ['key' => 'membership_type_en', 'label' => 'Type', 'db' => 'mt.name_en', 'searchable' => true],
                    ['key' => 'status', 'label' => 'Status', 'db' => 'm.status', 'searchable' => true],
                    ['key' => 'join_date', 'label' => 'Join Date', 'db' => 'm.join_date', 'searchable' => false],
                    ['key' => 'outstanding_balance', 'label' => 'Balance', 'db' => 'm.outstanding_balance', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'status', 'column' => 'm.status', 'operator' => '='],
                    ['name' => 'membership_type_id', 'column' => 'm.membership_type_id', 'operator' => '='],
                    ['name' => 'gender', 'column' => 'm.gender', 'operator' => '='],
                    ['name' => 'date_from', 'column' => 'm.join_date', 'operator' => '>='],
                    ['name' => 'date_to', 'column' => 'm.join_date', 'operator' => '<='],
                    ['name' => 'search_text', 'column' => 'm.full_name_en,m.full_name_ar,m.membership_number,m.national_id', 'operator' => 'LIKE'],
                ]),
                'default_sort'    => 'm.membership_number ASC',
                'chart_type'      => null,
                'print_layout'    => 'landscape',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'status', 'parameter_label_en' => 'Status', 'parameter_label_ar' => 'Ø§Ù„Ø­Ø§Ù„Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'active', 'label' => 'Active'],['value' => 'suspended', 'label' => 'Suspended'],['value' => 'frozen', 'label' => 'Frozen'],['value' => 'terminated', 'label' => 'Terminated']]), 'is_required' => 0],
                    ['parameter_name' => 'membership_type_id', 'parameter_label_en' => 'Membership Type', 'parameter_label_ar' => 'Ù†ÙˆØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©', 'parameter_type' => 'select', 'options' => json_encode(['source' => 'membership_types']), 'is_required' => 0],
                    ['parameter_name' => 'gender', 'parameter_label_en' => 'Gender', 'parameter_label_ar' => 'Ø§Ù„Ø¬Ù†Ø³', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'male', 'label' => 'Male'],['value' => 'female', 'label' => 'Female']]), 'is_required' => 0],
                    ['parameter_name' => 'date_from', 'parameter_label_en' => 'Join Date From', 'parameter_label_ar' => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ù†Ø¶Ù…Ø§Ù… Ù…Ù†', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'date_to', 'parameter_label_en' => 'Join Date To', 'parameter_label_ar' => 'ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ù†Ø¶Ù…Ø§Ù… Ø¥Ù„Ù‰', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'search_text', 'parameter_label_en' => 'Search', 'parameter_label_ar' => 'Ø¨Ø­Ø«', 'parameter_type' => 'text', 'is_required' => 0],
                ],
            ],

            // â”€â”€ 2. Financial Summary â”€â”€
            [
                'report_code'    => 'RPT_FINANCIAL',
                'report_name_en' => 'Financial Summary',
                'report_name_ar' => 'Ø§Ù„Ù…Ù„Ø®Øµ Ø§Ù„Ù…Ø§Ù„ÙŠ',
                'category'       => 'financial',
                'description_en' => 'Revenue summary by payment method and fee category',
                'description_ar' => 'Ù…Ù„Ø®Øµ Ø§Ù„Ø¥ÙŠØ±Ø§Ø¯Ø§Øª Ø­Ø³Ø¨ Ø·Ø±ÙŠÙ‚Ø© Ø§Ù„Ø¯ÙØ¹ ÙˆÙØ¦Ø© Ø§Ù„Ø±Ø³ÙˆÙ…',
                'query_template' => "SELECT p.payment_date, pm.name_en AS payment_method, p.amount, p.status, m.membership_number, m.full_name_en FROM payments p LEFT JOIN payment_methods pm ON p.payment_method_id = pm.id LEFT JOIN members m ON p.member_id = m.id WHERE p.status = 'completed' AND p.is_deleted = 0",
                'default_columns' => json_encode([
                    ['key' => 'payment_date', 'label' => 'Date', 'db' => 'p.payment_date', 'searchable' => false],
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Member', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'payment_method', 'label' => 'Method', 'db' => 'pm.name_en', 'searchable' => true],
                    ['key' => 'amount', 'label' => 'Amount', 'db' => 'p.amount', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'date_from', 'column' => 'p.payment_date', 'operator' => '>='],
                    ['name' => 'date_to', 'column' => 'p.payment_date', 'operator' => '<='],
                    ['name' => 'payment_method_id', 'column' => 'p.payment_method_id', 'operator' => '='],
                ]),
                'default_sort'    => 'p.payment_date DESC',
                'chart_type'      => 'bar',
                'chart_config'    => json_encode(['label_column' => 'payment_date', 'value_column' => 'amount', 'group_by' => 'payment_method']),
                'print_layout'    => 'portrait',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'date_from', 'parameter_label_en' => 'Date From', 'parameter_label_ar' => 'Ù…Ù† ØªØ§Ø±ÙŠØ®', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'date_to', 'parameter_label_en' => 'Date To', 'parameter_label_ar' => 'Ø¥Ù„Ù‰ ØªØ§Ø±ÙŠØ®', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'payment_method_id', 'parameter_label_en' => 'Payment Method', 'parameter_label_ar' => 'Ø·Ø±ÙŠÙ‚Ø© Ø§Ù„Ø¯ÙØ¹', 'parameter_type' => 'select', 'options' => json_encode(['source' => 'payment_methods']), 'is_required' => 0],
                ],
            ],

            // â”€â”€ 3. Outstanding Balances â”€â”€
            [
                'report_code'    => 'RPT_OUTSTANDING',
                'report_name_en' => 'Outstanding Balances',
                'report_name_ar' => 'Ø§Ù„Ø£Ø±ØµØ¯Ø© Ø§Ù„Ù…Ø³ØªØ­Ù‚Ø©',
                'category'       => 'financial',
                'description_en' => 'Members with outstanding balances and aging analysis',
                'description_ar' => 'Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ Ø°ÙˆÙˆ Ø§Ù„Ø£Ø±ØµØ¯Ø© Ø§Ù„Ù…Ø³ØªØ­Ù‚Ø© ÙˆØªØ­Ù„ÙŠÙ„ Ø§Ù„ØªÙ‚Ø§Ø¯Ù…',
                'query_template' => "SELECT mb.member_id, m.membership_number, m.full_name_en, m.full_name_ar, m.primary_phone, mt.name_en AS membership_type, mb.total_invoiced, mb.total_paid, mb.outstanding_balance, mb.overdue_amount, mb.oldest_overdue_date, mb.last_payment_date FROM member_balances mb JOIN members m ON mb.member_id = m.id LEFT JOIN membership_types mt ON m.membership_type_id = mt.id WHERE m.is_deleted = 0 AND mb.outstanding_balance > 0",
                'default_columns' => json_encode([
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Name', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'primary_phone', 'label' => 'Phone', 'db' => 'm.primary_phone', 'searchable' => true],
                    ['key' => 'membership_type', 'label' => 'Type', 'db' => 'mt.name_en', 'searchable' => true],
                    ['key' => 'total_invoiced', 'label' => 'Invoiced', 'db' => 'mb.total_invoiced', 'searchable' => false],
                    ['key' => 'total_paid', 'label' => 'Paid', 'db' => 'mb.total_paid', 'searchable' => false],
                    ['key' => 'outstanding_balance', 'label' => 'Outstanding', 'db' => 'mb.outstanding_balance', 'searchable' => false],
                    ['key' => 'overdue_amount', 'label' => 'Overdue', 'db' => 'mb.overdue_amount', 'searchable' => false],
                    ['key' => 'oldest_overdue_date', 'label' => 'Oldest Overdue', 'db' => 'mb.oldest_overdue_date', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'min_balance', 'column' => 'mb.outstanding_balance', 'operator' => '>='],
                    ['name' => 'membership_type_id', 'column' => 'm.membership_type_id', 'operator' => '='],
                ]),
                'default_sort'    => 'mb.outstanding_balance DESC',
                'chart_type'      => null,
                'print_layout'    => 'landscape',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'min_balance', 'parameter_label_en' => 'Min Balance', 'parameter_label_ar' => 'Ø§Ù„Ø­Ø¯ Ø§Ù„Ø£Ø¯Ù†Ù‰', 'parameter_type' => 'number', 'is_required' => 0],
                    ['parameter_name' => 'membership_type_id', 'parameter_label_en' => 'Membership Type', 'parameter_label_ar' => 'Ù†ÙˆØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©', 'parameter_type' => 'select', 'options' => json_encode(['source' => 'membership_types']), 'is_required' => 0],
                ],
            ],

            // â”€â”€ 4. Subscription Status â”€â”€
            [
                'report_code'    => 'RPT_SUBSCRIPTIONS',
                'report_name_en' => 'Subscription Status',
                'report_name_ar' => 'Ø­Ø§Ù„Ø© Ø§Ù„Ø§Ø´ØªØ±Ø§ÙƒØ§Øª',
                'category'       => 'membership',
                'description_en' => 'Active, expired, and expiring subscriptions',
                'description_ar' => 'Ø§Ù„Ø§Ø´ØªØ±Ø§ÙƒØ§Øª Ø§Ù„Ù†Ø´Ø·Ø© ÙˆØ§Ù„Ù…Ù†ØªÙ‡ÙŠØ© ÙˆØ§Ù„Ù‚Ø±ÙŠØ¨Ø© Ø§Ù„Ø§Ù†ØªÙ‡Ø§Ø¡',
                'query_template' => "SELECT ms.id, m.membership_number, m.full_name_en, mt.name_en AS membership_type, sp.name_en AS period_name, ms.start_date, ms.end_date, ms.net_amount, ms.paid_amount, ms.balance_due, ms.status, DATEDIFF(ms.end_date, CURDATE()) AS days_until_expiry FROM member_subscriptions ms JOIN members m ON ms.member_id = m.id LEFT JOIN membership_types mt ON ms.membership_type_id = mt.id LEFT JOIN subscription_periods sp ON ms.subscription_period_id = sp.id WHERE m.is_deleted = 0",
                'default_columns' => json_encode([
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Name', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'membership_type', 'label' => 'Type', 'db' => 'mt.name_en', 'searchable' => true],
                    ['key' => 'start_date', 'label' => 'Start', 'db' => 'ms.start_date', 'searchable' => false],
                    ['key' => 'end_date', 'label' => 'End', 'db' => 'ms.end_date', 'searchable' => false],
                    ['key' => 'net_amount', 'label' => 'Amount', 'db' => 'ms.net_amount', 'searchable' => false],
                    ['key' => 'balance_due', 'label' => 'Due', 'db' => 'ms.balance_due', 'searchable' => false],
                    ['key' => 'status', 'label' => 'Status', 'db' => 'ms.status', 'searchable' => true],
                    ['key' => 'days_until_expiry', 'label' => 'Days Left', 'db' => 'days_until_expiry', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'sub_status', 'column' => 'ms.status', 'operator' => '='],
                    ['name' => 'membership_type_id', 'column' => 'ms.membership_type_id', 'operator' => '='],
                ]),
                'default_sort'    => 'ms.end_date ASC',
                'chart_type'      => 'doughnut',
                'chart_config'    => json_encode(['group_column' => 'status']),
                'print_layout'    => 'landscape',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'sub_status', 'parameter_label_en' => 'Status', 'parameter_label_ar' => 'Ø§Ù„Ø­Ø§Ù„Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'active', 'label' => 'Active'],['value' => 'expired', 'label' => 'Expired'],['value' => 'pending', 'label' => 'Pending'],['value' => 'cancelled', 'label' => 'Cancelled']]), 'is_required' => 0],
                    ['parameter_name' => 'membership_type_id', 'parameter_label_en' => 'Membership Type', 'parameter_label_ar' => 'Ù†ÙˆØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©', 'parameter_type' => 'select', 'options' => json_encode(['source' => 'membership_types']), 'is_required' => 0],
                ],
            ],

            // â”€â”€ 5. Violations Report â”€â”€
            [
                'report_code'    => 'RPT_VIOLATIONS',
                'report_name_en' => 'Violations Report',
                'report_name_ar' => 'ØªÙ‚Ø±ÙŠØ± Ø§Ù„Ù…Ø®Ø§Ù„ÙØ§Øª',
                'category'       => 'operational',
                'description_en' => 'Violations by type, severity, and status',
                'description_ar' => 'Ø§Ù„Ù…Ø®Ø§Ù„ÙØ§Øª Ø­Ø³Ø¨ Ø§Ù„Ù†ÙˆØ¹ ÙˆØ§Ù„Ø´Ø¯Ø© ÙˆØ§Ù„Ø­Ø§Ù„Ø©',
                'query_template' => "SELECT v.id, v.violation_number, m.membership_number, m.full_name_en, vt.name_en AS violation_type, vc.name_en AS category, vt.severity, v.incident_date, v.status, v.occurrence_number FROM violations v JOIN members m ON v.member_id = m.id JOIN violation_types vt ON v.violation_type_id = vt.id JOIN violation_categories vc ON vt.category_id = vc.id WHERE 1=1",
                'default_columns' => json_encode([
                    ['key' => 'violation_number', 'label' => 'Violation #', 'db' => 'v.violation_number', 'searchable' => true],
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Member', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'violation_type', 'label' => 'Type', 'db' => 'vt.name_en', 'searchable' => true],
                    ['key' => 'category', 'label' => 'Category', 'db' => 'vc.name_en', 'searchable' => true],
                    ['key' => 'severity', 'label' => 'Severity', 'db' => 'vt.severity', 'searchable' => true],
                    ['key' => 'incident_date', 'label' => 'Date', 'db' => 'v.incident_date', 'searchable' => false],
                    ['key' => 'status', 'label' => 'Status', 'db' => 'v.status', 'searchable' => true],
                    ['key' => 'occurrence_number', 'label' => 'Occurrence', 'db' => 'v.occurrence_number', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'vio_status', 'column' => 'v.status', 'operator' => '='],
                    ['name' => 'severity', 'column' => 'vt.severity', 'operator' => '='],
                    ['name' => 'date_from', 'column' => 'v.incident_date', 'operator' => '>='],
                    ['name' => 'date_to', 'column' => 'v.incident_date', 'operator' => '<='],
                ]),
                'default_sort'    => 'v.incident_date DESC',
                'chart_type'      => 'bar',
                'chart_config'    => json_encode(['group_column' => 'severity']),
                'print_layout'    => 'landscape',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'vio_status', 'parameter_label_en' => 'Status', 'parameter_label_ar' => 'Ø§Ù„Ø­Ø§Ù„Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'reported', 'label' => 'Reported'],['value' => 'under_review', 'label' => 'Under Review'],['value' => 'confirmed', 'label' => 'Confirmed'],['value' => 'closed', 'label' => 'Closed'],['value' => 'dismissed', 'label' => 'Dismissed']]), 'is_required' => 0],
                    ['parameter_name' => 'severity', 'parameter_label_en' => 'Severity', 'parameter_label_ar' => 'Ø§Ù„Ø´Ø¯Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'minor', 'label' => 'Minor'],['value' => 'moderate', 'label' => 'Moderate'],['value' => 'major', 'label' => 'Major'],['value' => 'critical', 'label' => 'Critical']]), 'is_required' => 0],
                    ['parameter_name' => 'date_from', 'parameter_label_en' => 'Date From', 'parameter_label_ar' => 'Ù…Ù† ØªØ§Ø±ÙŠØ®', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'date_to', 'parameter_label_en' => 'Date To', 'parameter_label_ar' => 'Ø¥Ù„Ù‰ ØªØ§Ø±ÙŠØ®', 'parameter_type' => 'date', 'is_required' => 0],
                ],
            ],

            // â”€â”€ 6. Complaint Analysis â”€â”€
            [
                'report_code'    => 'RPT_COMPLAINTS',
                'report_name_en' => 'Complaint Analysis',
                'report_name_ar' => 'ØªØ­Ù„ÙŠÙ„ Ø§Ù„Ø´ÙƒØ§ÙˆÙ‰',
                'category'       => 'operational',
                'description_en' => 'Complaints by category, priority, and SLA compliance',
                'description_ar' => 'Ø§Ù„Ø´ÙƒØ§ÙˆÙ‰ Ø­Ø³Ø¨ Ø§Ù„ÙØ¦Ø© ÙˆØ§Ù„Ø£ÙˆÙ„ÙˆÙŠØ© ÙˆØ§Ù„Ø§Ù„ØªØ²Ø§Ù… Ø¨Ø§Ù„Ù…Ø³ØªÙˆÙ‰',
                'query_template' => "SELECT c.id, c.complaint_number, c.subject, c.status, cc.name_en AS category, cp.name_en AS priority, c.sla_deadline, c.sla_breached, m.membership_number, m.full_name_en, c.created_at FROM complaints c JOIN complaint_categories cc ON c.category_id = cc.id JOIN complaint_priorities cp ON c.priority_id = cp.id LEFT JOIN members m ON c.member_id = m.id WHERE 1=1",
                'default_columns' => json_encode([
                    ['key' => 'complaint_number', 'label' => 'Complaint #', 'db' => 'c.complaint_number', 'searchable' => true],
                    ['key' => 'subject', 'label' => 'Subject', 'db' => 'c.subject', 'searchable' => true],
                    ['key' => 'category', 'label' => 'Category', 'db' => 'cc.name_en', 'searchable' => true],
                    ['key' => 'priority', 'label' => 'Priority', 'db' => 'cp.name_en', 'searchable' => true],
                    ['key' => 'status', 'label' => 'Status', 'db' => 'c.status', 'searchable' => true],
                    ['key' => 'sla_breached', 'label' => 'SLA Breached', 'db' => 'c.sla_breached', 'searchable' => false],
                    ['key' => 'membership_number', 'label' => 'Member', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'created_at', 'label' => 'Created', 'db' => 'c.created_at', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'compl_status', 'column' => 'c.status', 'operator' => '='],
                    ['name' => 'category_id', 'column' => 'c.category_id', 'operator' => '='],
                    ['name' => 'sla_breached', 'column' => 'c.sla_breached', 'operator' => '='],
                ]),
                'default_sort'    => 'c.created_at DESC',
                'chart_type'      => 'pie',
                'chart_config'    => json_encode(['group_column' => 'status']),
                'print_layout'    => 'landscape',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'compl_status', 'parameter_label_en' => 'Status', 'parameter_label_ar' => 'Ø§Ù„Ø­Ø§Ù„Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'submitted', 'label' => 'Submitted'],['value' => 'in_progress', 'label' => 'In Progress'],['value' => 'resolved', 'label' => 'Resolved'],['value' => 'closed', 'label' => 'Closed']]), 'is_required' => 0],
                    ['parameter_name' => 'category_id', 'parameter_label_en' => 'Category', 'parameter_label_ar' => 'Ø§Ù„ÙØ¦Ø©', 'parameter_type' => 'select', 'options' => json_encode(['source' => 'complaint_categories']), 'is_required' => 0],
                    ['parameter_name' => 'sla_breached', 'parameter_label_en' => 'SLA Breached Only', 'parameter_label_ar' => 'Ø§Ù„Ù…ØªØ¬Ø§ÙˆØ²Ø© ÙÙ‚Ø·', 'parameter_type' => 'boolean', 'is_required' => 0],
                ],
            ],

            // â”€â”€ 7. Overdue Invoices â”€â”€
            [
                'report_code'    => 'RPT_OVERDUE',
                'report_name_en' => 'Overdue Invoices',
                'report_name_ar' => 'Ø§Ù„ÙÙˆØ§ØªÙŠØ± Ø§Ù„Ù…ØªØ£Ø®Ø±Ø©',
                'category'       => 'financial',
                'description_en' => 'Invoices past due date with aging analysis',
                'description_ar' => 'Ø§Ù„ÙÙˆØ§ØªÙŠØ± Ø§Ù„Ù…ØªØ¬Ø§ÙˆØ²Ø© Ù„ØªØ§Ø±ÙŠØ® Ø§Ù„Ø§Ø³ØªØ­Ù‚Ø§Ù‚',
                'query_template' => "SELECT i.id, i.invoice_number, i.invoice_type, i.invoice_date, i.due_date, i.total_amount, i.paid_amount, i.balance_due, DATEDIFF(CURDATE(), i.due_date) AS days_overdue, m.membership_number, m.full_name_en, m.primary_phone, mt.name_en AS membership_type FROM invoices i JOIN members m ON i.member_id = m.id LEFT JOIN membership_types mt ON m.membership_type_id = mt.id WHERE i.status IN ('issued','partially_paid','overdue') AND i.due_date < CURDATE() AND i.balance_due > 0 AND i.is_deleted = 0",
                'default_columns' => json_encode([
                    ['key' => 'invoice_number', 'label' => 'Invoice #', 'db' => 'i.invoice_number', 'searchable' => true],
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Name', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'invoice_type', 'label' => 'Type', 'db' => 'i.invoice_type', 'searchable' => true],
                    ['key' => 'due_date', 'label' => 'Due Date', 'db' => 'i.due_date', 'searchable' => false],
                    ['key' => 'total_amount', 'label' => 'Total', 'db' => 'i.total_amount', 'searchable' => false],
                    ['key' => 'balance_due', 'label' => 'Balance', 'db' => 'i.balance_due', 'searchable' => false],
                    ['key' => 'days_overdue', 'label' => 'Days Overdue', 'db' => 'days_overdue', 'searchable' => false],
                    ['key' => 'primary_phone', 'label' => 'Phone', 'db' => 'm.primary_phone', 'searchable' => true],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'invoice_type', 'column' => 'i.invoice_type', 'operator' => '='],
                    ['name' => 'min_overdue_days', 'column' => 'DATEDIFF(CURDATE(), i.due_date)', 'operator' => '>='],
                ]),
                'default_sort'    => 'days_overdue DESC',
                'chart_type'      => null,
                'print_layout'    => 'landscape',
                'is_schedulable'  => 1,
                'is_active'       => 1,
                'version'         => 1,
                '_params' => [
                    ['parameter_name' => 'invoice_type', 'parameter_label_en' => 'Invoice Type', 'parameter_label_ar' => 'Ù†ÙˆØ¹ Ø§Ù„ÙØ§ØªÙˆØ±Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'registration', 'label' => 'Registration'],['value' => 'subscription', 'label' => 'Subscription'],['value' => 'renewal', 'label' => 'Renewal'],['value' => 'penalty', 'label' => 'Penalty']]), 'is_required' => 0],
                    ['parameter_name' => 'min_overdue_days', 'parameter_label_en' => 'Min Overdue Days', 'parameter_label_ar' => 'Ø£Ù‚Ù„ Ø£ÙŠØ§Ù… ØªØ£Ø®ÙŠØ±', 'parameter_type' => 'number', 'is_required' => 0],
                ],
            ],

            // â”€â”€ 8. Collection Report â”€â”€
            [
                'report_code'    => 'RPT_COLLECTIONS',
                'report_name_en' => 'Collection Report',
                'report_name_ar' => 'ØªÙ‚Ø±ÙŠØ± Ø§Ù„ØªØ­ØµÙŠÙ„',
                'category'       => 'financial',
                'description_en' => 'Payments collected by cashier, method, and period',
                'description_ar' => 'Ø§Ù„Ù…Ø¯ÙÙˆØ¹Ø§Øª Ø§Ù„Ù…Ø­ØµÙ„Ø© Ø­Ø³Ø¨ Ø§Ù„Ù…ÙˆØ¸Ù ÙˆØ§Ù„Ø·Ø±ÙŠÙ‚Ø© ÙˆØ§Ù„ÙØªØ±Ø©',
                'query_template' => "SELECT p.payment_number, p.payment_date, p.amount, pm.name_en AS method, m.membership_number, m.full_name_en, u.full_name_en AS collected_by_name FROM payments p LEFT JOIN payment_methods pm ON p.payment_method_id = pm.id LEFT JOIN members m ON p.member_id = m.id LEFT JOIN auth_users u ON p.collected_by = u.id WHERE p.status = 'completed' AND p.is_deleted = 0",
                'default_columns' => json_encode([
                    ['key' => 'payment_number', 'label' => 'Payment #', 'db' => 'p.payment_number', 'searchable' => true],
                    ['key' => 'payment_date', 'label' => 'Date', 'db' => 'p.payment_date', 'searchable' => false],
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Member', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'method', 'label' => 'Method', 'db' => 'pm.name_en', 'searchable' => true],
                    ['key' => 'amount', 'label' => 'Amount', 'db' => 'p.amount', 'searchable' => false],
                    ['key' => 'collected_by_name', 'label' => 'Collected By', 'db' => 'u.full_name_en', 'searchable' => true],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'date_from', 'column' => 'p.payment_date', 'operator' => '>='],
                    ['name' => 'date_to', 'column' => 'p.payment_date', 'operator' => '<='],
                    ['name' => 'payment_method_id', 'column' => 'p.payment_method_id', 'operator' => '='],
                    ['name' => 'collected_by', 'column' => 'p.collected_by', 'operator' => '='],
                ]),
                'default_sort' => 'p.payment_date DESC',
                'chart_type'   => null,
                'print_layout' => 'landscape',
                'is_schedulable' => 1,
                'is_active' => 1,
                'version' => 1,
                '_params' => [
                    ['parameter_name' => 'date_from', 'parameter_label_en' => 'Date From', 'parameter_label_ar' => 'Ù…Ù† ØªØ§Ø±ÙŠØ®', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'date_to', 'parameter_label_en' => 'Date To', 'parameter_label_ar' => 'Ø¥Ù„Ù‰ ØªØ§Ø±ÙŠØ®', 'parameter_type' => 'date', 'is_required' => 0],
                    ['parameter_name' => 'payment_method_id', 'parameter_label_en' => 'Method', 'parameter_label_ar' => 'Ø·Ø±ÙŠÙ‚Ø© Ø§Ù„Ø¯ÙØ¹', 'parameter_type' => 'select', 'options' => json_encode(['source' => 'payment_methods']), 'is_required' => 0],
                ],
            ],

            // â”€â”€ 9. Expiring Cards â”€â”€
            [
                'report_code'    => 'RPT_EXPIRING_CARDS',
                'report_name_en' => 'Expiring Cards',
                'report_name_ar' => 'Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª Ø§Ù„Ù…Ù†ØªÙ‡ÙŠØ©',
                'category'       => 'operational',
                'description_en' => 'Cards expiring within a configurable time window',
                'description_ar' => 'Ø§Ù„Ø¨Ø·Ø§Ù‚Ø§Øª Ø§Ù„Ù…Ù†ØªÙ‡ÙŠØ© Ø®Ù„Ø§Ù„ ÙØªØ±Ø© Ù‚Ø§Ø¨Ù„Ø© Ù„Ù„ØªØ­Ø¯ÙŠØ¯',
                'query_template' => "SELECT mc.card_number, ct.name_en AS card_type, mc.expiry_date, DATEDIFF(mc.expiry_date, CURDATE()) AS days_until_expiry, m.membership_number, m.full_name_en, m.primary_phone FROM member_cards mc JOIN card_types ct ON mc.card_type_id = ct.id LEFT JOIN members m ON mc.member_id = m.id WHERE mc.status = 'active' AND mc.expiry_date IS NOT NULL AND mc.expiry_date >= CURDATE()",
                'default_columns' => json_encode([
                    ['key' => 'card_number', 'label' => 'Card #', 'db' => 'mc.card_number', 'searchable' => true],
                    ['key' => 'card_type', 'label' => 'Type', 'db' => 'ct.name_en', 'searchable' => true],
                    ['key' => 'membership_number', 'label' => 'Member #', 'db' => 'm.membership_number', 'searchable' => true],
                    ['key' => 'full_name_en', 'label' => 'Name', 'db' => 'm.full_name_en', 'searchable' => true],
                    ['key' => 'expiry_date', 'label' => 'Expiry', 'db' => 'mc.expiry_date', 'searchable' => false],
                    ['key' => 'days_until_expiry', 'label' => 'Days Left', 'db' => 'days_until_expiry', 'searchable' => false],
                    ['key' => 'primary_phone', 'label' => 'Phone', 'db' => 'm.primary_phone', 'searchable' => true],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'max_days', 'column' => 'DATEDIFF(mc.expiry_date, CURDATE())', 'operator' => '<='],
                ]),
                'default_sort' => 'mc.expiry_date ASC',
                'chart_type' => null,
                'print_layout' => 'portrait',
                'is_schedulable' => 1,
                'is_active' => 1,
                'version' => 1,
                '_params' => [
                    ['parameter_name' => 'max_days', 'parameter_label_en' => 'Expiring Within (days)', 'parameter_label_ar' => 'ØªÙ†ØªÙ‡ÙŠ Ø®Ù„Ø§Ù„ (Ø£ÙŠØ§Ù…)', 'parameter_type' => 'number', 'default_value' => '60', 'is_required' => 0],
                ],
            ],

            // â”€â”€ 10. Membership Distribution â”€â”€
            [
                'report_code'    => 'RPT_DISTRIBUTION',
                'report_name_en' => 'Membership Distribution',
                'report_name_ar' => 'ØªÙˆØ²ÙŠØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ§Øª',
                'category'       => 'membership',
                'description_en' => 'Members per membership type with counts',
                'description_ar' => 'ØªÙˆØ²ÙŠØ¹ Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ Ø­Ø³Ø¨ Ø£Ù†ÙˆØ§Ø¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
                'query_template' => "SELECT mt.name_en AS membership_type, mt.name_ar AS membership_type_ar, m.status, COUNT(*) AS member_count FROM members m JOIN membership_types mt ON m.membership_type_id = mt.id WHERE m.is_deleted = 0",
                'default_columns' => json_encode([
                    ['key' => 'membership_type', 'label' => 'Type', 'db' => 'mt.name_en', 'searchable' => true],
                    ['key' => 'status', 'label' => 'Status', 'db' => 'm.status', 'searchable' => true],
                    ['key' => 'member_count', 'label' => 'Count', 'db' => 'member_count', 'searchable' => false],
                ]),
                'available_filters' => json_encode([
                    ['name' => 'status', 'column' => 'm.status', 'operator' => '='],
                ]),
                'available_grouping' => json_encode(['mt.name_en', 'm.status']),
                'default_sort' => 'member_count DESC',
                'chart_type' => 'doughnut',
                'chart_config' => json_encode(['label_column' => 'membership_type', 'value_column' => 'member_count']),
                'print_layout' => 'portrait',
                'is_schedulable' => 1,
                'is_active' => 1,
                'version' => 1,
                '_params' => [
                    ['parameter_name' => 'status', 'parameter_label_en' => 'Status', 'parameter_label_ar' => 'Ø§Ù„Ø­Ø§Ù„Ø©', 'parameter_type' => 'select', 'options' => json_encode([['value' => '', 'label' => 'All'],['value' => 'active', 'label' => 'Active'],['value' => 'suspended', 'label' => 'Suspended']]), 'is_required' => 0],
                ],
            ],
        ];
    }
}
```

## File: modules\reports\Controllers\ScheduleApiController.php

```php
<?php

class ScheduleApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db = $this->db();

        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search', [])['value'] ?? '';

        $baseSql = "SELECT rs.*, rd.report_name_en, rd.report_name_ar, rd.report_code
                    FROM report_schedules rs
                    JOIN report_definitions rd ON rs.report_id = rd.id";

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM report_schedules");

        $where = [];
        $params = [];
        if ($search) {
            $where[] = "(rs.schedule_name LIKE ? OR rd.report_name_en LIKE ? OR rd.report_code LIKE ?)";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
        }

        $whereStr = !empty($where) ? ' WHERE ' . implode(' AND ', $where) : '';
        $filtered = (int) $db->fetchColumn("SELECT COUNT(*) FROM report_schedules rs JOIN report_definitions rd ON rs.report_id = rd.id" . $whereStr, $params);

        $orderCol = (int) ($request->get('order', [])['0']['column'] ?? 0);
        $orderDir = strtoupper($request->get('order', [])['0']['dir'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
        $orderMap = ['rs.id', 'rs.schedule_name', 'rd.report_name_en', 'rs.frequency', 'rs.is_active', 'rs.last_run_at', 'rs.next_run_at'];
        $orderField = $orderMap[$orderCol] ?? 'rs.id';

        $sql = $baseSql . $whereStr . " ORDER BY {$orderField} {$orderDir} LIMIT " . (int)$length . " OFFSET " . (int)$start;
        $rows = $db->fetchAll($sql, $params);

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $total,
            'recordsFiltered' => $filtered,
            'data'            => $rows,
        ]);
    }

    public function store(Request $request): void
    {
        $db = $this->db();

        $validator = new Validator($request->all(), [
            'report_id'       => 'required|integer',
            'schedule_name'   => 'required|string|max:200',
            'frequency'       => 'required|in:daily,weekly,monthly,quarterly,yearly',
            'day_of_week'     => 'nullable|integer',
            'day_of_month'    => 'nullable|integer',
            'time_of_day'     => 'required|string',
            'output_format'   => 'required|in:pdf,excel,csv,word',
            'delivery_method' => 'required|in:email,in_app,save,both_email_save',
            'recipients'      => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['created_by'] = Auth::id();
        $data['is_active'] = 1;
        $data['recipients'] = $data['recipients'] ? json_encode(array_map('trim', explode(',', $data['recipients']))) : null;

        $id = $db->insert('report_schedules', $data);
        $this->audit('create', 'report_schedules', $id, 'Created report schedule');
        $this->json(['success' => true, 'message' => lang('reports.schedule_created'), 'id' => $id]);
    }

    public function update(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $schedule = $db->fetchOne("SELECT * FROM report_schedules WHERE id = ?", [$id]);
        if (!$schedule) {
            $this->json(['success' => false, 'message' => 'Not found'], 404);
            return;
        }

        $validator = new Validator($request->all(), [
            'report_id'       => 'required|integer',
            'schedule_name'   => 'required|string|max:200',
            'frequency'       => 'required|in:daily,weekly,monthly,quarterly,yearly',
            'day_of_week'     => 'nullable|integer',
            'day_of_month'    => 'nullable|integer',
            'time_of_day'     => 'required|string',
            'output_format'   => 'required|in:pdf,excel,csv,word',
            'delivery_method' => 'required|in:email,in_app,save,both_email_save',
            'recipients'      => 'nullable|string',
        ]);

        if ($validator->fails()) {
            $this->json(['success' => false, 'errors' => $validator->errors()], 422);
            return;
        }

        $data = $validator->validated();
        $data['recipients'] = $data['recipients'] ? json_encode(array_map('trim', explode(',', $data['recipients']))) : null;

        $db->update('report_schedules', $data, 'id = ?', [$id]);
        $this->audit('update', 'report_schedules', $id, 'Updated report schedule');
        $this->json(['success' => true, 'message' => lang('reports.schedule_updated')]);
    }

    public function destroy(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $db->delete('report_schedule_runs', 'schedule_id = ?', [$id]);
        $db->delete('report_schedules', 'id = ?', [$id]);
        $this->audit('delete', 'report_schedules', $id, 'Deleted report schedule');
        $this->json(['success' => true, 'message' => lang('reports.schedule_deleted')]);
    }

    public function toggle(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $schedule = $db->fetchOne("SELECT is_active FROM report_schedules WHERE id = ?", [$id]);
        if (!$schedule) {
            $this->json(['success' => false, 'message' => 'Not found'], 404);
            return;
        }

        $newState = $schedule['is_active'] ? 0 : 1;
        $db->update('report_schedules', ['is_active' => $newState], 'id = ?', [$id]);
        $this->json(['success' => true, 'message' => $newState ? 'Enabled' : 'Disabled']);
    }

    public function runs(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $runs = $db->fetchAll(
            "SELECT * FROM report_schedule_runs WHERE schedule_id = ? ORDER BY run_at DESC LIMIT 50",
            [$id]
        );

        $this->json(['success' => true, 'data' => $runs]);
    }
}
```

## File: modules\reports\Controllers\ScheduleController.php

```php
<?php

class ScheduleController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('schedules/index', [
            'title' => lang('reports.scheduled_reports'),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => lang('reports.scheduled_reports')],
            ],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $reports = $db->fetchAll("SELECT id, report_code, report_name_en, report_name_ar FROM report_definitions WHERE is_active = 1 AND is_schedulable = 1 ORDER BY report_name_en");

        $this->view('schedules/create', [
            'title' => lang('reports.create_schedule'),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => lang('reports.scheduled_reports'), 'url' => '/reports/schedules'],
                ['label' => lang('common.create')],
            ],
            'reports' => $reports,
        ]);
    }

    public function edit(Request $request): void
    {
        $db = $this->db();
        $id = (int) $request->routeParam('id');

        $schedule = $db->fetchOne("SELECT * FROM report_schedules WHERE id = ?", [$id]);
        if (!$schedule) {
            $this->abort(404, 'Schedule not found');
        }

        $reports = $db->fetchAll("SELECT id, report_code, report_name_en, report_name_ar FROM report_definitions WHERE is_active = 1 AND is_schedulable = 1 ORDER BY report_name_en");

        $this->view('schedules/edit', [
            'title' => lang('reports.edit_schedule'),
            'breadcrumbs' => [
                ['label' => lang('reports.reports'), 'url' => '/reports'],
                ['label' => lang('reports.scheduled_reports'), 'url' => '/reports/schedules'],
                ['label' => lang('common.edit')],
            ],
            'schedule' => $schedule,
            'reports'  => $reports,
        ]);
    }
}
```

## File: modules\reports\css\reports.css

```css
/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   Reports Module Styles
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

.report-card-item {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    cursor: pointer;
}
.report-card-item:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.report-category-card .card-header {
    background: #f8fafc;
}

#kpi-cards .card {
    transition: transform 0.2s ease;
}
#kpi-cards .card:hover {
    transform: scale(1.02);
}

#preview-area table {
    font-size: 0.85rem;
}

.filter-row {
    animation: fadeIn 0.2s ease-in;
}

#layout-preview .card {
    border: 2px dashed #cbd5e0;
    background: #fff;
}

.widget-toggle:checked + label {
    font-weight: 600;
    color: #2c5282;
}

@media print {
    .card-header,
    #report-params-form,
    .btn,
    #chart-card,
    nav,
    .breadcrumb {
        display: none !important;
    }
    #report-results-table {
        font-size: 10pt;
    }
}
```

## File: modules\reports\js\reports.js

```js
/**
 * Reports Module â€” Global JS
 */
(function() {
    'use strict';

    // Auto-load dynamic select options from known sources
    window.ReportsModule = {
        loadSelectOptions: function(selectElement, source) {
            var db = Database || null;
            var url = '';

            var sourceMap = {
                'membership_types': { url: '/api/members/search?q=&type=membership_types', labelField: 'name_en', valueField: 'id' },
                'payment_methods': { url: '/api/reports/param-options?source=payment_methods', labelField: 'name_en', valueField: 'id' },
                'complaint_categories': { url: '/api/reports/param-options?source=complaint_categories', labelField: 'name_en', valueField: 'id' }
            };

            if (!sourceMap[source]) return;

            $.get(sourceMap[source].url, function(res) {
                var data = res.data || res.results || res;
                if (!Array.isArray(data)) return;
                data.forEach(function(item) {
                    var val = item[sourceMap[source].valueField] || item.id;
                    var label = item[sourceMap[source].labelField] || item.text || item.name;
                    $(selectElement).append('<option value="' + val + '">' + label + '</option>');
                });
            }).fail(function() {
                // Silently fail â€” options just won't be populated
            });
        }
    };

})();
```

## File: modules\reports\lang\ar.php

```php
<?php
return [
    'reports.reports'             => 'Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ±',
    'reports.browser_title'       => 'ØªØµÙØ­ Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ±',
    'reports.kpi_dashboard'       => 'Ù„ÙˆØ­Ø© Ø§Ù„Ù…Ø¤Ø´Ø±Ø§Øª',
    'reports.custom_builder'      => 'Ù…Ù†Ø´Ø¦ Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ± Ø§Ù„Ù…Ø®ØµØµØ©',
    'reports.scheduled_reports'   => 'Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ± Ø§Ù„Ù…Ø¬Ø¯ÙˆÙ„Ø©',
    'reports.dashboard_config'    => 'Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Ù„ÙˆØ­Ø© Ø§Ù„ØªØ­ÙƒÙ…',
    'reports.favorites'           => 'Ø§Ù„Ù…ÙØ¶Ù„Ø©',
    'reports.parameters'          => 'Ø§Ù„Ù…Ø¹Ø§ÙŠÙŠØ±',
    'reports.generate'            => 'ØªÙ†ÙÙŠØ°',
    'reports.no_reports'          => 'Ù„Ø§ ØªÙˆØ¬Ø¯ ØªÙ‚Ø§Ø±ÙŠØ± Ù…ØªØ§Ø­Ø©',
    'reports.create_schedule'     => 'Ø¥Ù†Ø´Ø§Ø¡ Ø¬Ø¯ÙˆÙ„',
    'reports.edit_schedule'       => 'ØªØ¹Ø¯ÙŠÙ„ Ø§Ù„Ø¬Ø¯ÙˆÙ„',
    'reports.schedule_created'    => 'ØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø¬Ø¯ÙˆÙ„ Ø¨Ù†Ø¬Ø§Ø­',
    'reports.schedule_updated'    => 'ØªÙ… ØªØ­Ø¯ÙŠØ« Ø§Ù„Ø¬Ø¯ÙˆÙ„ Ø¨Ù†Ø¬Ø§Ø­',
    'reports.schedule_deleted'    => 'ØªÙ… Ø­Ø°Ù Ø§Ù„Ø¬Ø¯ÙˆÙ„ Ø¨Ù†Ø¬Ø§Ø­',
    'reports.dashboard_saved'     => 'ØªÙ… Ø­ÙØ¸ ØªØ®Ø·ÙŠØ· Ù„ÙˆØ­Ø© Ø§Ù„ØªØ­ÙƒÙ…',
    'reports.active_members'      => 'Ø§Ù„Ø£Ø¹Ø¶Ø§Ø¡ Ø§Ù„Ù†Ø´Ø·ÙŠÙ†',
    'reports.pending_applications'=> 'Ø§Ù„Ø·Ù„Ø¨Ø§Øª Ø§Ù„Ù…Ø¹Ù„Ù‚Ø©',
    'reports.overdue_invoices'    => 'Ø§Ù„ÙÙˆØ§ØªÙŠØ± Ø§Ù„Ù…ØªØ£Ø®Ø±Ø©',
    'reports.today_revenue'       => 'Ø¥ÙŠØ±Ø§Ø¯Ø§Øª Ø§Ù„ÙŠÙˆÙ…',
    'reports.revenue_trend'       => 'Ø§ØªØ¬Ø§Ù‡ Ø§Ù„Ø¥ÙŠØ±Ø§Ø¯Ø§Øª',
    'reports.member_growth'       => 'Ù†Ù…Ùˆ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
];
```

## File: modules\reports\lang\en.php

```php
<?php
return [
    'reports.reports'             => 'Reports',
    'reports.browser_title'       => 'Report Browser',
    'reports.kpi_dashboard'       => 'KPI Dashboard',
    'reports.custom_builder'      => 'Custom Report Builder',
    'reports.scheduled_reports'   => 'Scheduled Reports',
    'reports.dashboard_config'    => 'Dashboard Configuration',
    'reports.favorites'           => 'Favorites',
    'reports.parameters'          => 'Parameters',
    'reports.generate'            => 'Generate',
    'reports.no_reports'          => 'No reports available',
    'reports.create_schedule'     => 'Create Schedule',
    'reports.edit_schedule'       => 'Edit Schedule',
    'reports.schedule_created'    => 'Schedule created successfully',
    'reports.schedule_updated'    => 'Schedule updated successfully',
    'reports.schedule_deleted'    => 'Schedule deleted successfully',
    'reports.dashboard_saved'     => 'Dashboard layout saved',
    'reports.active_members'      => 'Active Members',
    'reports.pending_applications'=> 'Pending Applications',
    'reports.overdue_invoices'    => 'Overdue Invoices',
    'reports.today_revenue'       => 'Today\'s Revenue',
    'reports.revenue_trend'       => 'Revenue Trend',
    'reports.member_growth'       => 'Member Growth',
];
```

## File: modules\reports\module.json

```json
{
    "name": "reports",
    "label_en": "Reports & Dashboards",
    "label_ar": "Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ± ÙˆÙ„ÙˆØ­Ø§Øª Ø§Ù„Ù…Ø¹Ù„ÙˆÙ…Ø§Øª",
    "version": "1.0.0",
    "description": "Comprehensive reporting, KPI dashboards, custom report builder, and scheduled reports",
    "sort_order": 120,
    "icon": "fas fa-chart-bar",
    "routes_file": "routes.php",
    "css_files": ["css/reports.css"],
    "js_files": ["js/reports.js"],
    "menu": [
        {
            "label_en": "Reports",
            "label_ar": "Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ±",
            "icon": "fas fa-chart-bar",
            "permission": "reports.view",
            "sort_order": 120,
            "children": [
                {
                    "label_en": "Report Browser",
                    "label_ar": "ØªØµÙØ­ Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ±",
                    "route": "/reports",
                    "permission": "reports.view",
                    "sort_order": 1
                },
                {
                    "label_en": "KPI Dashboard",
                    "label_ar": "Ù„ÙˆØ­Ø© Ø§Ù„Ù…Ø¤Ø´Ø±Ø§Øª",
                    "route": "/reports/kpis",
                    "permission": "reports.view",
                    "sort_order": 2
                },
                {
                    "label_en": "Custom Report Builder",
                    "label_ar": "Ù…Ù†Ø´Ø¦ Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ±",
                    "route": "/reports/custom",
                    "permission": "reports.custom",
                    "sort_order": 3
                },
                {
                    "label_en": "Scheduled Reports",
                    "label_ar": "Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ± Ø§Ù„Ù…Ø¬Ø¯ÙˆÙ„Ø©",
                    "route": "/reports/schedules",
                    "permission": "reports.schedule",
                    "sort_order": 4
                },
                {
                    "label_en": "Dashboard Config",
                    "label_ar": "Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Ù„ÙˆØ­Ø© Ø§Ù„ØªØ­ÙƒÙ…",
                    "route": "/reports/dashboard-config",
                    "permission": "reports.view",
                    "sort_order": 5
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "kpi_summary",
            "label_en": "KPI Summary",
            "label_ar": "Ù…Ù„Ø®Øµ Ø§Ù„Ù…Ø¤Ø´Ø±Ø§Øª",
            "permission": "reports.view",
            "partial": "views/widgets/kpi_summary.php",
            "size": "col-12",
            "sort_order": 1
        },
        {
            "widget_id": "revenue_chart",
            "label_en": "Revenue Trend",
            "label_ar": "Ø§ØªØ¬Ø§Ù‡ Ø§Ù„Ø¥ÙŠØ±Ø§Ø¯Ø§Øª",
            "permission": "reports.view",
            "partial": "views/widgets/revenue_chart.php",
            "size": "col-md-8",
            "sort_order": 5
        },
        {
            "widget_id": "member_growth",
            "label_en": "Member Growth",
            "label_ar": "Ù†Ù…Ùˆ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©",
            "permission": "reports.view",
            "partial": "views/widgets/member_growth.php",
            "size": "col-md-4",
            "sort_order": 6
        }
    ]
}
```

## File: modules\reports\routes.php

```php
<?php
return [
    // â”€â”€ Page Routes â”€â”€
    ['GET',  '/reports',                        'ReportController@index',           'reports.view'],
    ['GET',  '/reports/kpis',                   'ReportController@kpis',            'reports.view'],
    ['GET',  '/reports/custom',                 'ReportController@custom',          'reports.custom'],
    ['GET',  '/reports/dashboard-config',       'ReportController@dashboardConfig', 'reports.view'],
    ['GET',  '/reports/schedules',              'ScheduleController@index',         'reports.schedule'],
    ['GET',  '/reports/schedules/create',       'ScheduleController@create',        'reports.schedule'],
    ['GET',  '/reports/schedules/{id}/edit',    'ScheduleController@edit',          'reports.schedule'],
    ['GET',  '/reports/{id}',                   'ReportController@view',            'reports.view'],

    // â”€â”€ API: Reports â”€â”€
    ['POST', '/api/reports/init',               'ReportApiController@initReports',     'reports.view'],
    ['POST', '/api/reports/execute/{id}',       'ReportApiController@execute',         'reports.view'],
    ['POST', '/api/reports/export/{id}',        'ReportApiController@export',          'reports.view'],
    ['GET',  '/api/reports/kpi-data',           'ReportApiController@kpiData',         'reports.view'],
    ['GET',  '/api/reports/revenue-chart',      'ReportApiController@revenueChart',    'reports.view'],
    ['GET',  '/api/reports/member-growth',      'ReportApiController@memberGrowth',    'reports.view'],
    ['POST', '/api/reports/favorites',          'ReportApiController@saveFavorite',    'reports.view'],
    ['POST', '/api/reports/favorites/delete',   'ReportApiController@deleteFavorite',  'reports.view'],
    ['GET',  '/api/reports/favorites',          'ReportApiController@listFavorites',   'reports.view'],

    // â”€â”€ API: Custom Builder â”€â”€
    ['GET',  '/api/reports/custom/sources',     'CustomReportApiController@sources',   'reports.custom'],
    ['POST', '/api/reports/custom/columns',     'CustomReportApiController@columns',   'reports.custom'],
    ['POST', '/api/reports/custom/preview',     'CustomReportApiController@preview',   'reports.custom'],
    ['POST', '/api/reports/custom/save',        'CustomReportApiController@save',      'reports.custom'],

    // â”€â”€ API: Schedules â”€â”€
    ['GET',  '/api/reports/schedules/datatable','ScheduleApiController@datatable',     'reports.schedule'],
    ['POST', '/api/reports/schedules',          'ScheduleApiController@store',         'reports.schedule'],
    ['POST', '/api/reports/schedules/{id}',     'ScheduleApiController@update',        'reports.schedule'],
    ['POST', '/api/reports/schedules/{id}/delete','ScheduleApiController@destroy',     'reports.schedule'],
    ['POST', '/api/reports/schedules/{id}/toggle','ScheduleApiController@toggle',      'reports.schedule'],
    ['GET',  '/api/reports/schedules/{id}/runs','ScheduleApiController@runs',          'reports.schedule'],

    // â”€â”€ API: Dashboard Config â”€â”€
    ['GET',  '/api/reports/dashboard-config',   'DashboardConfigApiController@load',   'reports.view'],
    ['POST', '/api/reports/dashboard-config',   'DashboardConfigApiController@save',   'reports.view'],
];
```

## File: modules\reports\views\custom.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-wrench me-2"></i><?= lang('reports.custom_builder') ?></h4>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">1. Select Data Source</h6></div>
            <div class="card-body">
                <select id="custom-source" class="form-select"><option value="">-- Select Source --</option></select>
            </div>
        </div>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0">2. Select Columns</h6></div>
            <div class="card-body" id="columns-panel">
                <p class="text-muted small">Select a source first</p>
            </div>
        </div>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0">3. Filters (optional)</h6></div>
            <div class="card-body" id="filters-panel">
                <div id="filter-rows"></div>
                <button class="btn btn-sm btn-outline-primary mt-2" id="add-filter"><i class="fas fa-plus me-1"></i>Add Filter</button>
            </div>
        </div>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0">4. Sorting</h6></div>
            <div class="card-body">
                <div class="row g-2">
                    <div class="col-8"><select id="sort-by" class="form-select form-select-sm"><option value="">-- None --</option></select></div>
                    <div class="col-4"><select id="sort-dir" class="form-select form-select-sm"><option value="ASC">ASC</option><option value="DESC">DESC</option></select></div>
                </div>
            </div>
        </div>
        <div class="mt-3 d-flex gap-2">
            <button class="btn btn-primary" id="btn-preview"><i class="fas fa-eye me-1"></i>Preview</button>
            <button class="btn btn-success" id="btn-save-custom" disabled><i class="fas fa-save me-1"></i>Save as Report</button>
        </div>
    </div>
    <div class="col-md-8">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Preview Results</h6></div>
            <div class="card-body">
                <div id="preview-area" class="text-center text-muted py-5">
                    <i class="fas fa-table fa-3x mb-3"></i>
                    <p>Configure and click Preview to see results</p>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Save Modal -->
<div class="modal fade" id="saveCustomModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header"><h5 class="modal-title">Save Custom Report</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="mb-3"><label class="form-label">Report Name (EN) *</label><input type="text" id="save-name-en" class="form-control" required></div>
                <div class="mb-3"><label class="form-label">Report Name (AR)</label><input type="text" id="save-name-ar" class="form-control"></div>
                <div class="mb-3"><label class="form-label">Description</label><textarea id="save-desc" class="form-control" rows="2"></textarea></div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button class="btn btn-primary" id="btn-confirm-save"><i class="fas fa-save me-1"></i>Save</button>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
(function() {
    var availableColumns = [];
    var csrf = $('meta[name="csrf-token"]').attr('content');

    $.get('/api/reports/custom/sources', function(res) {
        if (res.success) {
            res.data.forEach(function(s) {
                $('#custom-source').append('<option value="'+s.value+'">'+s.label+'</option>');
            });
        }
    });

    $('#custom-source').on('change', function() {
        var source = $(this).val();
        if (!source) return;
        $.ajax({
            url: '/api/reports/custom/columns',
            type: 'POST',
            data: { source: source },
            headers: { 'X-CSRF-TOKEN': csrf, 'X-Requested-With': 'XMLHttpRequest' },
            success: function(res) {
                availableColumns = res.data || [];
                var html = '';
                availableColumns.forEach(function(c) {
                    html += '<div class="form-check"><input class="form-check-input col-check" type="checkbox" value="'+c.name+'" id="col-'+c.name+'"><label class="form-check-label" for="col-'+c.name+'">'+c.name+' <small class="text-muted">('+c.type+')</small></label></div>';
                });
                html += '<div class="mt-2"><button class="btn btn-xs btn-outline-primary" id="select-all-cols">Select All</button></div>';
                $('#columns-panel').html(html);
                var sortHtml = '<option value="">-- None --</option>';
                availableColumns.forEach(function(c) { sortHtml += '<option value="'+c.name+'">'+c.name+'</option>'; });
                $('#sort-by').html(sortHtml);
                $(document).off('click','#select-all-cols').on('click','#select-all-cols', function(e) {
                    e.preventDefault();
                    $('.col-check').prop('checked', true);
                });
            }
        });
    });

    function getFilterRow() {
        var opts = availableColumns.map(function(c) { return '<option value="'+c.name+'">'+c.name+'</option>'; }).join('');
        return '<div class="row g-1 mb-1 filter-row"><div class="col-4"><select class="form-select form-select-sm filter-col">'+opts+'</select></div><div class="col-3"><select class="form-select form-select-sm filter-op"><option value="=">=</option><option value="LIKE">LIKE</option><option value="!=">!=</option><option value=">">></option><option value="<"><</option></select></div><div class="col-4"><input type="text" class="form-control form-control-sm filter-val"></div><div class="col-1"><button class="btn btn-sm btn-outline-danger remove-filter"><i class="fas fa-times"></i></button></div></div>';
    }

    $('#add-filter').on('click', function() {
        if (availableColumns.length) $('#filter-rows').append(getFilterRow());
    });
    $(document).on('click', '.remove-filter', function() { $(this).closest('.filter-row').remove(); });

    $('#btn-preview').on('click', function() {
        var source = $('#custom-source').val();
        var cols = [];
        $('.col-check:checked').each(function() { cols.push($(this).val()); });
        if (!source || !cols.length) { Swal.fire('Error','Select source and columns','warning'); return; }

        var filters = [];
        $('.filter-row').each(function() {
            var col = $(this).find('.filter-col').val();
            var op = $(this).find('.filter-op').val();
            var val = $(this).find('.filter-val').val();
            if (col && val) filters.push({column: col, operator: op, value: val});
        });

        $.ajax({
            url: '/api/reports/custom/preview',
            type: 'POST',
            data: JSON.stringify({ source: source, columns: cols, filters: filters, sort_by: $('#sort-by').val(), sort_dir: $('#sort-dir').val() }),
            contentType: 'application/json',
            headers: { 'X-CSRF-TOKEN': csrf, 'X-Requested-With': 'XMLHttpRequest' },
            success: function(res) {
                if (!res.success || !res.data.length) {
                    $('#preview-area').html('<p class="text-muted">No results found</p>');
                    return;
                }
                var html = '<div class="table-responsive"><table class="table table-sm table-striped"><thead><tr>';
                cols.forEach(function(c) { html += '<th>'+c+'</th>'; });
                html += '</tr></thead><tbody>';
                res.data.forEach(function(row) {
                    html += '<tr>';
                    cols.forEach(function(c) { html += '<td>'+(row[c] !== null ? row[c] : '')+'</td>'; });
                    html += '</tr>';
                });
                html += '</tbody></table></div><p class="small text-muted">Showing '+res.data.length+' rows (max 100 preview)</p>';
                $('#preview-area').html(html);
                $('#btn-save-custom').prop('disabled', false);
            }
        });
    });

    $('#btn-save-custom').on('click', function() {
        new bootstrap.Modal('#saveCustomModal').show();
    });

    $('#btn-confirm-save').on('click', function() {
        var source = $('#custom-source').val();
        var cols = [];
        $('.col-check:checked').each(function() { cols.push($(this).val()); });

        $.ajax({
            url: '/api/reports/custom/save',
            type: 'POST',
            data: { source: source, columns: JSON.stringify(cols), report_name_en: $('#save-name-en').val(), report_name_ar: $('#save-name-ar').val(), description_en: $('#save-desc').val() },
            headers: { 'X-CSRF-TOKEN': csrf, 'X-Requested-With': 'XMLHttpRequest' },
            success: function(res) {
                if (res.success) {
                    window.location.href = res.redirect || '/reports';
                } else {
                    Swal.fire('Error', res.message || 'Save failed', 'error');
                }
            }
        });
    });
})();
</script>
<?php $__view->endPush() ?>
```

## File: modules\reports\views\dashboard_config.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-th-large me-2"></i><?= lang('reports.dashboard_config') ?></h4>
    <button class="btn btn-primary btn-sm" id="btn-save-layout"><i class="fas fa-save me-1"></i>Save Layout</button>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Available Widgets</h6></div>
            <div class="card-body" id="available-widgets">
                <?php foreach ($allWidgets as $w): ?>
                <div class="form-check mb-2">
                    <input class="form-check-input widget-toggle" type="checkbox" value="<?= e($w['widget_code']) ?>" id="w-<?= e($w['widget_code']) ?>"
                        <?php
                        $activeWidgets = $userLayout ? json_decode($userLayout['widgets'] ?? '[]', true) : [];
                        if (empty($activeWidgets) || in_array($w['widget_code'], array_column($activeWidgets, 'code'))) echo 'checked';
                        ?>>
                    <label class="form-check-label" for="w-<?= e($w['widget_code']) ?>">
                        <?= e($w['widget_name_en']) ?>
                        <small class="text-muted d-block"><?= e($w['widget_name_ar'] ?? '') ?></small>
                    </label>
                </div>
                <?php endforeach; ?>
                <?php if (empty($allWidgets)): ?>
                <p class="text-muted">No system widgets registered yet. Module widgets will appear as they are installed.</p>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <div class="col-md-8">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Current Layout Preview</h6></div>
            <div class="card-body">
                <p class="text-muted">Toggle widgets on the left panel and save your preferred dashboard layout. Changes take effect on the main dashboard after saving.</p>
                <div id="layout-preview" class="border rounded p-3 bg-light" style="min-height:200px">
                    <div class="row g-2" id="preview-grid"></div>
                </div>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function() {
    var csrf = $('meta[name="csrf-token"]').attr('content');

    function updatePreview() {
        var grid = $('#preview-grid');
        grid.empty();
        $('.widget-toggle:checked').each(function() {
            var code = $(this).val();
            var label = $(this).next('label').text().trim().split('\n')[0];
            grid.append('<div class="col-md-4"><div class="card"><div class="card-body py-2 text-center"><small>'+label+'</small></div></div></div>');
        });
        if (!grid.children().length) {
            grid.html('<p class="text-muted text-center py-4">No widgets selected</p>');
        }
    }

    $('.widget-toggle').on('change', updatePreview);
    updatePreview();

    $('#btn-save-layout').on('click', function() {
        var widgets = [];
        $('.widget-toggle:checked').each(function() {
            widgets.push({ code: $(this).val(), order: widgets.length });
        });
        $.ajax({
            url: '/api/reports/dashboard-config',
            type: 'POST',
            data: { widgets: JSON.stringify(widgets) },
            headers: { 'X-CSRF-TOKEN': csrf, 'X-Requested-With': 'XMLHttpRequest' },
            success: function(res) {
                if (res.success) {
                    Swal.fire({icon:'success',title:'Saved!',timer:1500,showConfirmButton:false,toast:true,position:'top-end'});
                }
            }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\reports\views\index.php

```php
<?php
$categoryLabels = [
    'membership'  => ['en' => 'Membership', 'ar' => 'Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©', 'icon' => 'fas fa-users', 'color' => 'primary'],
    'financial'   => ['en' => 'Financial',  'ar' => 'Ø§Ù„Ù…Ø§Ù„ÙŠØ©',  'icon' => 'fas fa-money-bill-wave', 'color' => 'success'],
    'operational' => ['en' => 'Operational','ar' => 'Ø§Ù„ØªØ´ØºÙŠÙ„',  'icon' => 'fas fa-cogs', 'color' => 'info'],
    'governance'  => ['en' => 'Governance', 'ar' => 'Ø§Ù„Ø­ÙˆÙƒÙ…Ø©',  'icon' => 'fas fa-landmark', 'color' => 'warning'],
    'custom'      => ['en' => 'Custom',     'ar' => 'Ù…Ø®ØµØµ',     'icon' => 'fas fa-wrench', 'color' => 'secondary'],
    'other'       => ['en' => 'Other',      'ar' => 'Ø£Ø®Ø±Ù‰',     'icon' => 'fas fa-folder', 'color' => 'dark'],
];
$lang = Lang::current();
?>

<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-chart-bar me-2"></i><?= lang('reports.browser_title') ?></h4>
    <div class="d-flex gap-2">
        <input type="text" id="report-search" class="form-control form-control-sm" placeholder="<?= lang('common.search') ?>..." style="width:250px">
    </div>
</div>

<?php if (!empty($favorites)): ?>
<div class="card mb-4">
    <div class="card-header bg-warning bg-opacity-10">
        <h6 class="mb-0"><i class="fas fa-star text-warning me-2"></i><?= lang('reports.favorites') ?></h6>
    </div>
    <div class="card-body">
        <div class="row g-3">
            <?php foreach ($favorites as $fav): ?>
            <div class="col-md-3">
                <a href="/reports/<?= (int)$fav['report_id'] ?>" class="text-decoration-none">
                    <div class="card border-warning h-100 report-card-item">
                        <div class="card-body py-3">
                            <h6 class="mb-1"><?= e($fav['favorite_name']) ?></h6>
                            <small class="text-muted"><?= e($fav['report_name_' . $lang] ?: $fav['report_name_en']) ?></small>
                        </div>
                    </div>
                </a>
            </div>
            <?php endforeach; ?>
        </div>
    </div>
</div>
<?php endif; ?>

<?php foreach ($categories as $catKey => $catReports): ?>
<?php $catInfo = $categoryLabels[$catKey] ?? $categoryLabels['other']; ?>
<div class="card mb-4 report-category-card" data-category="<?= e($catKey) ?>">
    <div class="card-header">
        <h5 class="mb-0">
            <i class="<?= e($catInfo['icon']) ?> text-<?= e($catInfo['color']) ?> me-2"></i>
            <?= e($catInfo[$lang] ?? $catInfo['en']) ?>
            <span class="badge bg-<?= e($catInfo['color']) ?> ms-2"><?= count($catReports) ?></span>
        </h5>
    </div>
    <div class="card-body">
        <div class="row g-3">
            <?php foreach ($catReports as $report): ?>
            <div class="col-md-4 col-lg-3 report-card-wrapper" data-name="<?= e(strtolower($report['report_name_en'] . ' ' . ($report['report_name_ar'] ?? ''))) ?>">
                <a href="/reports/<?= (int)$report['id'] ?>" class="text-decoration-none">
                    <div class="card h-100 report-card-item border-start border-3 border-<?= e($catInfo['color']) ?>">
                        <div class="card-body">
                            <div class="d-flex justify-content-between align-items-start">
                                <h6 class="card-title mb-1"><?= e($report['report_name_' . $lang] ?: $report['report_name_en']) ?></h6>
                                <?php if ($report['is_favorite']): ?>
                                <i class="fas fa-star text-warning"></i>
                                <?php endif; ?>
                            </div>
                            <p class="card-text text-muted small mb-2">
                                <?= e(str_limit($report['description_' . $lang] ?: $report['description_en'] ?: '', 80)) ?>
                            </p>
                            <div class="d-flex gap-1">
                                <?php if ($report['chart_type']): ?>
                                <span class="badge bg-light text-dark"><i class="fas fa-chart-pie me-1"></i>Chart</span>
                                <?php endif; ?>
                                <?php if ($report['is_schedulable']): ?>
                                <span class="badge bg-light text-dark"><i class="fas fa-clock me-1"></i>Schedulable</span>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>
                </a>
            </div>
            <?php endforeach; ?>
        </div>
    </div>
</div>
<?php endforeach; ?>

<?php if (empty($categories)): ?>
<div class="card">
    <div class="card-body text-center py-5">
        <i class="fas fa-chart-bar fa-3x text-muted mb-3"></i>
        <h5 class="text-muted"><?= lang('reports.no_reports') ?></h5>
        <p class="text-muted">Reports will be available once initialized.</p>
        <button class="btn btn-primary" id="btn-init-reports"><i class="fas fa-magic me-1"></i>Initialize Reports</button>
    </div>
</div>
<?php endif; ?>

<?php $__view->push('scripts') ?>
<script>
$(function() {
    $('#report-search').on('input', function() {
        var q = $(this).val().toLowerCase();
        $('.report-card-wrapper').each(function() {
            var name = $(this).data('name') || '';
            $(this).toggle(name.indexOf(q) !== -1);
        });
        $('.report-category-card').each(function() {
            var visible = $(this).find('.report-card-wrapper:visible').length;
            $(this).toggle(visible > 0);
        });
    });

    $('#btn-init-reports').on('click', function() {
        var $btn = $(this);
        $btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Initializing...');
        $.ajax({
            url: '/api/reports/init',
            type: 'POST',
            headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
            success: function() { location.reload(); },
            error: function() { $btn.prop('disabled', false).text('Initialize Reports'); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\reports\views\kpis.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-tachometer-alt me-2"></i><?= lang('reports.kpi_dashboard') ?></h4>
    <button class="btn btn-sm btn-outline-primary" id="btn-refresh-kpis"><i class="fas fa-sync me-1"></i>Refresh</button>
</div>

<div class="row g-3 mb-4" id="kpi-cards">
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><h6 class="text-muted">Active Members</h6><h2 class="mb-0" id="kpi-active-members"><span class="spinner-border spinner-border-sm"></span></h2></div></div></div>
    <div class="col-md-3"><div class="card border-success h-100"><div class="card-body"><h6 class="text-muted">Revenue This Month</h6><h2 class="mb-0" id="kpi-revenue"><span class="spinner-border spinner-border-sm"></span></h2></div></div></div>
    <div class="col-md-3"><div class="card border-warning h-100"><div class="card-body"><h6 class="text-muted">Outstanding</h6><h2 class="mb-0" id="kpi-outstanding"><span class="spinner-border spinner-border-sm"></span></h2></div></div></div>
    <div class="col-md-3"><div class="card border-danger h-100"><div class="card-body"><h6 class="text-muted">Overdue</h6><h2 class="mb-0" id="kpi-overdue"><span class="spinner-border spinner-border-sm"></span></h2></div></div></div>
</div>

<div class="row g-3 mb-4">
    <div class="col-md-2"><div class="card h-100"><div class="card-body text-center"><i class="fas fa-user-plus fa-2x text-info mb-2"></i><h5 id="kpi-new-members">-</h5><small class="text-muted">New This Month</small></div></div></div>
    <div class="col-md-2"><div class="card h-100"><div class="card-body text-center"><i class="fas fa-file-alt fa-2x text-warning mb-2"></i><h5 id="kpi-pending-apps">-</h5><small class="text-muted">Pending Apps</small></div></div></div>
    <div class="col-md-2"><div class="card h-100"><div class="card-body text-center"><i class="fas fa-snowflake fa-2x text-info mb-2"></i><h5 id="kpi-freezes">-</h5><small class="text-muted">Active Freezes</small></div></div></div>
    <div class="col-md-2"><div class="card h-100"><div class="card-body text-center"><i class="fas fa-exclamation-triangle fa-2x text-danger mb-2"></i><h5 id="kpi-violations">-</h5><small class="text-muted">Open Violations</small></div></div></div>
    <div class="col-md-2"><div class="card h-100"><div class="card-body text-center"><i class="fas fa-headset fa-2x text-primary mb-2"></i><h5 id="kpi-complaints">-</h5><small class="text-muted">Open Complaints</small></div></div></div>
    <div class="col-md-2"><div class="card h-100"><div class="card-body text-center"><i class="fas fa-id-card fa-2x text-secondary mb-2"></i><h5 id="kpi-cards-expiring">-</h5><small class="text-muted">Cards Expiring</small></div></div></div>
</div>

<div class="row g-3">
    <div class="col-md-8">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><i class="fas fa-chart-line me-2"></i>Revenue Trend (30 Days)</h6></div>
            <div class="card-body"><canvas id="revenue-trend-chart" height="300"></canvas></div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><i class="fas fa-chart-bar me-2"></i>Member Growth (12 Months)</h6></div>
            <div class="card-body"><canvas id="member-growth-chart" height="300"></canvas></div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
(function() {
    var revenueChart = null;
    var growthChart = null;

    function loadKpis() {
        $.get('/api/reports/kpi-data', function(res) {
            if (!res.success) return;
            var d = res.data;
            $('#kpi-active-members').text(parseInt(d.total_active_members || 0).toLocaleString());
            $('#kpi-revenue').text(parseFloat(d.revenue_this_month || 0).toLocaleString('en',{minimumFractionDigits:2}));
            $('#kpi-outstanding').text(parseFloat(d.total_outstanding || 0).toLocaleString('en',{minimumFractionDigits:2}));
            $('#kpi-overdue').text(parseFloat(d.total_overdue || 0).toLocaleString('en',{minimumFractionDigits:2}));
            $('#kpi-new-members').text(d.new_members_this_month || 0);
            $('#kpi-pending-apps').text(d.pending_applications || 0);
            $('#kpi-freezes').text(d.active_freezes || 0);
            $('#kpi-violations').text(d.open_violations || 0);
            $('#kpi-complaints').text(d.open_complaints || 0);
            $('#kpi-cards-expiring').text(d.cards_expiring_30_days || 0);
        });
    }

    function loadRevenueChart() {
        $.get('/api/reports/revenue-chart?days=30', function(res) {
            if (!res.success) return;
            var ctx = document.getElementById('revenue-trend-chart');
            if (revenueChart) revenueChart.destroy();
            revenueChart = new Chart(ctx, {
                type: 'line',
                data: { labels: res.labels, datasets: [{label: 'Revenue', data: res.values, borderColor: '#2c5282', backgroundColor: 'rgba(44,82,130,0.1)', fill: true, tension: 0.3}] },
                options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } }, plugins: { legend: { display: false } } }
            });
        });
    }

    function loadGrowthChart() {
        $.get('/api/reports/member-growth', function(res) {
            if (!res.success) return;
            var ctx = document.getElementById('member-growth-chart');
            if (growthChart) growthChart.destroy();
            growthChart = new Chart(ctx, {
                type: 'bar',
                data: { labels: res.labels, datasets: [{label: 'New Members', data: res.values, backgroundColor: '#38a169'}] },
                options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } }, plugins: { legend: { display: false } } }
            });
        });
    }

    loadKpis();
    loadRevenueChart();
    loadGrowthChart();

    $('#btn-refresh-kpis').on('click', function() {
        loadKpis();
        loadRevenueChart();
        loadGrowthChart();
    });
})();
</script>
<?php $__view->endPush() ?>
```

## File: modules\reports\views\schedules\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-plus me-2"></i><?= lang('reports.create_schedule') ?></h5></div>
            <div class="card-body">
                <form id="schedule-form" data-ajax="true" method="POST" action="/api/reports/schedules">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Schedule Name <span class="text-danger">*</span></label>
                            <input type="text" name="schedule_name" class="form-control" required>
                            <div class="invalid-feedback" data-field="schedule_name"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Report <span class="text-danger">*</span></label>
                            <select name="report_id" class="form-select" required>
                                <option value="">-- Select --</option>
                                <?php foreach ($reports as $r): ?>
                                <option value="<?= (int)$r['id'] ?>"><?= e($r['report_name_en']) ?> (<?= e($r['report_code']) ?>)</option>
                                <?php endforeach; ?>
                            </select>
                            <div class="invalid-feedback" data-field="report_id"></div>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Frequency <span class="text-danger">*</span></label>
                            <select name="frequency" class="form-select" required>
                                <option value="daily">Daily</option>
                                <option value="weekly">Weekly</option>
                                <option value="monthly" selected>Monthly</option>
                                <option value="quarterly">Quarterly</option>
                                <option value="yearly">Yearly</option>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Day of Week (for weekly)</label>
                            <select name="day_of_week" class="form-select">
                                <option value="">N/A</option>
                                <option value="0">Sunday</option><option value="1">Monday</option><option value="2">Tuesday</option><option value="3">Wednesday</option><option value="4">Thursday</option><option value="5">Friday</option><option value="6">Saturday</option>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Day of Month</label>
                            <input type="number" name="day_of_month" class="form-control" min="1" max="28" value="1">
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Time <span class="text-danger">*</span></label>
                            <input type="time" name="time_of_day" class="form-control" value="06:00" required>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Output Format <span class="text-danger">*</span></label>
                            <select name="output_format" class="form-select" required>
                                <option value="pdf">PDF</option>
                                <option value="excel">Excel</option>
                                <option value="csv">CSV</option>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Delivery <span class="text-danger">*</span></label>
                            <select name="delivery_method" class="form-select" required>
                                <option value="email">Email</option>
                                <option value="in_app">In-App</option>
                                <option value="save">Save to Server</option>
                                <option value="both_email_save">Email + Save</option>
                            </select>
                        </div>
                        <div class="col-12">
                            <label class="form-label">Recipients (comma-separated emails)</label>
                            <input type="text" name="recipients" class="form-control" placeholder="admin@club.com, finance@club.com">
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/reports/schedules" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\reports\views\schedules\edit.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-edit me-2"></i><?= lang('reports.edit_schedule') ?></h5></div>
            <div class="card-body">
                <form id="schedule-form" data-ajax="true" method="POST" action="/api/reports/schedules/<?= (int)$schedule['id'] ?>">
                    <?= csrf_field() ?>
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="form-label">Schedule Name <span class="text-danger">*</span></label>
                            <input type="text" name="schedule_name" class="form-control" value="<?= e($schedule['schedule_name']) ?>" required>
                            <div class="invalid-feedback" data-field="schedule_name"></div>
                        </div>
                        <div class="col-md-6">
                            <label class="form-label">Report <span class="text-danger">*</span></label>
                            <select name="report_id" class="form-select" required>
                                <?php foreach ($reports as $r): ?>
                                <option value="<?= (int)$r['id'] ?>" <?= $r['id'] == $schedule['report_id'] ? 'selected' : '' ?>><?= e($r['report_name_en']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Frequency</label>
                            <select name="frequency" class="form-select" required>
                                <?php foreach (['daily','weekly','monthly','quarterly','yearly'] as $f): ?>
                                <option value="<?= $f ?>" <?= $schedule['frequency'] === $f ? 'selected' : '' ?>><?= ucfirst($f) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Day of Week</label>
                            <select name="day_of_week" class="form-select">
                                <option value="">N/A</option>
                                <?php for ($i = 0; $i < 7; $i++): ?>
                                <option value="<?= $i ?>" <?= $schedule['day_of_week'] == $i ? 'selected' : '' ?>><?= date('l', strtotime("Sunday +{$i} days")) ?></option>
                                <?php endfor; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Day of Month</label>
                            <input type="number" name="day_of_month" class="form-control" min="1" max="28" value="<?= (int)($schedule['day_of_month'] ?? 1) ?>">
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Time</label>
                            <input type="time" name="time_of_day" class="form-control" value="<?= e($schedule['time_of_day'] ?? '06:00') ?>" required>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Output Format</label>
                            <select name="output_format" class="form-select" required>
                                <?php foreach (['pdf','excel','csv'] as $f): ?>
                                <option value="<?= $f ?>" <?= $schedule['output_format'] === $f ? 'selected' : '' ?>><?= strtoupper($f) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-4">
                            <label class="form-label">Delivery</label>
                            <select name="delivery_method" class="form-select" required>
                                <?php foreach (['email','in_app','save','both_email_save'] as $d): ?>
                                <option value="<?= $d ?>" <?= $schedule['delivery_method'] === $d ? 'selected' : '' ?>><?= ucfirst(str_replace('_', ' ', $d)) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-12">
                            <label class="form-label">Recipients</label>
                            <?php $recip = json_decode($schedule['recipients'] ?? '[]', true); ?>
                            <input type="text" name="recipients" class="form-control" value="<?= e(is_array($recip) ? implode(', ', $recip) : '') ?>">
                        </div>
                    </div>
                    <div class="mt-4 d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/reports/schedules" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: modules\reports\views\schedules\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-clock me-2"></i><?= lang('reports.scheduled_reports') ?></h4>
    <a href="/reports/schedules/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('common.create') ?></a>
</div>

<div class="card">
    <div class="card-body">
        <table id="schedules-table" class="table table-striped table-hover w-100"
               data-url="/api/reports/schedules/datatable"
               data-columns='<?= e(json_encode([
                   ["data" => "id", "title" => "#"],
                   ["data" => "schedule_name", "title" => "Name"],
                   ["data" => "report_name_en", "title" => "Report"],
                   ["data" => "frequency", "title" => "Frequency"],
                   ["data" => "is_active", "title" => "Active"],
                   ["data" => "last_run_at", "title" => "Last Run"],
                   ["data" => "next_run_at", "title" => "Next Run"],
                   ["data" => "id", "title" => "Actions", "orderable" => false, "searchable" => false]
               ])) ?>'>
            <thead>
                <tr>
                    <th>#</th>
                    <th>Name</th>
                    <th>Report</th>
                    <th>Frequency</th>
                    <th>Active</th>
                    <th>Last Run</th>
                    <th>Next Run</th>
                    <th>Actions</th>
                </tr>
            </thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function() {
    var csrf = $('meta[name="csrf-token"]').attr('content');

    var table = $('#schedules-table').DataTable({
        processing: true,
        serverSide: true,
        ajax: { url: '/api/reports/schedules/datatable', type: 'GET' },
        columns: [
            { data: 'id' },
            { data: 'schedule_name' },
            { data: 'report_name_en' },
            { data: 'frequency', render: function(d) { return '<span class="badge bg-info">'+d+'</span>'; } },
            { data: 'is_active', render: function(d,t,r) { return '<div class="form-check form-switch"><input class="form-check-input toggle-active" type="checkbox" data-id="'+r.id+'" '+(d==1?'checked':'')+'></div>'; } },
            { data: 'last_run_at', render: function(d) { return d || '<span class="text-muted">Never</span>'; } },
            { data: 'next_run_at', render: function(d) { return d || '-'; } },
            { data: 'id', orderable: false, render: function(d) {
                return '<div class="btn-group btn-group-sm">'+
                    '<a href="/reports/schedules/'+d+'/edit" class="btn btn-outline-primary" title="Edit"><i class="fas fa-edit"></i></a>'+
                    '<button class="btn btn-outline-danger btn-delete" data-id="'+d+'" title="Delete"><i class="fas fa-trash"></i></button>'+
                '</div>';
            }}
        ],
        order: [[0, 'desc']],
        pageLength: 25
    });

    $(document).on('change', '.toggle-active', function() {
        var id = $(this).data('id');
        $.ajax({ url: '/api/reports/schedules/'+id+'/toggle', type: 'POST', headers: {'X-CSRF-TOKEN': csrf, 'X-Requested-With':'XMLHttpRequest'}, success: function() { table.ajax.reload(null, false); } });
    });

    $(document).on('click', '.btn-delete', function() {
        var id = $(this).data('id');
        Swal.fire({ title: 'Delete?', text: 'This schedule will be removed', icon: 'warning', showCancelButton: true, confirmButtonColor: '#e53e3e', confirmButtonText: 'Delete' }).then(function(r) {
            if (r.isConfirmed) {
                $.ajax({ url: '/api/reports/schedules/'+id+'/delete', type: 'POST', headers: {'X-CSRF-TOKEN': csrf, 'X-Requested-With':'XMLHttpRequest'}, success: function() { table.ajax.reload(null, false); } });
            }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\reports\views\view.php

```php
<?php
$lang = Lang::current();
$reportName = $report['report_name_' . $lang] ?: $report['report_name_en'];
$columns = json_decode($report['default_columns'] ?? '[]', true) ?: [];
$chartType = $report['chart_type'] ?? null;
$chartConfig = json_decode($report['chart_config'] ?? '{}', true) ?: [];
?>

<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h4 class="mb-1"><?= e($reportName) ?></h4>
        <p class="text-muted mb-0"><?= e($report['description_' . $lang] ?: $report['description_en'] ?: '') ?></p>
    </div>
    <div class="d-flex gap-2">
        <button class="btn btn-outline-warning btn-sm" id="btn-save-favorite" title="Save as Favorite"><i class="fas fa-star me-1"></i>Favorite</button>
        <a href="/reports" class="btn btn-outline-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i><?= lang('common.back') ?></a>
    </div>
</div>

<!-- Parameters -->
<?php if (!empty($parameters)): ?>
<div class="card mb-4">
    <div class="card-header"><h6 class="mb-0"><i class="fas fa-filter me-2"></i><?= lang('reports.parameters') ?></h6></div>
    <div class="card-body">
        <form id="report-params-form">
            <div class="row g-3">
                <?php foreach ($parameters as $param):
                    $pName = $param['parameter_name'];
                    $pLabel = $param['parameter_label_' . $lang] ?: $param['parameter_label_en'];
                    $pType = $param['parameter_type'];
                    $pOpts = json_decode($param['options'] ?? '[]', true) ?: [];
                    $pDefault = $param['default_value'] ?? '';
                ?>
                <div class="col-md-3">
                    <label class="form-label"><?= e($pLabel) ?></label>
                    <?php if ($pType === 'select'): ?>
                        <select name="<?= e($pName) ?>" class="form-select form-select-sm">
                            <?php if (isset($pOpts['source'])): ?>
                                <option value="">-- All --</option>
                            <?php else: ?>
                                <?php foreach ($pOpts as $opt): ?>
                                <option value="<?= e($opt['value'] ?? '') ?>"><?= e($opt['label'] ?? $opt['value'] ?? '') ?></option>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </select>
                    <?php elseif ($pType === 'date'): ?>
                        <input type="date" name="<?= e($pName) ?>" class="form-control form-control-sm" value="<?= e($pDefault) ?>">
                    <?php elseif ($pType === 'number'): ?>
                        <input type="number" name="<?= e($pName) ?>" class="form-control form-control-sm" value="<?= e($pDefault) ?>">
                    <?php elseif ($pType === 'boolean'): ?>
                        <select name="<?= e($pName) ?>" class="form-select form-select-sm">
                            <option value="">--</option>
                            <option value="1">Yes</option>
                            <option value="0">No</option>
                        </select>
                    <?php else: ?>
                        <input type="text" name="<?= e($pName) ?>" class="form-control form-control-sm" value="<?= e($pDefault) ?>">
                    <?php endif; ?>
                </div>
                <?php endforeach; ?>
                <div class="col-md-3 d-flex align-items-end">
                    <button type="submit" class="btn btn-primary btn-sm me-2"><i class="fas fa-play me-1"></i><?= lang('reports.generate') ?></button>
                    <button type="reset" class="btn btn-outline-secondary btn-sm"><i class="fas fa-redo me-1"></i>Reset</button>
                </div>
            </div>
        </form>
    </div>
</div>
<?php endif; ?>

<!-- Chart Area -->
<?php if ($chartType): ?>
<div class="card mb-4" id="chart-card" style="display:none">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h6 class="mb-0"><i class="fas fa-chart-pie me-2"></i>Chart</h6>
        <button class="btn btn-sm btn-outline-secondary" id="toggle-chart"><i class="fas fa-eye-slash"></i></button>
    </div>
    <div class="card-body">
        <div style="max-height:350px;position:relative">
            <canvas id="report-chart"></canvas>
        </div>
    </div>
</div>
<?php endif; ?>

<!-- Export Buttons -->
<div class="mb-3 d-flex gap-2">
    <button class="btn btn-sm btn-outline-success" id="btn-export-csv"><i class="fas fa-file-csv me-1"></i>CSV</button>
    <button class="btn btn-sm btn-outline-primary" id="btn-export-excel"><i class="fas fa-file-excel me-1"></i>Excel</button>
    <button class="btn btn-sm btn-outline-danger" id="btn-export-print"><i class="fas fa-print me-1"></i>Print</button>
</div>

<!-- Results Table -->
<div class="card">
    <div class="card-body">
        <div class="table-responsive">
            <table id="report-results-table" class="table table-striped table-hover w-100">
                <thead>
                    <tr>
                        <?php foreach ($columns as $col): ?>
                        <th><?= e($col['label'] ?? $col['key'] ?? '') ?></th>
                        <?php endforeach; ?>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
        </div>
    </div>
</div>

<!-- Favorite Modal -->
<div class="modal fade" id="favoriteModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header"><h5 class="modal-title"><i class="fas fa-star me-2"></i>Save Favorite</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <form id="favorite-form" data-ajax="true" action="/api/reports/favorites" method="POST">
                    <?= csrf_field() ?>
                    <input type="hidden" name="report_id" value="<?= (int)$report['id'] ?>">
                    <input type="hidden" name="saved_parameters" id="fav-params-json">
                    <div class="mb-3">
                        <label class="form-label">Favorite Name</label>
                        <input type="text" name="favorite_name" class="form-control" required>
                        <div class="invalid-feedback" data-field="favorite_name"></div>
                    </div>
                    <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
(function() {
    var reportId = <?= (int)$report['id'] ?>;
    var columns = <?= json_encode($columns) ?>;
    var chartType = <?= json_encode($chartType) ?>;
    var chartConfig = <?= json_encode($chartConfig) ?>;
    var dtColumns = [];
    var chartInstance = null;

    columns.forEach(function(c) {
        var def = { data: c.key, title: c.label || c.key, orderable: true };
        if (c.key === 'status') {
            def.render = function(data) {
                if (!data) return '';
                var cls = {'active':'success','pending':'warning','suspended':'danger','frozen':'info','completed':'success','expired':'secondary','overdue':'danger','closed':'secondary','resolved':'success'};
                return '<span class="badge bg-'+(cls[data]||'secondary')+'">'+data.replace(/_/g,' ')+'</span>';
            };
        }
        if (c.key && (c.key.indexOf('amount') !== -1 || c.key.indexOf('balance') !== -1 || c.key.indexOf('paid') !== -1 || c.key.indexOf('invoiced') !== -1 || c.key.indexOf('overdue_amount') !== -1)) {
            def.render = function(data) { return data !== null && data !== '' ? parseFloat(data).toLocaleString('en', {minimumFractionDigits:2, maximumFractionDigits:2}) : '0.00'; };
        }
        dtColumns.push(def);
    });

    var table = $('#report-results-table').DataTable({
        processing: true,
        serverSide: true,
        ajax: {
            url: '/api/reports/execute/' + reportId,
            type: 'POST',
            data: function(d) {
                d.params = {};
                $('#report-params-form').serializeArray().forEach(function(item) {
                    if (item.value) d.params[item.name] = item.value;
                });
                return d;
            },
            beforeSend: function(xhr) {
                xhr.setRequestHeader('X-CSRF-TOKEN', $('meta[name="csrf-token"]').attr('content'));
                xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
            }
        },
        columns: dtColumns,
        order: [[0, 'asc']],
        pageLength: 25,
        lengthMenu: [10, 25, 50, 100],
        language: { emptyTable: 'No data â€” adjust parameters and generate', processing: '<div class="spinner-border spinner-border-sm"></div>' },
        drawCallback: function(settings) {
            if (chartType && settings.json && settings.json.data) {
                buildChart(settings.json.data);
            }
        }
    });

    $('#report-params-form').on('submit', function(e) {
        e.preventDefault();
        table.ajax.reload();
    });

    function getParams() {
        var p = {};
        $('#report-params-form').serializeArray().forEach(function(item) {
            if (item.value) p[item.name] = item.value;
        });
        return p;
    }

    function exportReport(format) {
        var form = $('<form method="POST" action="/api/reports/export/' + reportId + '"></form>');
        form.append('<input type="hidden" name="_token" value="' + $('meta[name="csrf-token"]').attr('content') + '">');
        form.append('<input type="hidden" name="format" value="' + format + '">');
        var params = getParams();
        for (var k in params) {
            form.append('<input type="hidden" name="params[' + k + ']" value="' + params[k] + '">');
        }
        $('body').append(form);
        form.submit();
        form.remove();
    }

    $('#btn-export-csv').on('click', function() { exportReport('csv'); });
    $('#btn-export-excel').on('click', function() { exportReport('excel'); });
    $('#btn-export-print').on('click', function() { window.print(); });

    $('#btn-save-favorite').on('click', function() {
        $('#fav-params-json').val(JSON.stringify(getParams()));
        new bootstrap.Modal('#favoriteModal').show();
    });

    $('#favorite-form').on('submit', function(e) {
        e.preventDefault();
        var fd = new FormData(this);
        $.ajax({
            url: $(this).attr('action'),
            type: 'POST',
            data: fd,
            processData: false,
            contentType: false,
            headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'), 'X-Requested-With': 'XMLHttpRequest'},
            success: function(res) {
                if (res.success) {
                    bootstrap.Modal.getInstance(document.getElementById('favoriteModal')).hide();
                    Swal.fire({icon:'success',title:'Saved!',timer:1500,showConfirmButton:false,toast:true,position:'top-end'});
                }
            }
        });
    });

    function buildChart(data) {
        if (!chartType || !data.length) return;
        $('#chart-card').show();
        var canvas = document.getElementById('report-chart');
        if (!canvas) return;

        if (chartInstance) chartInstance.destroy();

        var groupCol = chartConfig.group_column || chartConfig.label_column || (columns[0] ? columns[0].key : '');
        var valueCol = chartConfig.value_column || (columns.length > 1 ? columns[columns.length - 1].key : '');

        var grouped = {};
        data.forEach(function(row) {
            var label = row[groupCol] || 'Unknown';
            var val = parseFloat(row[valueCol]) || 1;
            grouped[label] = (grouped[label] || 0) + val;
        });

        var labels = Object.keys(grouped);
        var values = Object.values(grouped);
        var colors = ['#2c5282','#38a169','#dd6b20','#e53e3e','#805ad5','#d69e2e','#319795','#ed64a6','#718096','#4299e1'];

        chartInstance = new Chart(canvas, {
            type: chartType === 'doughnut' ? 'doughnut' : (chartType === 'pie' ? 'pie' : 'bar'),
            data: {
                labels: labels,
                datasets: [{
                    data: values,
                    backgroundColor: colors.slice(0, labels.length),
                    borderWidth: 1
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: { legend: { position: chartType === 'bar' ? 'top' : 'right' } }
            }
        });
    }

    $('#toggle-chart').on('click', function() {
        var body = $(this).closest('.card').find('.card-body');
        body.toggle();
        $(this).find('i').toggleClass('fa-eye-slash fa-eye');
    });

    // Load dynamic select options
    <?php foreach ($parameters as $param):
        $opts = json_decode($param['options'] ?? '[]', true) ?: [];
        if (isset($opts['source'])):
            $source = $opts['source'];
    ?>
    (function() {
        var selectEl = $('select[name="<?= e($param['parameter_name']) ?>"]');
        $.get('/api/reports/param-options?source=<?= e($source) ?>', function(res) {
            if (res && res.data) {
                res.data.forEach(function(item) {
                    selectEl.append('<option value="'+item.id+'">'+item.name+'</option>');
                });
            }
        }).fail(function() {
            // Load from DB directly via simple query
        });
    })();
    <?php endif; endforeach; ?>

    // Try to load select options from known sources
    $('select[name="membership_type_id"]').each(function() {
        var sel = $(this);
        if (sel.children().length <= 1) {
            $.get('/api/members/search?type=membership_types', function(res) {}).fail(function(){});
        }
    });
})();
</script>
<?php $__view->endPush() ?>
```

## File: modules\reports\views\widgets\kpi_summary.php

```php
<?php
try {
    $db = Database::getInstance();
    $activeMembers = (int) $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status='active' AND is_deleted=0");
    $pendingApps = (int) $db->fetchColumn("SELECT COUNT(*) FROM applications WHERE status NOT IN ('approved','rejected','cancelled','expired') AND is_deleted=0");
    $overdueInvoices = (int) $db->fetchColumn("SELECT COUNT(*) FROM invoices WHERE status IN ('issued','partially_paid','overdue') AND due_date < CURDATE() AND balance_due > 0 AND is_deleted=0");
    $todayRevenue = (float) $db->fetchColumn("SELECT COALESCE(SUM(amount),0) FROM payments WHERE status='completed' AND payment_date=CURDATE() AND is_deleted=0");
} catch (\Exception $e) {
    $activeMembers = $pendingApps = $overdueInvoices = 0;
    $todayRevenue = 0;
}
?>
<div class="row g-3">
    <div class="col-md-3">
        <div class="card border-primary h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('reports.active_members') ?></h6>
                        <h3 class="mb-0"><?= number_format($activeMembers) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-users fa-2x text-primary opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-warning h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('reports.pending_applications') ?></h6>
                        <h3 class="mb-0"><?= number_format($pendingApps) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-file-alt fa-2x text-warning opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('reports.overdue_invoices') ?></h6>
                        <h3 class="mb-0"><?= number_format($overdueInvoices) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-exclamation-circle fa-2x text-danger opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-success h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1"><?= lang('reports.today_revenue') ?></h6>
                        <h3 class="mb-0"><?= number_format($todayRevenue, 2) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-money-bill-wave fa-2x text-success opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
</div>
```

## File: modules\reports\views\widgets\member_growth.php

```php
<div class="card h-100">
    <div class="card-header"><h6 class="mb-0"><i class="fas fa-user-plus me-2"></i><?= lang('reports.member_growth') ?></h6></div>
    <div class="card-body">
        <canvas id="widget-growth-chart" height="200"></canvas>
    </div>
</div>
<script>
(function() {
    function loadWidget() {
        $.get('/api/reports/member-growth', function(res) {
            if (!res.success) return;
            var ctx = document.getElementById('widget-growth-chart');
            if (!ctx) return;
            new Chart(ctx, {
                type: 'bar',
                data: { labels: res.labels, datasets: [{label: 'New Members', data: res.values, backgroundColor: '#38a169', borderRadius: 4}] },
                options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } } }, plugins: { legend: { display: false } } }
            });
        });
    }
    if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', loadWidget); } else { loadWidget(); }
})();
</script>
```

## File: modules\reports\views\widgets\revenue_chart.php

```php
<div class="card h-100">
    <div class="card-header"><h6 class="mb-0"><i class="fas fa-chart-line me-2"></i><?= lang('reports.revenue_trend') ?></h6></div>
    <div class="card-body">
        <canvas id="widget-revenue-chart" height="200"></canvas>
    </div>
</div>
<script>
(function() {
    function loadWidget() {
        $.get('/api/reports/revenue-chart?days=14', function(res) {
            if (!res.success) return;
            var ctx = document.getElementById('widget-revenue-chart');
            if (!ctx) return;
            new Chart(ctx, {
                type: 'line',
                data: { labels: res.labels, datasets: [{label: 'Revenue', data: res.values, borderColor: '#2c5282', backgroundColor: 'rgba(44,82,130,0.08)', fill: true, tension: 0.4, pointRadius: 2}] },
                options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, ticks: { callback: function(v) { return v.toLocaleString(); } } } }, plugins: { legend: { display: false } } }
            });
        });
    }
    if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', loadWidget); } else { loadWidget(); }
})();
</script>
```

## File: modules\sports\Controllers\AchievementController.php

```php
<?php

class AchievementController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('achievements/index', [
            'title' => lang('sports.achievements'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.achievements')]],
        ]);
    }

    public function create(Request $request): void
    {
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->view('achievements/create', [
            'title' => lang('sports.create_achievement'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.achievements'), 'url' => '/sports/achievements'], ['label' => lang('common.create')]],
            'sports' => $sports,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'member_id' => 'required|integer|exists:members,id',
            'sport_id' => 'required|integer|exists:sports,id',
            'achievement_type' => 'required|in:medal,trophy,certificate,record,award,ranking,other',
            'title_en' => 'required|string|max:300',
            'title_ar' => 'nullable|string|max:300',
            'description' => 'nullable|string',
            'achievement_date' => 'required|date',
            'competition_name' => 'nullable|string|max:300',
            'position' => 'nullable|integer',
            'medal_type' => 'nullable|in:gold,silver,bronze',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['verified'] = 0;
        $id = $this->db()->insert('sport_member_achievements', $data);
        $this->audit('create', 'sport_member_achievement', $id, "Created achievement: {$data['title_en']}");
        $this->json(['success' => true, 'message' => 'Achievement recorded', 'redirect' => '/sports/achievements']);
    }
}
```

## File: modules\sports\Controllers\ClassController.php

```php
<?php

class ClassController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $statuses = ['planned','enrollment_open','active','full','completed','cancelled'];

        $this->view('classes/index', [
            'title' => lang('sports.classes'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.classes')]],
            'sports' => $sports,
            'statuses' => $statuses,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $coaches = $db->fetchAll("SELECT id, CONCAT(first_name_en,' ',last_name_en) as name FROM sport_coaches WHERE status = 'active' ORDER BY first_name_en");
        $facilities = $db->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $seasons = $db->fetchAll("SELECT ss.id, CONCAT(sp.name_en, ' - ', ss.name_en) as name FROM sport_seasons ss LEFT JOIN sports sp ON ss.sport_id = sp.id WHERE ss.status IN ('upcoming','registration','active') ORDER BY ss.start_date DESC");

        $this->view('classes/create', [
            'title' => lang('sports.create_class'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.classes'), 'url' => '/sports/classes'], ['label' => lang('common.create')]],
            'sports' => $sports, 'coaches' => $coaches, 'facilities' => $facilities, 'seasons' => $seasons,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'class_code' => 'required|string|max:20|unique:sport_classes,class_code',
            'sport_id' => 'required|integer|exists:sports,id',
            'name_en' => 'required|string|max:300',
            'name_ar' => 'nullable|string|max:300',
            'coach_id' => 'nullable|integer',
            'facility_id' => 'nullable|integer',
            'class_type' => 'required|in:beginner,intermediate,advanced,all_levels,kids,youth,adult,senior',
            'max_participants' => 'required|integer|min:1',
            'min_participants' => 'nullable|integer',
            'fee_per_session' => 'nullable|decimal',
            'fee_per_month' => 'nullable|decimal',
            'fee_per_term' => 'nullable|decimal',
            'session_duration_minutes' => 'nullable|integer',
            'gender_restriction' => 'required|in:male,female,both',
            'min_age' => 'nullable|integer',
            'max_age' => 'nullable|integer',
            'season_id' => 'nullable|integer',
            'start_date' => 'nullable|date',
            'end_date' => 'nullable|date',
            'status' => 'required|in:planned,enrollment_open,active,full,completed,cancelled',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['is_active'] = 1;
        $data['min_participants'] = $data['min_participants'] ?? 1;
        $data['session_duration_minutes'] = $data['session_duration_minutes'] ?? 60;
        $data['fee_per_session'] = $data['fee_per_session'] ?? 0;
        $data['fee_per_month'] = $data['fee_per_month'] ?? 0;
        $data['fee_per_term'] = $data['fee_per_term'] ?? 0;

        $id = $this->db()->insert('sport_classes', $data);

        // Save schedules
        $days = $request->post('schedule_day', []);
        $startTimes = $request->post('schedule_start', []);
        $endTimes = $request->post('schedule_end', []);
        if (is_array($days)) {
            for ($i = 0; $i < count($days); $i++) {
                if (!empty($days[$i]) && !empty($startTimes[$i]) && !empty($endTimes[$i])) {
                    $this->db()->insert('sport_class_schedules', [
                        'class_id' => $id,
                        'day_of_week' => (int)$days[$i],
                        'start_time' => $startTimes[$i],
                        'end_time' => $endTimes[$i],
                        'facility_id' => $data['facility_id'],
                        'coach_id' => $data['coach_id'],
                        'is_active' => 1,
                    ]);
                }
            }
        }

        $this->audit('create', 'sport_class', $id, "Created class: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Class created', 'redirect' => '/sports/classes/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $class = $db->fetchOne(
            "SELECT cl.*, sp.name_en as sport_name, CONCAT(co.first_name_en,' ',co.last_name_en) as coach_name, f.name_en as facility_name
             FROM sport_classes cl LEFT JOIN sports sp ON cl.sport_id = sp.id LEFT JOIN sport_coaches co ON cl.coach_id = co.id LEFT JOIN facilities f ON cl.facility_id = f.id
             WHERE cl.id = ?", [$id]);
        if (!$class) { $this->abort(404); return; }

        $schedules = $db->fetchAll("SELECT * FROM sport_class_schedules WHERE class_id = ? AND is_active = 1 ORDER BY day_of_week", [$id]);
        $enrollments = $db->fetchAll(
            "SELECT e.*, m.full_name_en as member_name, m.membership_number, d.full_name_en as dependent_name
             FROM sport_class_enrollments e LEFT JOIN members m ON e.member_id = m.id LEFT JOIN member_dependents d ON e.dependent_id = d.id
             WHERE e.class_id = ? ORDER BY e.enrollment_date DESC", [$id]);
        $waitlist = $db->fetchAll(
            "SELECT w.*, m.full_name_en as member_name, m.membership_number
             FROM sport_class_waitlist w LEFT JOIN members m ON w.member_id = m.id
             WHERE w.class_id = ? AND w.status = 'waiting' ORDER BY w.queue_position", [$id]);

        $enrolledCount = $db->fetchColumn("SELECT COUNT(*) FROM sport_class_enrollments WHERE class_id = ? AND status IN ('enrolled','active')", [$id]);

        $this->view('classes/show', [
            'title' => $class['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.classes'), 'url' => '/sports/classes'], ['label' => $class['name_en']]],
            'class' => $class, 'schedules' => $schedules, 'enrollments' => $enrollments, 'waitlist' => $waitlist, 'enrolledCount' => $enrolledCount,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $class = $db->fetchOne("SELECT * FROM sport_classes WHERE id = ?", [$id]);
        if (!$class) { $this->abort(404); return; }

        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $coaches = $db->fetchAll("SELECT id, CONCAT(first_name_en,' ',last_name_en) as name FROM sport_coaches WHERE status = 'active' ORDER BY first_name_en");
        $facilities = $db->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $schedules = $db->fetchAll("SELECT * FROM sport_class_schedules WHERE class_id = ? AND is_active = 1 ORDER BY day_of_week", [$id]);

        $this->view('classes/edit', [
            'title' => 'Edit: ' . $class['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.classes'), 'url' => '/sports/classes'], ['label' => lang('common.edit')]],
            'class' => $class, 'sports' => $sports, 'coaches' => $coaches, 'facilities' => $facilities, 'schedules' => $schedules,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:300',
            'name_ar' => 'nullable|string|max:300',
            'coach_id' => 'nullable|integer',
            'facility_id' => 'nullable|integer',
            'class_type' => 'required|in:beginner,intermediate,advanced,all_levels,kids,youth,adult,senior',
            'max_participants' => 'required|integer|min:1',
            'fee_per_month' => 'nullable|decimal',
            'status' => 'required|in:planned,enrollment_open,active,full,completed,cancelled',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['fee_per_month'] = $data['fee_per_month'] ?? 0;
        $this->db()->update('sport_classes', $data, 'id = ?', [$id]);
        $this->audit('update', 'sport_class', $id, "Updated class: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Class updated', 'redirect' => '/sports/classes/' . $id]);
    }

    public function enroll(Request $request): void
    {
        $classId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $memberId = (int) $request->post('member_id');
        $dependentId = $request->post('dependent_id') ? (int) $request->post('dependent_id') : null;
        $db = $this->db();

        $class = $db->fetchOne("SELECT * FROM sport_classes WHERE id = ?", [$classId]);
        if (!$class) { $this->json(['success' => false, 'message' => 'Class not found'], 404); return; }

        $enrolledCount = $db->fetchColumn("SELECT COUNT(*) FROM sport_class_enrollments WHERE class_id = ? AND status IN ('enrolled','active')", [$classId]);
        if ($enrolledCount >= $class['max_participants']) {
            $maxPos = $db->fetchColumn("SELECT COALESCE(MAX(queue_position),0) FROM sport_class_waitlist WHERE class_id = ?", [$classId]);
            $db->insert('sport_class_waitlist', [
                'class_id' => $classId, 'member_id' => $memberId, 'dependent_id' => $dependentId,
                'queue_position' => $maxPos + 1, 'status' => 'waiting',
            ]);
            $this->audit('create', 'sport_class_waitlist', null, "Added to waitlist for class #{$classId}");
            $this->json(['success' => true, 'message' => 'Class full â€” added to waitlist']);
            return;
        }

        $feeAmount = $class['fee_per_month'] ?: 0;
        $db->insert('sport_class_enrollments', [
            'class_id' => $classId, 'member_id' => $memberId, 'dependent_id' => $dependentId,
            'enrollment_date' => date('Y-m-d'), 'start_date' => date('Y-m-d'),
            'status' => 'enrolled', 'fee_type' => 'monthly', 'fee_amount' => $feeAmount,
            'created_by' => auth_id(),
        ]);
        $this->audit('create', 'sport_class_enrollment', null, "Enrolled member #{$memberId} in class #{$classId}");
        $this->json(['success' => true, 'message' => 'Member enrolled successfully', 'redirect' => '/sports/classes/' . $classId]);
    }

    public function withdraw(Request $request): void
    {
        $classId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $enrollmentId = (int) $request->post('enrollment_id');
        $this->db()->update('sport_class_enrollments', [
            'status' => 'withdrawn', 'withdrawn_date' => date('Y-m-d'),
            'withdrawal_reason' => $request->post('reason', 'Withdrawn by staff'),
        ], 'id = ? AND class_id = ?', [$enrollmentId, $classId]);
        $this->audit('update', 'sport_class_enrollment', $enrollmentId, "Withdrawn from class #{$classId}");
        $this->json(['success' => true, 'message' => 'Member withdrawn', 'redirect' => '/sports/classes/' . $classId]);
    }

    public function attendance(Request $request): void
    {
        $classId = (int) $request->routeParam('id');
        $db = $this->db();
        $class = $db->fetchOne("SELECT cl.*, sp.name_en as sport_name FROM sport_classes cl LEFT JOIN sports sp ON cl.sport_id = sp.id WHERE cl.id = ?", [$classId]);
        if (!$class) { $this->abort(404); return; }

        $sessionDate = $request->get('date', date('Y-m-d'));
        $enrollments = $db->fetchAll(
            "SELECT e.*, m.full_name_en as member_name, m.membership_number, d.full_name_en as dependent_name
             FROM sport_class_enrollments e LEFT JOIN members m ON e.member_id = m.id LEFT JOIN member_dependents d ON e.dependent_id = d.id
             WHERE e.class_id = ? AND e.status IN ('enrolled','active') ORDER BY m.full_name_en", [$classId]);

        $existing = $db->fetchAll("SELECT * FROM sport_class_attendance WHERE class_id = ? AND session_date = ?", [$classId, $sessionDate]);
        $attendanceMap = [];
        foreach ($existing as $att) { $attendanceMap[$att['enrollment_id']] = $att; }

        $this->view('classes/attendance', [
            'title' => 'Attendance: ' . $class['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.classes'), 'url' => '/sports/classes'], ['label' => $class['name_en'], 'url' => '/sports/classes/' . $classId], ['label' => 'Attendance']],
            'class' => $class, 'enrollments' => $enrollments, 'sessionDate' => $sessionDate, 'attendanceMap' => $attendanceMap,
        ]);
    }

    public function saveAttendance(Request $request): void
    {
        $classId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $sessionDate = $request->post('session_date', date('Y-m-d'));
        $sessionStart = $request->post('session_start', '00:00');
        $statuses = $request->post('attendance', []);
        $db = $this->db();

        $db->delete('sport_class_attendance', 'class_id = ? AND session_date = ?', [$classId, $sessionDate]);

        foreach ($statuses as $enrollmentId => $status) {
            if (!in_array($status, ['present','absent','late','excused','cancelled'])) continue;
            $db->insert('sport_class_attendance', [
                'class_id' => $classId, 'enrollment_id' => (int) $enrollmentId,
                'session_date' => $sessionDate, 'session_start' => $sessionStart,
                'status' => $status, 'recorded_by' => auth_id(),
            ]);
        }

        $this->audit('create', 'sport_class_attendance', null, "Recorded attendance for class #{$classId} on {$sessionDate}");
        $this->json(['success' => true, 'message' => 'Attendance saved', 'redirect' => '/sports/classes/' . $classId . '/attendance?date=' . $sessionDate]);
    }
}
```

## File: modules\sports\Controllers\CoachController.php

```php
<?php

class CoachController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('coaches/index', [
            'title' => lang('sports.coaches'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.coaches')]],
        ]);
    }

    public function create(Request $request): void
    {
        $this->view('coaches/create', [
            'title' => lang('sports.create_coach'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.coaches'), 'url' => '/sports/coaches'], ['label' => lang('common.create')]],
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'coach_code' => 'required|string|max:20|unique:sport_coaches,coach_code',
            'first_name_en' => 'required|string|max:100',
            'last_name_en' => 'required|string|max:100',
            'first_name_ar' => 'nullable|string|max:100',
            'last_name_ar' => 'nullable|string|max:100',
            'phone' => 'nullable|string|max:20',
            'email' => 'nullable|email|max:191',
            'experience_years' => 'nullable|integer',
            'hourly_rate' => 'nullable|decimal',
            'status' => 'required|in:active,inactive,on_leave,terminated',
            'hire_date' => 'nullable|date',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();

        if ($request->file('photo') && $request->file('photo')['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($request->file('photo'));
            $upload->setAllowedTypes(['jpg','jpeg','png'])->setMaxSize(5*1024*1024)->setDestination('uploads/coaches/');
            if ($upload->validate()) { $data['photo_path'] = $upload->save(); }
        }

        $id = $this->db()->insert('sport_coaches', $data);
        $this->audit('create', 'sport_coach', $id, "Created coach: {$data['first_name_en']} {$data['last_name_en']}");
        $this->json(['success' => true, 'message' => 'Coach created', 'redirect' => '/sports/coaches']);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $coach = $db->fetchOne("SELECT * FROM sport_coaches WHERE id = ?", [$id]);
        if (!$coach) { $this->abort(404); return; }

        $certs = $db->fetchAll("SELECT * FROM sport_coach_certifications WHERE coach_id = ? ORDER BY issue_date DESC", [$id]);
        $classes = $db->fetchAll("SELECT cl.*, sp.name_en as sport_name FROM sport_classes cl LEFT JOIN sports sp ON cl.sport_id = sp.id WHERE cl.coach_id = ? AND cl.is_active = 1 ORDER BY cl.name_en", [$id]);

        $this->view('coaches/show', [
            'title' => $coach['first_name_en'] . ' ' . $coach['last_name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.coaches'), 'url' => '/sports/coaches'], ['label' => $coach['first_name_en']]],
            'coach' => $coach, 'certifications' => $certs, 'classes' => $classes,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $coach = $this->db()->fetchOne("SELECT * FROM sport_coaches WHERE id = ?", [$id]);
        if (!$coach) { $this->abort(404); return; }
        $this->view('coaches/edit', [
            'title' => 'Edit: ' . $coach['first_name_en'] . ' ' . $coach['last_name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.coaches'), 'url' => '/sports/coaches'], ['label' => lang('common.edit')]],
            'coach' => $coach,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'first_name_en' => 'required|string|max:100',
            'last_name_en' => 'required|string|max:100',
            'first_name_ar' => 'nullable|string|max:100',
            'last_name_ar' => 'nullable|string|max:100',
            'phone' => 'nullable|string|max:20',
            'email' => 'nullable|email|max:191',
            'experience_years' => 'nullable|integer',
            'hourly_rate' => 'nullable|decimal',
            'status' => 'required|in:active,inactive,on_leave,terminated',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();

        if ($request->file('photo') && $request->file('photo')['error'] === UPLOAD_ERR_OK) {
            $upload = new FileUpload($request->file('photo'));
            $upload->setAllowedTypes(['jpg','jpeg','png'])->setMaxSize(5*1024*1024)->setDestination('uploads/coaches/');
            if ($upload->validate()) { $data['photo_path'] = $upload->save(); }
        }

        $this->db()->update('sport_coaches', $data, 'id = ?', [$id]);
        $this->audit('update', 'sport_coach', $id, "Updated coach: {$data['first_name_en']} {$data['last_name_en']}");
        $this->json(['success' => true, 'message' => 'Coach updated', 'redirect' => '/sports/coaches']);
    }
}
```

## File: modules\sports\Controllers\EquipmentController.php

```php
<?php

class EquipmentController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('equipment/index', [
            'title' => lang('sports.equipment'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.equipment')]],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $facilities = $db->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $this->view('equipment/create', [
            'title' => lang('sports.create_equipment'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.equipment'), 'url' => '/sports/equipment'], ['label' => lang('common.create')]],
            'sports' => $sports, 'facilities' => $facilities,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'equipment_code' => 'required|string|max:20|unique:sport_equipment,equipment_code',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'sport_id' => 'nullable|integer',
            'facility_id' => 'nullable|integer',
            'category' => 'nullable|string|max:50',
            'brand' => 'nullable|string|max:100',
            'quantity_total' => 'required|integer|min:1',
            'condition_status' => 'required|in:new,good,fair,poor,damaged,retired',
            'is_rentable' => 'nullable|boolean',
            'rental_fee_per_hour' => 'nullable|decimal',
            'deposit_amount' => 'nullable|decimal',
            'purchase_price' => 'nullable|decimal',
            'purchase_date' => 'nullable|date',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['quantity_available'] = $data['quantity_total'];
        $data['is_rentable'] = !empty($data['is_rentable']) ? 1 : 0;
        $data['is_active'] = 1;
        $id = $this->db()->insert('sport_equipment', $data);
        $this->audit('create', 'sport_equipment', $id, "Created equipment: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Equipment added', 'redirect' => '/sports/equipment']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $equip = $this->db()->fetchOne("SELECT * FROM sport_equipment WHERE id = ?", [$id]);
        if (!$equip) { $this->abort(404); return; }
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $facilities = $this->db()->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $this->view('equipment/edit', [
            'title' => 'Edit: ' . $equip['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.equipment'), 'url' => '/sports/equipment'], ['label' => lang('common.edit')]],
            'equipment' => $equip, 'sports' => $sports, 'facilities' => $facilities,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'quantity_total' => 'required|integer|min:1',
            'condition_status' => 'required|in:new,good,fair,poor,damaged,retired',
            'is_rentable' => 'nullable|boolean',
            'rental_fee_per_hour' => 'nullable|decimal',
            'is_active' => 'nullable|boolean',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['is_rentable'] = !empty($data['is_rentable']) ? 1 : 0;
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;
        $this->db()->update('sport_equipment', $data, 'id = ?', [$id]);
        $this->audit('update', 'sport_equipment', $id, 'Updated equipment');
        $this->json(['success' => true, 'message' => 'Equipment updated', 'redirect' => '/sports/equipment']);
    }

    public function rentals(Request $request): void
    {
        $equipment = $this->db()->fetchAll("SELECT id, name_en, equipment_code FROM sport_equipment WHERE is_rentable = 1 AND is_active = 1 AND quantity_available > 0 ORDER BY name_en");
        $this->view('equipment/rentals', [
            'title' => lang('sports.equipment_rentals'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.equipment'), 'url' => '/sports/equipment'], ['label' => lang('sports.rentals')]],
            'equipment' => $equipment,
        ]);
    }

    public function storeRental(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $rentalNumber = SequenceGenerator::next('rental_number');
        $validator = new Validator($request->all(), [
            'equipment_id' => 'required|integer|exists:sport_equipment,id',
            'member_id' => 'required|integer|exists:members,id',
            'quantity' => 'required|integer|min:1',
            'rental_end_expected' => 'required|datetime',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $equip = $this->db()->fetchOne("SELECT * FROM sport_equipment WHERE id = ?", [$data['equipment_id']]);
        if ((int)$data['quantity'] > (int)$equip['quantity_available']) {
            $this->json(['success' => false, 'message' => 'Insufficient stock. Available: ' . $equip['quantity_available']], 422); return;
        }

        $feeAmount = ($equip['rental_fee_per_hour'] ?? 0) * $data['quantity'];
        $depositAmount = ($equip['deposit_amount'] ?? 0) * $data['quantity'];

        $id = $this->db()->insert('sport_equipment_rentals', [
            'rental_number' => $rentalNumber, 'equipment_id' => $data['equipment_id'],
            'member_id' => $data['member_id'], 'quantity' => $data['quantity'],
            'rental_start' => date('Y-m-d H:i:s'), 'rental_end_expected' => $data['rental_end_expected'],
            'fee_amount' => $feeAmount, 'deposit_amount' => $depositAmount,
            'status' => 'active', 'created_by' => auth_id(),
        ]);
        $this->audit('create', 'sport_equipment_rental', $id, "Rented equipment: {$rentalNumber}");
        $this->json(['success' => true, 'message' => 'Equipment rented: ' . $rentalNumber, 'redirect' => '/sports/equipment/rentals']);
    }

    public function returnRental(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $condition = $request->post('condition_on_return', 'good');
        $this->db()->update('sport_equipment_rentals', [
            'status' => 'returned', 'rental_end_actual' => date('Y-m-d H:i:s'),
            'condition_on_return' => $condition, 'deposit_returned' => 1,
            'deposit_returned_amount' => $condition === 'good' ? $this->db()->fetchColumn("SELECT deposit_amount FROM sport_equipment_rentals WHERE id = ?", [$id]) : 0,
        ], 'id = ?', [$id]);
        $this->audit('update', 'sport_equipment_rental', $id, "Returned rental #{$id}");
        $this->json(['success' => true, 'message' => 'Equipment returned', 'redirect' => '/sports/equipment/rentals']);
    }
}
```

## File: modules\sports\Controllers\LeagueController.php

```php
<?php

class LeagueController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('leagues/index', [
            'title' => lang('sports.leagues'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.leagues')]],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $seasons = $db->fetchAll("SELECT ss.id, CONCAT(sp.name_en,' - ',ss.name_en) as name FROM sport_seasons ss LEFT JOIN sports sp ON ss.sport_id = sp.id ORDER BY ss.start_date DESC LIMIT 20");
        $this->view('leagues/create', [
            'title' => lang('sports.create_league'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.leagues'), 'url' => '/sports/leagues'], ['label' => lang('common.create')]],
            'sports' => $sports, 'seasons' => $seasons,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'league_code' => 'required|string|max:20|unique:sport_leagues,league_code',
            'sport_id' => 'required|integer|exists:sports,id',
            'season_id' => 'required|integer|exists:sport_seasons,id',
            'name_en' => 'required|string|max:300',
            'name_ar' => 'nullable|string|max:300',
            'league_type' => 'required|in:round_robin,knockout,group_stage,swiss,custom',
            'start_date' => 'required|date',
            'end_date' => 'nullable|date',
            'max_teams' => 'nullable|integer',
            'status' => 'required|in:registration,scheduled,in_progress,completed,cancelled',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $id = $this->db()->insert('sport_leagues', $validator->validated());
        $this->audit('create', 'sport_league', $id, 'Created league');
        $this->json(['success' => true, 'message' => 'League created', 'redirect' => '/sports/leagues/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $league = $db->fetchOne("SELECT l.*, sp.name_en as sport_name, ss.name_en as season_name FROM sport_leagues l LEFT JOIN sports sp ON l.sport_id = sp.id LEFT JOIN sport_seasons ss ON l.season_id = ss.id WHERE l.id = ?", [$id]);
        if (!$league) { $this->abort(404); return; }

        $standings = $db->fetchAll(
            "SELECT lt.*, t.name_en as team_name, t.team_code
             FROM sport_league_teams lt LEFT JOIN sport_teams t ON lt.team_id = t.id
             WHERE lt.league_id = ? ORDER BY lt.points DESC, lt.goal_difference DESC, lt.goals_for DESC", [$id]);

        $matches = $db->fetchAll(
            "SELECT m.*, ht.name_en as home_name, at.name_en as away_name
             FROM sport_matches m LEFT JOIN sport_teams ht ON m.home_team_id = ht.id LEFT JOIN sport_teams at ON m.away_team_id = at.id
             WHERE m.league_id = ? ORDER BY m.match_date, m.start_time", [$id]);

        $availableTeams = $db->fetchAll("SELECT id, name_en, team_code FROM sport_teams WHERE sport_id = ? AND status = 'active' AND id NOT IN (SELECT team_id FROM sport_league_teams WHERE league_id = ?) ORDER BY name_en", [$league['sport_id'], $id]);

        $this->view('leagues/show', [
            'title' => $league['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.leagues'), 'url' => '/sports/leagues'], ['label' => $league['name_en']]],
            'league' => $league, 'standings' => $standings, 'matches' => $matches, 'availableTeams' => $availableTeams,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $league = $this->db()->fetchOne("SELECT * FROM sport_leagues WHERE id = ?", [$id]);
        if (!$league) { $this->abort(404); return; }
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $seasons = $this->db()->fetchAll("SELECT ss.id, CONCAT(sp.name_en,' - ',ss.name_en) as name FROM sport_seasons ss LEFT JOIN sports sp ON ss.sport_id = sp.id ORDER BY ss.start_date DESC LIMIT 20");
        $this->view('leagues/edit', [
            'title' => 'Edit: ' . $league['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.leagues'), 'url' => '/sports/leagues'], ['label' => lang('common.edit')]],
            'league' => $league, 'sports' => $sports, 'seasons' => $seasons,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:300',
            'name_ar' => 'nullable|string|max:300',
            'league_type' => 'required|in:round_robin,knockout,group_stage,swiss,custom',
            'start_date' => 'required|date',
            'end_date' => 'nullable|date',
            'status' => 'required|in:registration,scheduled,in_progress,completed,cancelled',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $this->db()->update('sport_leagues', $validator->validated(), 'id = ?', [$id]);
        $this->audit('update', 'sport_league', $id, 'Updated league');
        $this->json(['success' => true, 'message' => 'League updated', 'redirect' => '/sports/leagues/' . $id]);
    }

    public function registerTeam(Request $request): void
    {
        $leagueId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $teamId = (int) $request->post('team_id');
        $this->db()->insert('sport_league_teams', [
            'league_id' => $leagueId, 'team_id' => $teamId,
            'registration_date' => date('Y-m-d'), 'status' => 'registered',
        ]);
        $this->audit('create', 'sport_league_team', null, "Registered team #{$teamId} in league #{$leagueId}");
        $this->json(['success' => true, 'message' => 'Team registered', 'redirect' => '/sports/leagues/' . $leagueId]);
    }
}
```

## File: modules\sports\Controllers\LockerController.php

```php
<?php

class LockerController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $rooms = $db->fetchAll("SELECT lr.*, f.name_en as facility_name FROM sport_locker_rooms lr LEFT JOIN facilities f ON lr.facility_id = f.id ORDER BY lr.name_en");
        $lockers = $db->fetchAll(
            "SELECT l.*, lr.name_en as room_name,
                    (SELECT CONCAT(m.membership_number, ' - ', m.full_name_en) FROM sport_locker_assignments la JOIN members m ON la.member_id = m.id WHERE la.locker_id = l.id AND la.status = 'active' LIMIT 1) as assigned_to
             FROM sport_lockers l LEFT JOIN sport_locker_rooms lr ON l.room_id = lr.id ORDER BY lr.name_en, l.locker_number");

        $this->view('lockers/index', [
            'title' => lang('sports.lockers'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.lockers')]],
            'rooms' => $rooms, 'lockers' => $lockers,
        ]);
    }

    public function storeRoom(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'room_code' => 'required|string|max:20|unique:sport_locker_rooms,room_code',
            'name_en' => 'required|string|max:100',
            'name_ar' => 'required|string|max:100',
            'gender' => 'required|in:male,female,unisex',
            'facility_id' => 'nullable|integer',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['is_active'] = 1;
        $this->db()->insert('sport_locker_rooms', $data);
        $this->json(['success' => true, 'message' => 'Room created', 'redirect' => '/sports/lockers']);
    }

    public function storeLocker(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $roomId = (int) $request->post('room_id');
        $count = (int) $request->post('count', 1);
        $size = $request->post('size', 'medium');
        $db = $this->db();

        $maxNumber = (int) $db->fetchColumn("SELECT COALESCE(MAX(CAST(locker_number AS UNSIGNED)), 0) FROM sport_lockers WHERE room_id = ?", [$roomId]);
        for ($i = 1; $i <= $count; $i++) {
            $num = $maxNumber + $i;
            $db->insert('sport_lockers', [
                'locker_number' => str_pad($num, 3, '0', STR_PAD_LEFT),
                'room_id' => $roomId, 'size' => $size, 'status' => 'available',
            ]);
        }
        $db->update('sport_locker_rooms', [
            'total_lockers' => $db->fetchColumn("SELECT COUNT(*) FROM sport_lockers WHERE room_id = ?", [$roomId]),
            'available_lockers' => $db->fetchColumn("SELECT COUNT(*) FROM sport_lockers WHERE room_id = ? AND status = 'available'", [$roomId]),
        ], 'id = ?', [$roomId]);

        $this->json(['success' => true, 'message' => "{$count} lockers added", 'redirect' => '/sports/lockers']);
    }

    public function assign(Request $request): void
    {
        $lockerId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $memberId = (int) $request->post('member_id');
        $db = $this->db();
        $db->insert('sport_locker_assignments', [
            'locker_id' => $lockerId, 'member_id' => $memberId,
            'assignment_type' => $request->post('assignment_type', 'seasonal'),
            'start_date' => date('Y-m-d'), 'fee_amount' => $request->post('fee_amount', 0),
            'status' => 'active', 'key_issued' => 1,
        ]);
        $db->update('sport_lockers', ['status' => 'assigned'], 'id = ?', [$lockerId]);
        $roomId = $db->fetchColumn("SELECT room_id FROM sport_lockers WHERE id = ?", [$lockerId]);
        $db->update('sport_locker_rooms', [
            'available_lockers' => $db->fetchColumn("SELECT COUNT(*) FROM sport_lockers WHERE room_id = ? AND status = 'available'", [$roomId]),
        ], 'id = ?', [$roomId]);
        $this->json(['success' => true, 'message' => 'Locker assigned', 'redirect' => '/sports/lockers']);
    }

    public function release(Request $request): void
    {
        $lockerId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $db = $this->db();
        $db->update('sport_locker_assignments', ['status' => 'expired', 'end_date' => date('Y-m-d'), 'key_returned' => 1], 'locker_id = ? AND status = ?', [$lockerId, 'active']);
        $db->update('sport_lockers', ['status' => 'available'], 'id = ?', [$lockerId]);
        $roomId = $db->fetchColumn("SELECT room_id FROM sport_lockers WHERE id = ?", [$lockerId]);
        $db->update('sport_locker_rooms', [
            'available_lockers' => $db->fetchColumn("SELECT COUNT(*) FROM sport_lockers WHERE room_id = ? AND status = 'available'", [$roomId]),
        ], 'id = ?', [$roomId]);
        $this->json(['success' => true, 'message' => 'Locker released', 'redirect' => '/sports/lockers']);
    }
}
```

## File: modules\sports\Controllers\MatchController.php

```php
<?php

class MatchController extends Controller
{
    public function index(Request $request): void
    {
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->view('matches/index', [
            'title' => lang('sports.matches'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.matches')]],
            'sports' => $sports,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $leagues = $db->fetchAll("SELECT l.id, CONCAT(sp.name_en,' - ',l.name_en) as name FROM sport_leagues l LEFT JOIN sports sp ON l.sport_id = sp.id WHERE l.status IN ('registration','scheduled','in_progress') ORDER BY l.start_date DESC");
        $facilities = $db->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 AND is_bookable = 1 ORDER BY name_en");
        $this->view('matches/create', [
            'title' => lang('sports.create_match'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.matches'), 'url' => '/sports/matches'], ['label' => lang('common.create')]],
            'sports' => $sports, 'leagues' => $leagues, 'facilities' => $facilities,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $matchNumber = SequenceGenerator::next('match_number');
        $validator = new Validator($request->all(), [
            'sport_id' => 'required|integer|exists:sports,id',
            'home_team_id' => 'required|integer|exists:sport_teams,id',
            'away_team_id' => 'required|integer|exists:sport_teams,id',
            'match_date' => 'required|date',
            'start_time' => 'nullable|string',
            'facility_id' => 'nullable|integer',
            'league_id' => 'nullable|integer',
            'tournament_id' => 'nullable|integer',
            'match_type' => 'required|in:league,friendly,tournament,playoff,final,exhibition',
            'round_number' => 'nullable|integer',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['match_number'] = $matchNumber;
        $data['status'] = 'scheduled';
        $id = $this->db()->insert('sport_matches', $data);
        $this->audit('create', 'sport_match', $id, "Created match: {$matchNumber}");
        $this->json(['success' => true, 'message' => 'Match created', 'redirect' => '/sports/matches/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $match = $db->fetchOne(
            "SELECT m.*, sp.name_en as sport_name, ht.name_en as home_name, at.name_en as away_name,
                    f.name_en as facility_name, l.name_en as league_name
             FROM sport_matches m LEFT JOIN sports sp ON m.sport_id = sp.id
             LEFT JOIN sport_teams ht ON m.home_team_id = ht.id LEFT JOIN sport_teams at ON m.away_team_id = at.id
             LEFT JOIN facilities f ON m.facility_id = f.id LEFT JOIN sport_leagues l ON m.league_id = l.id
             WHERE m.id = ?", [$id]);
        if (!$match) { $this->abort(404); return; }

        $events = $db->fetchAll(
            "SELECT mr.*, t.name_en as team_name, m.full_name_en as member_name
             FROM sport_match_results mr LEFT JOIN sport_teams t ON mr.team_id = t.id LEFT JOIN members m ON mr.member_id = m.id
             WHERE mr.match_id = ? ORDER BY mr.event_minute", [$id]);

        $this->view('matches/show', [
            'title' => $match['match_number'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.matches'), 'url' => '/sports/matches'], ['label' => $match['match_number']]],
            'match' => $match, 'events' => $events,
        ]);
    }

    public function recordResult(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $homeScore = (int) $request->post('home_score', 0);
        $awayScore = (int) $request->post('away_score', 0);
        $this->db()->update('sport_matches', [
            'home_score' => $homeScore, 'away_score' => $awayScore,
            'status' => 'completed', 'end_time' => date('H:i:s'),
        ], 'id = ?', [$id]);
        $this->audit('update', 'sport_match', $id, "Recorded result: {$homeScore} - {$awayScore}");
        $this->json(['success' => true, 'message' => 'Result recorded', 'redirect' => '/sports/matches/' . $id]);
    }

    public function updateStatus(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $status = $request->post('status');
        if (!in_array($status, ['scheduled','in_progress','completed','postponed','cancelled','forfeited'])) {
            $this->json(['success' => false, 'message' => 'Invalid status'], 422); return;
        }
        $this->db()->update('sport_matches', ['status' => $status], 'id = ?', [$id]);
        $this->audit('update', 'sport_match', $id, "Changed match status to {$status}");
        $this->json(['success' => true, 'message' => 'Status updated', 'redirect' => '/sports/matches/' . $id]);
    }
}
```

## File: modules\sports\Controllers\SeasonController.php

```php
<?php

class SeasonController extends Controller
{
    public function index(Request $request): void
    {
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->view('seasons/index', [
            'title' => lang('sports.seasons'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.seasons')]],
            'sports' => $sports,
        ]);
    }

    public function create(Request $request): void
    {
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->view('seasons/create', [
            'title' => lang('sports.create_season'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.seasons'), 'url' => '/sports/seasons'], ['label' => lang('common.create')]],
            'sports' => $sports,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'season_code' => 'required|string|max:20|unique:sport_seasons,season_code',
            'sport_id' => 'required|integer|exists:sports,id',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'start_date' => 'required|date',
            'end_date' => 'required|date',
            'registration_start' => 'nullable|date',
            'registration_end' => 'nullable|date',
            'status' => 'required|in:upcoming,registration,active,completed,cancelled',
            'max_participants' => 'nullable|integer',
            'fee_amount' => 'nullable|decimal',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['fee_amount'] = $data['fee_amount'] ?? 0;
        $id = $this->db()->insert('sport_seasons', $data);
        $this->audit('create', 'sport_season', $id, "Created season: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Season created', 'redirect' => '/sports/seasons']);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $season = $this->db()->fetchOne("SELECT * FROM sport_seasons WHERE id = ?", [$id]);
        if (!$season) { $this->abort(404); return; }
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->view('seasons/edit', [
            'title' => 'Edit: ' . $season['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.seasons'), 'url' => '/sports/seasons'], ['label' => lang('common.edit')]],
            'season' => $season, 'sports' => $sports,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'start_date' => 'required|date',
            'end_date' => 'required|date',
            'registration_start' => 'nullable|date',
            'registration_end' => 'nullable|date',
            'status' => 'required|in:upcoming,registration,active,completed,cancelled',
            'max_participants' => 'nullable|integer',
            'fee_amount' => 'nullable|decimal',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $this->db()->update('sport_seasons', $data, 'id = ?', [$id]);
        $this->audit('update', 'sport_season', $id, "Updated season: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Season updated', 'redirect' => '/sports/seasons']);
    }
}
```

## File: modules\sports\Controllers\SportApiController.php

```php
<?php

class SportApiController extends Controller
{
    public function sportsDatatable(Request $request): void
    {
        $draw    = (int) $request->get('draw', 1);
        $start   = (int) $request->get('start', 0);
        $length  = (int) $request->get('length', 25);
        $search  = $request->get('search')['value'] ?? '';
        $orderCol= (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir= ($request->get('order')[0]['dir'] ?? 'asc') === 'desc' ? 'DESC' : 'ASC';

        $columns = ['s.sport_code','s.name_en','sc.name_en','s.is_active','s.sort_order'];
        $db = $this->db();

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (s.name_en LIKE ? OR s.name_ar LIKE ? OR s.sport_code LIKE ?)";
            $s = "%{$search}%";
            $params = [$s, $s, $s];
        }
        if ($request->get('category_id')) {
            $where .= " AND s.category_id = ?";
            $params[] = $request->get('category_id');
        }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sports s WHERE 1=1");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sports s LEFT JOIN sport_categories sc ON s.category_id = sc.id WHERE {$where}", $params);

        $orderColumn = $columns[$orderCol] ?? 's.sort_order';
        $rows = $db->fetchAll(
            "SELECT s.*, sc.name_en as category_name, sc.name_ar as category_name_ar
             FROM sports s LEFT JOIN sport_categories sc ON s.category_id = sc.id
             WHERE {$where} ORDER BY {$orderColumn} {$orderDir} LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = array_map(function($r) {
            $r['status_badge'] = $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>';
            $r['actions'] = '<a href="/sports/view/' . $r['id'] . '" class="btn btn-sm btn-info" title="View"><i class="fas fa-eye"></i></a> '
                . '<a href="/sports/' . $r['id'] . '/edit" class="btn btn-sm btn-warning" title="Edit"><i class="fas fa-edit"></i></a>';
            return $r;
        }, $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function seasonsDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (ss.name_en LIKE ? OR ss.season_code LIKE ?)";
            $s = "%{$search}%"; $params = [$s, $s];
        }
        if ($request->get('sport_id')) { $where .= " AND ss.sport_id = ?"; $params[] = $request->get('sport_id'); }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_seasons ss WHERE 1=1");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_seasons ss WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT ss.*, sp.name_en as sport_name FROM sport_seasons ss
             LEFT JOIN sports sp ON ss.sport_id = sp.id WHERE {$where} ORDER BY ss.start_date DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'status_badge' => status_badge($r['status']),
            'actions' => '<a href="/sports/seasons/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function coachesDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1';
        $params = [];
        if ($search) {
            $where .= " AND (c.first_name_en LIKE ? OR c.last_name_en LIKE ? OR c.coach_code LIKE ? OR c.phone LIKE ?)";
            $s = "%{$search}%"; $params = [$s, $s, $s, $s];
        }
        if ($request->get('status')) { $where .= " AND c.status = ?"; $params[] = $request->get('status'); }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_coaches c");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_coaches c WHERE {$where}", $params);
        $rows = $db->fetchAll("SELECT c.* FROM sport_coaches c WHERE {$where} ORDER BY c.first_name_en ASC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'full_name' => e($r['first_name_en'] . ' ' . $r['last_name_en']),
            'status_badge' => status_badge($r['status']),
            'actions' => '<a href="/sports/coaches/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a> '
                . '<a href="/sports/coaches/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function classesDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1';
        $params = [];
        if ($search) { $where .= " AND (cl.name_en LIKE ? OR cl.class_code LIKE ?)"; $s = "%{$search}%"; $params = [$s, $s]; }
        if ($request->get('sport_id')) { $where .= " AND cl.sport_id = ?"; $params[] = $request->get('sport_id'); }
        if ($request->get('status')) { $where .= " AND cl.status = ?"; $params[] = $request->get('status'); }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_classes cl");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_classes cl WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT cl.*, sp.name_en as sport_name,
                    CONCAT(co.first_name_en, ' ', co.last_name_en) as coach_name,
                    f.name_en as facility_name,
                    (SELECT COUNT(*) FROM sport_class_enrollments e WHERE e.class_id = cl.id AND e.status IN ('enrolled','active')) as enrolled_count
             FROM sport_classes cl
             LEFT JOIN sports sp ON cl.sport_id = sp.id
             LEFT JOIN sport_coaches co ON cl.coach_id = co.id
             LEFT JOIN facilities f ON cl.facility_id = f.id
             WHERE {$where} ORDER BY cl.created_at DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'spots' => ($r['enrolled_count'] ?? 0) . '/' . $r['max_participants'],
            'status_badge' => status_badge($r['status']),
            'actions' => '<a href="/sports/classes/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a> '
                . '<a href="/sports/classes/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function teamsDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1'; $params = [];
        if ($search) { $where .= " AND (t.name_en LIKE ? OR t.team_code LIKE ?)"; $s = "%{$search}%"; $params = [$s, $s]; }
        if ($request->get('sport_id')) { $where .= " AND t.sport_id = ?"; $params[] = $request->get('sport_id'); }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_teams t");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_teams t WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT t.*, sp.name_en as sport_name, CONCAT(co.first_name_en,' ',co.last_name_en) as coach_name
             FROM sport_teams t LEFT JOIN sports sp ON t.sport_id = sp.id LEFT JOIN sport_coaches co ON t.coach_id = co.id
             WHERE {$where} ORDER BY t.name_en ASC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'players' => $r['current_players'] . '/' . ($r['max_players'] ?: 'âˆž'),
            'status_badge' => status_badge($r['status']),
            'record' => $r['wins'] . 'W-' . $r['losses'] . 'L-' . $r['draws'] . 'D',
            'actions' => '<a href="/sports/teams/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a> '
                . '<a href="/sports/teams/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function leaguesDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1'; $params = [];
        if ($search) { $where .= " AND (l.name_en LIKE ? OR l.league_code LIKE ?)"; $s = "%{$search}%"; $params = [$s, $s]; }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_leagues l");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_leagues l WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT l.*, sp.name_en as sport_name, ss.name_en as season_name,
                    (SELECT COUNT(*) FROM sport_league_teams lt WHERE lt.league_id = l.id) as team_count
             FROM sport_leagues l LEFT JOIN sports sp ON l.sport_id = sp.id LEFT JOIN sport_seasons ss ON l.season_id = ss.id
             WHERE {$where} ORDER BY l.start_date DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'status_badge' => status_badge($r['status']),
            'actions' => '<a href="/sports/leagues/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a> '
                . '<a href="/sports/leagues/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function matchesDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1'; $params = [];
        if ($search) { $where .= " AND (m.match_number LIKE ?)"; $params = ["%{$search}%"]; }
        if ($request->get('sport_id')) { $where .= " AND m.sport_id = ?"; $params[] = $request->get('sport_id'); }
        if ($request->get('status')) { $where .= " AND m.status = ?"; $params[] = $request->get('status'); }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_matches m");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_matches m WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT m.*, sp.name_en as sport_name, ht.name_en as home_team, at.name_en as away_team, f.name_en as facility_name
             FROM sport_matches m
             LEFT JOIN sports sp ON m.sport_id = sp.id
             LEFT JOIN sport_teams ht ON m.home_team_id = ht.id
             LEFT JOIN sport_teams at ON m.away_team_id = at.id
             LEFT JOIN facilities f ON m.facility_id = f.id
             WHERE {$where} ORDER BY m.match_date DESC, m.start_time DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'score' => $r['status'] === 'completed' ? ($r['home_score'] . ' - ' . $r['away_score']) : '-',
            'status_badge' => status_badge($r['status']),
            'actions' => '<a href="/sports/matches/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function tournamentsDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1'; $params = [];
        if ($search) { $where .= " AND (t.name_en LIKE ? OR t.tournament_code LIKE ?)"; $s = "%{$search}%"; $params = [$s, $s]; }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_tournaments t");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_tournaments t WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT t.*, sp.name_en as sport_name
             FROM sport_tournaments t LEFT JOIN sports sp ON t.sport_id = sp.id
             WHERE {$where} ORDER BY t.start_date DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'participants' => $r['current_participants'] . '/' . ($r['max_participants'] ?: 'âˆž'),
            'status_badge' => status_badge($r['status']),
            'actions' => '<a href="/sports/tournaments/' . $r['id'] . '" class="btn btn-sm btn-info"><i class="fas fa-eye"></i></a> '
                . '<a href="/sports/tournaments/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function equipmentDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1'; $params = [];
        if ($search) { $where .= " AND (e.name_en LIKE ? OR e.equipment_code LIKE ?)"; $s = "%{$search}%"; $params = [$s, $s]; }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_equipment e");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_equipment e WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT e.*, sp.name_en as sport_name, f.name_en as facility_name
             FROM sport_equipment e LEFT JOIN sports sp ON e.sport_id = sp.id LEFT JOIN facilities f ON e.facility_id = f.id
             WHERE {$where} ORDER BY e.name_en ASC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'stock' => $r['quantity_available'] . '/' . $r['quantity_total'],
            'condition_badge' => status_badge($r['condition_status']),
            'actions' => '<a href="/sports/equipment/' . $r['id'] . '/edit" class="btn btn-sm btn-warning"><i class="fas fa-edit"></i></a>'
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function rentalsDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $db = $this->db();

        $where = '1=1'; $params = [];
        if ($search) { $where .= " AND (r.rental_number LIKE ? OR m.full_name_en LIKE ?)"; $s = "%{$search}%"; $params = [$s, $s]; }
        if ($request->get('status')) { $where .= " AND r.status = ?"; $params[] = $request->get('status'); }

        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_equipment_rentals r LEFT JOIN members m ON r.member_id=m.id WHERE 1=1");
        $filtered = $db->fetchColumn("SELECT COUNT(*) FROM sport_equipment_rentals r LEFT JOIN members m ON r.member_id=m.id WHERE {$where}", $params);
        $rows = $db->fetchAll(
            "SELECT r.*, eq.name_en as equipment_name, m.full_name_en as member_name, m.membership_number
             FROM sport_equipment_rentals r
             LEFT JOIN sport_equipment eq ON r.equipment_id = eq.id
             LEFT JOIN members m ON r.member_id = m.id
             WHERE {$where} ORDER BY r.created_at DESC LIMIT {$length} OFFSET {$start}", $params);

        $data = array_map(fn($r) => array_merge($r, [
            'status_badge' => status_badge($r['status']),
            'actions' => $r['status'] === 'active' ?
                '<form method="POST" action="/sports/equipment/rentals/' . $r['id'] . '/return" style="display:inline">' . csrf_field()
                . '<button type="submit" class="btn btn-sm btn-success" title="Return"><i class="fas fa-undo"></i> Return</button></form>' : ''
        ]), $rows);

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $filtered, 'data' => $data]);
    }

    public function membershipsDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $db = $this->db();

        $rows = $db->fetchAll(
            "SELECT sm.*, sp.name_en as sport_name, m.full_name_en as member_name, m.membership_number
             FROM sport_memberships sm
             LEFT JOIN sports sp ON sm.sport_id = sp.id LEFT JOIN members m ON sm.member_id = m.id
             ORDER BY sm.created_at DESC LIMIT {$length} OFFSET {$start}");
        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_memberships");

        $data = array_map(fn($r) => array_merge($r, ['status_badge' => status_badge($r['status'])]), $rows);
        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function achievementsDatatable(Request $request): void
    {
        $draw = (int) $request->get('draw', 1);
        $start = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $db = $this->db();

        $rows = $db->fetchAll(
            "SELECT a.*, sp.name_en as sport_name, m.full_name_en as member_name, m.membership_number
             FROM sport_member_achievements a
             LEFT JOIN sports sp ON a.sport_id = sp.id LEFT JOIN members m ON a.member_id = m.id
             ORDER BY a.achievement_date DESC LIMIT {$length} OFFSET {$start}");
        $total = $db->fetchColumn("SELECT COUNT(*) FROM sport_member_achievements");

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $rows]);
    }

    public function sportsList(Request $request): void
    {
        $rows = $this->db()->fetchAll("SELECT id, sport_code, name_en, name_ar FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->json($rows);
    }

    public function coachesList(Request $request): void
    {
        $rows = $this->db()->fetchAll("SELECT id, coach_code, CONCAT(first_name_en,' ',last_name_en) as name FROM sport_coaches WHERE status = 'active' ORDER BY first_name_en");
        $this->json($rows);
    }

    public function teamsList(Request $request): void
    {
        $sportId = $request->get('sport_id');
        $where = "t.status = 'active'";
        $params = [];
        if ($sportId) { $where .= " AND t.sport_id = ?"; $params[] = $sportId; }
        $rows = $this->db()->fetchAll("SELECT t.id, t.team_code, t.name_en FROM sport_teams t WHERE {$where} ORDER BY t.name_en", $params);
        $this->json($rows);
    }
}
```

## File: modules\sports\Controllers\SportController.php

```php
<?php

class SportController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $categories = $db->fetchAll("SELECT * FROM sport_categories WHERE is_active = 1 ORDER BY sort_order");
        $sports = $db->fetchAll(
            "SELECT s.*, sc.name_en as category_name, sc.color_hex,
                    (SELECT COUNT(*) FROM sport_classes cl WHERE cl.sport_id = s.id AND cl.status IN ('active','enrollment_open')) as active_classes,
                    (SELECT COUNT(*) FROM sport_teams t WHERE t.sport_id = s.id AND t.status = 'active') as active_teams
             FROM sports s LEFT JOIN sport_categories sc ON s.category_id = sc.id
             WHERE s.is_active = 1 ORDER BY s.sort_order");

        $this->view('sports/index', [
            'title' => lang('sports.title'),
            'breadcrumbs' => [['label' => lang('sports.title')]],
            'categories' => $categories,
            'sports' => $sports,
        ]);
    }

    public function create(Request $request): void
    {
        $categories = $this->db()->fetchAll("SELECT * FROM sport_categories WHERE is_active = 1 ORDER BY sort_order");
        $this->view('sports/create', [
            'title' => lang('sports.create'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('common.create')]],
            'categories' => $categories,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $validator = new Validator($request->all(), [
            'sport_code' => 'required|string|max:20|unique:sports,sport_code',
            'category_id' => 'required|integer|exists:sport_categories,id',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'description_en' => 'nullable|string',
            'description_ar' => 'nullable|string',
            'team_sport' => 'nullable|boolean',
            'min_players' => 'nullable|integer',
            'max_players' => 'nullable|integer',
            'gender_restriction' => 'required|in:male,female,both,mixed',
            'requires_medical_clearance' => 'nullable|boolean',
            'sort_order' => 'nullable|integer',
        ]);

        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $data = $validator->validated();
        $data['team_sport'] = !empty($data['team_sport']) ? 1 : 0;
        $data['requires_medical_clearance'] = !empty($data['requires_medical_clearance']) ? 1 : 0;
        $data['is_active'] = 1;
        $data['sort_order'] = $data['sort_order'] ?? 0;

        $id = $this->db()->insert('sports', $data);
        $this->audit('create', 'sport', $id, "Created sport: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Sport created successfully', 'redirect' => '/sports']);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $sport = $db->fetchOne("SELECT s.*, sc.name_en as category_name FROM sports s LEFT JOIN sport_categories sc ON s.category_id = sc.id WHERE s.id = ?", [$id]);
        if (!$sport) { $this->abort(404); return; }

        $classes = $db->fetchAll("SELECT cl.*, CONCAT(co.first_name_en,' ',co.last_name_en) as coach_name FROM sport_classes cl LEFT JOIN sport_coaches co ON cl.coach_id = co.id WHERE cl.sport_id = ? AND cl.is_active = 1 ORDER BY cl.name_en", [$id]);
        $teams = $db->fetchAll("SELECT * FROM sport_teams WHERE sport_id = ? AND status = 'active' ORDER BY name_en", [$id]);
        $rules = $db->fetchAll("SELECT * FROM sport_rules WHERE sport_id = ? AND is_active = 1 ORDER BY sort_order", [$id]);
        $seasons = $db->fetchAll("SELECT * FROM sport_seasons WHERE sport_id = ? ORDER BY start_date DESC LIMIT 5", [$id]);

        $this->view('sports/show', [
            'title' => $sport['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => $sport['name_en']]],
            'sport' => $sport, 'classes' => $classes, 'teams' => $teams, 'rules' => $rules, 'seasons' => $seasons,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $sport = $this->db()->fetchOne("SELECT * FROM sports WHERE id = ?", [$id]);
        if (!$sport) { $this->abort(404); return; }
        $categories = $this->db()->fetchAll("SELECT * FROM sport_categories WHERE is_active = 1 ORDER BY sort_order");

        $this->view('sports/edit', [
            'title' => 'Edit: ' . $sport['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('common.edit')]],
            'sport' => $sport, 'categories' => $categories,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }

        $validator = new Validator($request->all(), [
            'category_id' => 'required|integer|exists:sport_categories,id',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'description_en' => 'nullable|string',
            'description_ar' => 'nullable|string',
            'team_sport' => 'nullable|boolean',
            'min_players' => 'nullable|integer',
            'max_players' => 'nullable|integer',
            'gender_restriction' => 'required|in:male,female,both,mixed',
            'requires_medical_clearance' => 'nullable|boolean',
            'is_active' => 'nullable|boolean',
            'sort_order' => 'nullable|integer',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }

        $old = $this->db()->fetchOne("SELECT * FROM sports WHERE id = ?", [$id]);
        $data = $validator->validated();
        $data['team_sport'] = !empty($data['team_sport']) ? 1 : 0;
        $data['requires_medical_clearance'] = !empty($data['requires_medical_clearance']) ? 1 : 0;
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;

        $this->db()->update('sports', $data, 'id = ?', [$id]);
        $this->audit('update', 'sport', $id, "Updated sport: {$data['name_en']}", $old, $data);
        $this->json(['success' => true, 'message' => 'Sport updated successfully', 'redirect' => '/sports']);
    }

    public function destroy(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $this->db()->update('sports', ['is_active' => 0], 'id = ?', [$id]);
        $this->audit('delete', 'sport', $id, 'Deactivated sport');
        $this->json(['success' => true, 'message' => 'Sport deactivated']);
    }

    public function categories(Request $request): void
    {
        $categories = $this->db()->fetchAll("SELECT * FROM sport_categories ORDER BY sort_order");
        $this->view('categories/index', [
            'title' => lang('sports.categories'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.categories')]],
            'categories' => $categories,
        ]);
    }

    public function storeCategory(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'category_code' => 'required|string|max:20|unique:sport_categories,category_code',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'sort_order' => 'nullable|integer',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['is_active'] = 1;
        $id = $this->db()->insert('sport_categories', $data);
        $this->audit('create', 'sport_category', $id, "Created category: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Category created', 'redirect' => '/sports/categories']);
    }

    public function updateCategory(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'sort_order' => 'nullable|integer',
            'is_active' => 'nullable|boolean',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['is_active'] = !empty($data['is_active']) ? 1 : 0;
        $this->db()->update('sport_categories', $data, 'id = ?', [$id]);
        $this->audit('update', 'sport_category', $id, "Updated category: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Category updated', 'redirect' => '/sports/categories']);
    }
}
```

## File: modules\sports\Controllers\SportMembershipController.php

```php
<?php

class SportMembershipController extends Controller
{
    public function index(Request $request): void
    {
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $this->view('memberships/index', [
            'title' => lang('sports.sport_memberships'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.sport_memberships')]],
            'sports' => $sports,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'member_id' => 'required|integer|exists:members,id',
            'sport_id' => 'required|integer|exists:sports,id',
            'membership_level' => 'required|in:basic,standard,premium,elite',
            'start_date' => 'required|date',
            'end_date' => 'nullable|date',
            'fee_amount' => 'nullable|decimal',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['status'] = 'active';
        $data['fee_amount'] = $data['fee_amount'] ?? 0;
        $id = $this->db()->insert('sport_memberships', $data);
        $this->audit('create', 'sport_membership', $id, 'Created sport membership');
        $this->json(['success' => true, 'message' => 'Sport membership created', 'redirect' => '/sports/memberships']);
    }
}
```

## File: modules\sports\Controllers\TeamController.php

```php
<?php

class TeamController extends Controller
{
    public function index(Request $request): void
    {
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 AND team_sport = 1 ORDER BY name_en");
        $this->view('teams/index', [
            'title' => lang('sports.teams'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.teams')]],
            'sports' => $sports,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $coaches = $db->fetchAll("SELECT id, CONCAT(first_name_en,' ',last_name_en) as name FROM sport_coaches WHERE status = 'active' ORDER BY first_name_en");
        $seasons = $db->fetchAll("SELECT ss.id, CONCAT(sp.name_en,' - ',ss.name_en) as name FROM sport_seasons ss LEFT JOIN sports sp ON ss.sport_id = sp.id WHERE ss.status IN ('upcoming','registration','active') ORDER BY ss.start_date DESC");
        $this->view('teams/create', [
            'title' => lang('sports.create_team'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.teams'), 'url' => '/sports/teams'], ['label' => lang('common.create')]],
            'sports' => $sports, 'coaches' => $coaches, 'seasons' => $seasons,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'team_code' => 'required|string|max:20|unique:sport_teams,team_code',
            'sport_id' => 'required|integer|exists:sports,id',
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'team_type' => 'required|in:club,amateur,youth,senior,women,mixed',
            'coach_id' => 'nullable|integer',
            'season_id' => 'nullable|integer',
            'max_players' => 'nullable|integer',
            'color_primary' => 'nullable|string|max:7',
            'color_secondary' => 'nullable|string|max:7',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['status'] = 'active';
        $id = $this->db()->insert('sport_teams', $data);
        $this->audit('create', 'sport_team', $id, "Created team: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Team created', 'redirect' => '/sports/teams/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $team = $db->fetchOne(
            "SELECT t.*, sp.name_en as sport_name, CONCAT(co.first_name_en,' ',co.last_name_en) as coach_name, ss.name_en as season_name
             FROM sport_teams t LEFT JOIN sports sp ON t.sport_id = sp.id LEFT JOIN sport_coaches co ON t.coach_id = co.id LEFT JOIN sport_seasons ss ON t.season_id = ss.id
             WHERE t.id = ?", [$id]);
        if (!$team) { $this->abort(404); return; }

        $members = $db->fetchAll(
            "SELECT tm.*, m.full_name_en as member_name, m.membership_number, m.photo_path
             FROM sport_team_members tm LEFT JOIN members m ON tm.member_id = m.id
             WHERE tm.team_id = ? AND tm.status = 'active' ORDER BY tm.jersey_number", [$id]);

        $recentMatches = $db->fetchAll(
            "SELECT sm.*, ht.name_en as home_name, at.name_en as away_name
             FROM sport_matches sm LEFT JOIN sport_teams ht ON sm.home_team_id = ht.id LEFT JOIN sport_teams at ON sm.away_team_id = at.id
             WHERE (sm.home_team_id = ? OR sm.away_team_id = ?) ORDER BY sm.match_date DESC LIMIT 10", [$id, $id]);

        $this->view('teams/show', [
            'title' => $team['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.teams'), 'url' => '/sports/teams'], ['label' => $team['name_en']]],
            'team' => $team, 'members' => $members, 'recentMatches' => $recentMatches,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $team = $db->fetchOne("SELECT * FROM sport_teams WHERE id = ?", [$id]);
        if (!$team) { $this->abort(404); return; }
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $coaches = $db->fetchAll("SELECT id, CONCAT(first_name_en,' ',last_name_en) as name FROM sport_coaches WHERE status = 'active' ORDER BY first_name_en");
        $this->view('teams/edit', [
            'title' => 'Edit: ' . $team['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.teams'), 'url' => '/sports/teams'], ['label' => lang('common.edit')]],
            'team' => $team, 'sports' => $sports, 'coaches' => $coaches,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:200',
            'name_ar' => 'required|string|max:200',
            'team_type' => 'required|in:club,amateur,youth,senior,women,mixed',
            'coach_id' => 'nullable|integer',
            'max_players' => 'nullable|integer',
            'status' => 'required|in:active,inactive,disbanded,suspended',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $this->db()->update('sport_teams', $validator->validated(), 'id = ?', [$id]);
        $this->audit('update', 'sport_team', $id, 'Updated team');
        $this->json(['success' => true, 'message' => 'Team updated', 'redirect' => '/sports/teams/' . $id]);
    }

    public function addMember(Request $request): void
    {
        $teamId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $memberId = (int) $request->post('member_id');
        $this->db()->insert('sport_team_members', [
            'team_id' => $teamId, 'member_id' => $memberId,
            'jersey_number' => $request->post('jersey_number') ?: null,
            'position' => $request->post('position', ''),
            'role' => $request->post('role', 'player'),
            'joined_date' => date('Y-m-d'), 'status' => 'active',
        ]);
        $this->audit('create', 'sport_team_member', null, "Added member #{$memberId} to team #{$teamId}");
        $this->json(['success' => true, 'message' => 'Member added to team', 'redirect' => '/sports/teams/' . $teamId]);
    }

    public function removeMember(Request $request): void
    {
        $teamId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $teamMemberId = (int) $request->post('team_member_id');
        $this->db()->update('sport_team_members', ['status' => 'left', 'left_date' => date('Y-m-d')], 'id = ? AND team_id = ?', [$teamMemberId, $teamId]);
        $this->audit('update', 'sport_team_member', $teamMemberId, "Removed from team #{$teamId}");
        $this->json(['success' => true, 'message' => 'Member removed from team', 'redirect' => '/sports/teams/' . $teamId]);
    }
}
```

## File: modules\sports\Controllers\TournamentController.php

```php
<?php

class TournamentController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('tournaments/index', [
            'title' => lang('sports.tournaments'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.tournaments')]],
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $sports = $db->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $facilities = $db->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $seasons = $db->fetchAll("SELECT ss.id, CONCAT(sp.name_en,' - ',ss.name_en) as name FROM sport_seasons ss LEFT JOIN sports sp ON ss.sport_id = sp.id WHERE ss.status IN ('upcoming','registration','active') ORDER BY ss.start_date DESC");
        $this->view('tournaments/create', [
            'title' => lang('sports.create_tournament'),
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.tournaments'), 'url' => '/sports/tournaments'], ['label' => lang('common.create')]],
            'sports' => $sports, 'facilities' => $facilities, 'seasons' => $seasons,
        ]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $tCode = SequenceGenerator::next('tournament_code');
        $validator = new Validator($request->all(), [
            'sport_id' => 'required|integer|exists:sports,id',
            'name_en' => 'required|string|max:300',
            'name_ar' => 'nullable|string|max:300',
            'tournament_type' => 'required|in:individual,doubles,team,mixed',
            'format' => 'required|in:single_elimination,double_elimination,round_robin,swiss,group_knockout,custom',
            'start_date' => 'required|date',
            'end_date' => 'nullable|date',
            'registration_start' => 'nullable|date',
            'registration_end' => 'nullable|date',
            'max_participants' => 'nullable|integer',
            'entry_fee' => 'nullable|decimal',
            'prize_pool' => 'nullable|decimal',
            'facility_id' => 'nullable|integer',
            'season_id' => 'nullable|integer',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $data = $validator->validated();
        $data['tournament_code'] = $tCode;
        $data['status'] = 'planned';
        $data['entry_fee'] = $data['entry_fee'] ?? 0;
        $data['prize_pool'] = $data['prize_pool'] ?? 0;
        $data['created_by'] = auth_id();
        $id = $this->db()->insert('sport_tournaments', $data);
        $this->audit('create', 'sport_tournament', $id, "Created tournament: {$data['name_en']}");
        $this->json(['success' => true, 'message' => 'Tournament created', 'redirect' => '/sports/tournaments/' . $id]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $tournament = $db->fetchOne("SELECT t.*, sp.name_en as sport_name, f.name_en as facility_name FROM sport_tournaments t LEFT JOIN sports sp ON t.sport_id = sp.id LEFT JOIN facilities f ON t.facility_id = f.id WHERE t.id = ?", [$id]);
        if (!$tournament) { $this->abort(404); return; }

        $registrations = $db->fetchAll(
            "SELECT r.*, m.full_name_en as member_name, m.membership_number, t.name_en as team_name
             FROM sport_tournament_registrations r LEFT JOIN members m ON r.member_id = m.id LEFT JOIN sport_teams t ON r.team_id = t.id
             WHERE r.tournament_id = ? ORDER BY r.seed_number, r.registration_date", [$id]);

        $this->view('tournaments/show', [
            'title' => $tournament['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.tournaments'), 'url' => '/sports/tournaments'], ['label' => $tournament['name_en']]],
            'tournament' => $tournament, 'registrations' => $registrations,
        ]);
    }

    public function edit(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $tournament = $this->db()->fetchOne("SELECT * FROM sport_tournaments WHERE id = ?", [$id]);
        if (!$tournament) { $this->abort(404); return; }
        $sports = $this->db()->fetchAll("SELECT id, name_en FROM sports WHERE is_active = 1 ORDER BY name_en");
        $facilities = $this->db()->fetchAll("SELECT id, name_en FROM facilities WHERE is_active = 1 ORDER BY name_en");
        $this->view('tournaments/edit', [
            'title' => 'Edit: ' . $tournament['name_en'],
            'breadcrumbs' => [['label' => lang('sports.title'), 'url' => '/sports'], ['label' => lang('sports.tournaments'), 'url' => '/sports/tournaments'], ['label' => lang('common.edit')]],
            'tournament' => $tournament, 'sports' => $sports, 'facilities' => $facilities,
        ]);
    }

    public function update(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $validator = new Validator($request->all(), [
            'name_en' => 'required|string|max:300',
            'name_ar' => 'nullable|string|max:300',
            'status' => 'required|in:planned,registration,in_progress,completed,cancelled',
            'start_date' => 'required|date',
            'end_date' => 'nullable|date',
            'max_participants' => 'nullable|integer',
        ]);
        if ($validator->fails()) { $this->json(['success' => false, 'errors' => $validator->errors()], 422); return; }
        $this->db()->update('sport_tournaments', $validator->validated(), 'id = ?', [$id]);
        $this->audit('update', 'sport_tournament', $id, 'Updated tournament');
        $this->json(['success' => true, 'message' => 'Tournament updated', 'redirect' => '/sports/tournaments/' . $id]);
    }

    public function register(Request $request): void
    {
        $tournamentId = (int) $request->routeParam('id');
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'Invalid token'], 403); return; }
        $memberId = $request->post('member_id') ? (int) $request->post('member_id') : null;
        $teamId = $request->post('team_id') ? (int) $request->post('team_id') : null;
        $this->db()->insert('sport_tournament_registrations', [
            'tournament_id' => $tournamentId, 'member_id' => $memberId, 'team_id' => $teamId,
            'registration_date' => date('Y-m-d'), 'status' => 'registered',
        ]);
        $this->db()->update('sport_tournaments', ['current_participants' => $this->db()->fetchColumn("SELECT COUNT(*) FROM sport_tournament_registrations WHERE tournament_id = ?", [$tournamentId])], 'id = ?', [$tournamentId]);
        $this->audit('create', 'sport_tournament_registration', null, "Registered for tournament #{$tournamentId}");
        $this->json(['success' => true, 'message' => 'Registration successful', 'redirect' => '/sports/tournaments/' . $tournamentId]);
    }
}
```

## File: modules\sports\module.json

```json
{
    "name": "sports",
    "label_en": "Sports & Activities",
    "label_ar": "Ø§Ù„Ø±ÙŠØ§Ø¶Ø© ÙˆØ§Ù„Ø£Ù†Ø´Ø·Ø©",
    "version": "1.0.0",
    "description": "Sports management, classes, teams, leagues, tournaments, equipment, and lockers",
    "sort_order": 70,
    "icon": "fas fa-running",
    "routes_file": "routes.php",
    "css_files": ["css/sports.css"],
    "js_files": ["js/sports.js"],
    "menu": [
        {
            "label_en": "Sports & Activities",
            "label_ar": "Ø§Ù„Ø±ÙŠØ§Ø¶Ø© ÙˆØ§Ù„Ø£Ù†Ø´Ø·Ø©",
            "icon": "fas fa-running",
            "permission": "sports.view",
            "sort_order": 70,
            "children": [
                {
                    "label_en": "Sports Catalog",
                    "label_ar": "Ø¯Ù„ÙŠÙ„ Ø§Ù„Ø±ÙŠØ§Ø¶Ø§Øª",
                    "route": "/sports",
                    "permission": "sports.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Seasons",
                    "label_ar": "Ø§Ù„Ù…ÙˆØ§Ø³Ù…",
                    "route": "/sports/seasons",
                    "permission": "sports.view",
                    "sort_order": 2
                },
                {
                    "label_en": "Coaches",
                    "label_ar": "Ø§Ù„Ù…Ø¯Ø±Ø¨ÙŠÙ†",
                    "route": "/sports/coaches",
                    "permission": "sports.view",
                    "sort_order": 3
                },
                {
                    "label_en": "Classes",
                    "label_ar": "Ø§Ù„Ø­ØµØµ Ø§Ù„ØªØ¯Ø±ÙŠØ¨ÙŠØ©",
                    "route": "/sports/classes",
                    "permission": "classes.view",
                    "sort_order": 4
                },
                {
                    "label_en": "Teams",
                    "label_ar": "Ø§Ù„ÙØ±Ù‚",
                    "route": "/sports/teams",
                    "permission": "teams.view",
                    "sort_order": 5
                },
                {
                    "label_en": "Leagues",
                    "label_ar": "Ø§Ù„Ø¯ÙˆØ±ÙŠØ§Øª",
                    "route": "/sports/leagues",
                    "permission": "sports.view",
                    "sort_order": 6
                },
                {
                    "label_en": "Matches",
                    "label_ar": "Ø§Ù„Ù…Ø¨Ø§Ø±ÙŠØ§Øª",
                    "route": "/sports/matches",
                    "permission": "sports.view",
                    "sort_order": 7
                },
                {
                    "label_en": "Tournaments",
                    "label_ar": "Ø§Ù„Ø¨Ø·ÙˆÙ„Ø§Øª",
                    "route": "/sports/tournaments",
                    "permission": "tournaments.view",
                    "sort_order": 8
                },
                {
                    "label_en": "Equipment",
                    "label_ar": "Ø§Ù„Ù…Ø¹Ø¯Ø§Øª",
                    "route": "/sports/equipment",
                    "permission": "equipment.view",
                    "sort_order": 9
                },
                {
                    "label_en": "Equipment Rentals",
                    "label_ar": "ØªØ£Ø¬ÙŠØ± Ø§Ù„Ù…Ø¹Ø¯Ø§Øª",
                    "route": "/sports/equipment/rentals",
                    "permission": "equipment.view",
                    "sort_order": 10
                },
                {
                    "label_en": "Lockers",
                    "label_ar": "Ø§Ù„Ø®Ø²Ø§Ø¦Ù†",
                    "route": "/sports/lockers",
                    "permission": "sports.view",
                    "sort_order": 11
                },
                {
                    "label_en": "Sport Memberships",
                    "label_ar": "Ø¹Ø¶ÙˆÙŠØ§Øª Ø±ÙŠØ§Ø¶ÙŠØ©",
                    "route": "/sports/memberships",
                    "permission": "sports.view",
                    "sort_order": 12
                },
                {
                    "label_en": "Achievements",
                    "label_ar": "Ø§Ù„Ø¥Ù†Ø¬Ø§Ø²Ø§Øª",
                    "route": "/sports/achievements",
                    "permission": "sports.view",
                    "sort_order": 13
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "active_classes",
            "label_en": "Active Classes",
            "label_ar": "Ø§Ù„Ø­ØµØµ Ø§Ù„Ù†Ø´Ø·Ø©",
            "permission": "classes.view",
            "partial": "views/widgets/active_classes.php",
            "size": "col-md-4",
            "sort_order": 70
        },
        {
            "widget_id": "upcoming_matches",
            "label_en": "Upcoming Matches",
            "label_ar": "Ø§Ù„Ù…Ø¨Ø§Ø±ÙŠØ§Øª Ø§Ù„Ù‚Ø§Ø¯Ù…Ø©",
            "permission": "sports.view",
            "partial": "views/widgets/upcoming_matches.php",
            "size": "col-md-4",
            "sort_order": 71
        },
        {
            "widget_id": "equipment_alerts",
            "label_en": "Equipment Alerts",
            "label_ar": "ØªÙ†Ø¨ÙŠÙ‡Ø§Øª Ø§Ù„Ù…Ø¹Ø¯Ø§Øª",
            "permission": "equipment.view",
            "partial": "views/widgets/equipment_alerts.php",
            "size": "col-md-4",
            "sort_order": 72
        }
    ]
}
```

## File: modules\sports\routes.php

```php
<?php
return [
    // â”€â”€ Sports Catalog â”€â”€
    ['GET',  '/sports',                          'SportController@index',       'sports.view'],
    ['GET',  '/sports/create',                   'SportController@create',      'sports.manage'],
    ['POST', '/sports',                          'SportController@store',       'sports.manage'],
    ['GET',  '/sports/view/{id}',                'SportController@show',        'sports.view'],
    ['GET',  '/sports/{id}/edit',                'SportController@edit',        'sports.manage'],
    ['POST', '/sports/{id}',                     'SportController@update',      'sports.manage'],
    ['POST', '/sports/{id}/delete',              'SportController@destroy',     'sports.manage'],

    // â”€â”€ Categories â”€â”€
    ['GET',  '/sports/categories',               'SportController@categories',  'sports.manage'],
    ['POST', '/sports/categories',               'SportController@storeCategory','sports.manage'],
    ['POST', '/sports/categories/{id}',          'SportController@updateCategory','sports.manage'],

    // â”€â”€ Seasons â”€â”€
    ['GET',  '/sports/seasons',                  'SeasonController@index',      'sports.view'],
    ['GET',  '/sports/seasons/create',           'SeasonController@create',     'sports.manage'],
    ['POST', '/sports/seasons',                  'SeasonController@store',      'sports.manage'],
    ['GET',  '/sports/seasons/{id}/edit',        'SeasonController@edit',       'sports.manage'],
    ['POST', '/sports/seasons/{id}',             'SeasonController@update',     'sports.manage'],

    // â”€â”€ Coaches â”€â”€
    ['GET',  '/sports/coaches',                  'CoachController@index',       'sports.view'],
    ['GET',  '/sports/coaches/create',           'CoachController@create',      'sports.manage'],
    ['POST', '/sports/coaches',                  'CoachController@store',       'sports.manage'],
    ['GET',  '/sports/coaches/{id}',             'CoachController@show',        'sports.view'],
    ['GET',  '/sports/coaches/{id}/edit',        'CoachController@edit',        'sports.manage'],
    ['POST', '/sports/coaches/{id}',             'CoachController@update',      'sports.manage'],

    // â”€â”€ Classes â”€â”€
    ['GET',  '/sports/classes',                  'ClassController@index',       'classes.view'],
    ['GET',  '/sports/classes/create',           'ClassController@create',      'classes.manage'],
    ['POST', '/sports/classes',                  'ClassController@store',       'classes.manage'],
    ['GET',  '/sports/classes/{id}',             'ClassController@show',        'classes.view'],
    ['GET',  '/sports/classes/{id}/edit',        'ClassController@edit',        'classes.manage'],
    ['POST', '/sports/classes/{id}',             'ClassController@update',      'classes.manage'],
    ['GET',  '/sports/classes/{id}/attendance',  'ClassController@attendance',  'classes.attendance'],
    ['POST', '/sports/classes/{id}/attendance',  'ClassController@saveAttendance','classes.attendance'],
    ['POST', '/sports/classes/{id}/enroll',      'ClassController@enroll',      'classes.manage'],
    ['POST', '/sports/classes/{id}/withdraw',    'ClassController@withdraw',    'classes.manage'],

    // â”€â”€ Teams â”€â”€
    ['GET',  '/sports/teams',                    'TeamController@index',        'teams.view'],
    ['GET',  '/sports/teams/create',             'TeamController@create',       'teams.manage'],
    ['POST', '/sports/teams',                    'TeamController@store',        'teams.manage'],
    ['GET',  '/sports/teams/{id}',               'TeamController@show',         'teams.view'],
    ['GET',  '/sports/teams/{id}/edit',          'TeamController@edit',         'teams.manage'],
    ['POST', '/sports/teams/{id}',               'TeamController@update',       'teams.manage'],
    ['POST', '/sports/teams/{id}/add-member',    'TeamController@addMember',    'teams.manage'],
    ['POST', '/sports/teams/{id}/remove-member', 'TeamController@removeMember', 'teams.manage'],

    // â”€â”€ Leagues â”€â”€
    ['GET',  '/sports/leagues',                  'LeagueController@index',      'sports.view'],
    ['GET',  '/sports/leagues/create',           'LeagueController@create',     'sports.manage'],
    ['POST', '/sports/leagues',                  'LeagueController@store',      'sports.manage'],
    ['GET',  '/sports/leagues/{id}',             'LeagueController@show',       'sports.view'],
    ['GET',  '/sports/leagues/{id}/edit',        'LeagueController@edit',       'sports.manage'],
    ['POST', '/sports/leagues/{id}',             'LeagueController@update',     'sports.manage'],
    ['POST', '/sports/leagues/{id}/register-team','LeagueController@registerTeam','sports.manage'],

    // â”€â”€ Matches â”€â”€
    ['GET',  '/sports/matches',                  'MatchController@index',       'sports.view'],
    ['GET',  '/sports/matches/create',           'MatchController@create',      'sports.manage'],
    ['POST', '/sports/matches',                  'MatchController@store',       'sports.manage'],
    ['GET',  '/sports/matches/{id}',             'MatchController@show',        'sports.view'],
    ['POST', '/sports/matches/{id}/result',      'MatchController@recordResult','sports.manage'],
    ['POST', '/sports/matches/{id}/status',      'MatchController@updateStatus','sports.manage'],

    // â”€â”€ Tournaments â”€â”€
    ['GET',  '/sports/tournaments',              'TournamentController@index',  'tournaments.view'],
    ['GET',  '/sports/tournaments/create',       'TournamentController@create', 'tournaments.manage'],
    ['POST', '/sports/tournaments',              'TournamentController@store',  'tournaments.manage'],
    ['GET',  '/sports/tournaments/{id}',         'TournamentController@show',   'tournaments.view'],
    ['GET',  '/sports/tournaments/{id}/edit',    'TournamentController@edit',   'tournaments.manage'],
    ['POST', '/sports/tournaments/{id}',         'TournamentController@update', 'tournaments.manage'],
    ['POST', '/sports/tournaments/{id}/register','TournamentController@register','tournaments.manage'],

    // â”€â”€ Equipment â”€â”€
    ['GET',  '/sports/equipment',                'EquipmentController@index',   'equipment.view'],
    ['GET',  '/sports/equipment/create',         'EquipmentController@create',  'equipment.manage'],
    ['POST', '/sports/equipment',                'EquipmentController@store',   'equipment.manage'],
    ['GET',  '/sports/equipment/{id}/edit',      'EquipmentController@edit',    'equipment.manage'],
    ['POST', '/sports/equipment/{id}',           'EquipmentController@update',  'equipment.manage'],
    ['GET',  '/sports/equipment/rentals',        'EquipmentController@rentals', 'equipment.view'],
    ['POST', '/sports/equipment/rentals',        'EquipmentController@storeRental','equipment.manage'],
    ['POST', '/sports/equipment/rentals/{id}/return','EquipmentController@returnRental','equipment.manage'],

    // â”€â”€ Lockers â”€â”€
    ['GET',  '/sports/lockers',                  'LockerController@index',      'sports.view'],
    ['POST', '/sports/lockers/rooms',            'LockerController@storeRoom',  'sports.manage'],
    ['POST', '/sports/lockers',                  'LockerController@storeLocker','sports.manage'],
    ['POST', '/sports/lockers/{id}/assign',      'LockerController@assign',     'sports.manage'],
    ['POST', '/sports/lockers/{id}/release',     'LockerController@release',    'sports.manage'],

    // â”€â”€ Sport Memberships â”€â”€
    ['GET',  '/sports/memberships',              'SportMembershipController@index','sports.view'],
    ['POST', '/sports/memberships',              'SportMembershipController@store','sports.manage'],

    // â”€â”€ Achievements â”€â”€
    ['GET',  '/sports/achievements',             'AchievementController@index', 'sports.view'],
    ['GET',  '/sports/achievements/create',      'AchievementController@create','sports.manage'],
    ['POST', '/sports/achievements',             'AchievementController@store', 'sports.manage'],

    // â”€â”€ API / DataTable â”€â”€
    ['GET',  '/api/sports/datatable',            'SportApiController@sportsDatatable',   'sports.view'],
    ['GET',  '/api/sports/seasons/datatable',    'SportApiController@seasonsDatatable',  'sports.view'],
    ['GET',  '/api/sports/coaches/datatable',    'SportApiController@coachesDatatable',  'sports.view'],
    ['GET',  '/api/sports/classes/datatable',    'SportApiController@classesDatatable',  'classes.view'],
    ['GET',  '/api/sports/teams/datatable',      'SportApiController@teamsDatatable',    'teams.view'],
    ['GET',  '/api/sports/leagues/datatable',    'SportApiController@leaguesDatatable',  'sports.view'],
    ['GET',  '/api/sports/matches/datatable',    'SportApiController@matchesDatatable',  'sports.view'],
    ['GET',  '/api/sports/tournaments/datatable','SportApiController@tournamentsDatatable','tournaments.view'],
    ['GET',  '/api/sports/equipment/datatable',  'SportApiController@equipmentDatatable','equipment.view'],
    ['GET',  '/api/sports/equipment/rentals/datatable','SportApiController@rentalsDatatable','equipment.view'],
    ['GET',  '/api/sports/memberships/datatable','SportApiController@membershipsDatatable','sports.view'],
    ['GET',  '/api/sports/achievements/datatable','SportApiController@achievementsDatatable','sports.view'],
    ['GET',  '/api/sports/list',                 'SportApiController@sportsList',        'sports.view'],
    ['GET',  '/api/sports/coaches/list',         'SportApiController@coachesList',       'sports.view'],
    ['GET',  '/api/sports/teams/list',           'SportApiController@teamsList',         'sports.view'],
];
```

## File: modules\sports\views\categories\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0"><i class="fas fa-tags me-2"></i>Sport Categories</h4></div>
<div class="row g-3">
<div class="col-md-5">
    <div class="card"><div class="card-header">Add Category</div><div class="card-body">
    <form method="POST" action="/sports/categories" data-ajax="true"><?=csrf_field()?>
    <div class="mb-2"><label class="form-label">Code</label><input type="text" name="category_code" class="form-control form-control-sm" required><div class="invalid-feedback" data-field="category_code"></div></div>
    <div class="mb-2"><label class="form-label">Name (EN)</label><input type="text" name="name_en" class="form-control form-control-sm" required></div>
    <div class="mb-2"><label class="form-label">Name (AR)</label><input type="text" name="name_ar" class="form-control form-control-sm" dir="rtl" required></div>
    <div class="mb-2"><label class="form-label">Sort Order</label><input type="number" name="sort_order" class="form-control form-control-sm" value="0"></div>
    <button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-save me-1"></i>Save</button>
    </form></div></div>
</div>
<div class="col-md-7">
    <div class="card"><div class="card-body p-0">
    <table class="table table-sm table-hover mb-0"><thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Order</th><th>Active</th></tr></thead><tbody>
    <?php foreach($categories as $c):?>
    <tr><td><?=e($c['category_code'])?></td><td><?=e($c['name_en'])?></td><td><?=e($c['name_ar'])?></td><td><?=$c['sort_order']?></td><td><?=$c['is_active']?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-muted"></i>'?></td></tr>
    <?php endforeach;?>
    </tbody></table>
    </div></div>
</div>
</div>
```

## File: modules\sports\views\classes\attendance.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-clipboard-list me-2"></i>Attendance: <?= e($class['name_en']) ?></h4>
    <a href="/sports/classes/<?= $class['id'] ?>" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back to Class</a>
</div>

<div class="card">
    <div class="card-header">
        <form method="GET" action="/sports/classes/<?= $class['id'] ?>/attendance" class="row g-2 align-items-end">
            <div class="col-md-3">
                <label class="form-label small">Session Date</label>
                <input type="date" name="date" class="form-control form-control-sm datepicker" value="<?= e($sessionDate) ?>">
            </div>
            <div class="col-md-2"><button type="submit" class="btn btn-primary btn-sm">Load</button></div>
        </form>
    </div>
    <div class="card-body">
        <form method="POST" action="/sports/classes/<?= $class['id'] ?>/attendance" data-ajax="true">
            <?= csrf_field() ?>
            <input type="hidden" name="session_date" value="<?= e($sessionDate) ?>">
            <input type="hidden" name="session_start" value="09:00">
            <table class="table table-sm table-hover">
                <thead><tr><th>#</th><th>Member</th><th>Status</th></tr></thead>
                <tbody>
                <?php foreach ($enrollments as $i => $en):
                    $existing = $attendanceMap[$en['id']] ?? null;
                    $currentStatus = $existing['status'] ?? 'present';
                ?>
                <tr>
                    <td><?= $i+1 ?></td>
                    <td><?= e($en['member_name'] ?? '') ?> <small class="text-muted"><?= e($en['membership_number'] ?? '') ?></small></td>
                    <td>
                        <div class="btn-group btn-group-sm" role="group">
                            <?php foreach (['present' => 'success', 'absent' => 'danger', 'late' => 'warning', 'excused' => 'info'] as $st => $color): ?>
                            <input type="radio" class="btn-check" name="attendance[<?= $en['id'] ?>]" id="att_<?= $en['id'] ?>_<?= $st ?>" value="<?= $st ?>" <?= $currentStatus === $st ? 'checked' : '' ?>>
                            <label class="btn btn-outline-<?= $color ?>" for="att_<?= $en['id'] ?>_<?= $st ?>"><?= ucfirst($st) ?></label>
                            <?php endforeach; ?>
                        </div>
                    </td>
                </tr>
                <?php endforeach; ?>
                <?php if (empty($enrollments)): ?><tr><td colspan="3" class="text-muted text-center">No enrolled members</td></tr><?php endif; ?>
                </tbody>
            </table>
            <?php if (!empty($enrollments)): ?>
            <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save Attendance</button>
            <?php endif; ?>
        </form>
    </div>
</div>
```

## File: modules\sports\views\classes\create.php

```php
<div class="row justify-content-center"><div class="col-lg-10">
<div class="card"><div class="card-header"><h5 class="mb-0"><i class="fas fa-plus me-2"></i>New Class</h5></div>
<div class="card-body">
<form method="POST" action="/sports/classes" data-ajax="true">
<?= csrf_field() ?>
<div class="row g-3">
    <div class="col-md-3"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="class_code" class="form-control" required><div class="invalid-feedback" data-field="class_code"></div></div>
    <div class="col-md-3"><label class="form-label">Sport <span class="text-danger">*</span></label><select name="sport_id" class="form-select" required><option value="">--</option><?php foreach($sports as $s): ?><option value="<?=$s['id']?>"><?=e($s['name_en'])?></option><?php endforeach; ?></select></div>
    <div class="col-md-3"><label class="form-label">Coach</label><select name="coach_id" class="form-select"><option value="">--</option><?php foreach($coaches as $c): ?><option value="<?=$c['id']?>"><?=e($c['name'])?></option><?php endforeach; ?></select></div>
    <div class="col-md-3"><label class="form-label">Facility</label><select name="facility_id" class="form-select"><option value="">--</option><?php foreach($facilities as $f): ?><option value="<?=$f['id']?>"><?=e($f['name_en'])?></option><?php endforeach; ?></select></div>
    <div class="col-md-6"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required></div>
    <div class="col-md-6"><label class="form-label">Name (AR)</label><input type="text" name="name_ar" class="form-control" dir="rtl"></div>
    <div class="col-md-3"><label class="form-label">Level</label><select name="class_type" class="form-select"><?php foreach(['all_levels','beginner','intermediate','advanced','kids','youth','adult','senior'] as $t): ?><option value="<?=$t?>"><?=ucfirst(str_replace('_',' ',$t))?></option><?php endforeach; ?></select></div>
    <div class="col-md-3"><label class="form-label">Max Participants <span class="text-danger">*</span></label><input type="number" name="max_participants" class="form-control" value="20" required></div>
    <div class="col-md-3"><label class="form-label">Fee/Month</label><input type="number" name="fee_per_month" class="form-control" step="0.01" value="0"></div>
    <div class="col-md-3"><label class="form-label">Session Duration (min)</label><input type="number" name="session_duration_minutes" class="form-control" value="60"></div>
    <div class="col-md-3"><label class="form-label">Gender</label><select name="gender_restriction" class="form-select"><option value="both">Both</option><option value="male">Male</option><option value="female">Female</option></select></div>
    <div class="col-md-3"><label class="form-label">Min Age</label><input type="number" name="min_age" class="form-control"></div>
    <div class="col-md-3"><label class="form-label">Max Age</label><input type="number" name="max_age" class="form-control"></div>
    <div class="col-md-3"><label class="form-label">Status</label><select name="status" class="form-select"><option value="planned">Planned</option><option value="enrollment_open">Enrollment Open</option><option value="active">Active</option></select></div>
    <div class="col-md-3"><label class="form-label">Start Date</label><input type="date" name="start_date" class="form-control datepicker"></div>
    <div class="col-md-3"><label class="form-label">End Date</label><input type="date" name="end_date" class="form-control datepicker"></div>
    <div class="col-md-3"><label class="form-label">Season</label><select name="season_id" class="form-select"><option value="">--</option><?php foreach($seasons as $ss): ?><option value="<?=$ss['id']?>"><?=e($ss['name'])?></option><?php endforeach; ?></select></div>
</div>
<hr>
<h6>Weekly Schedule</h6>
<div id="scheduleRows">
    <div class="row g-2 mb-2 schedule-row">
        <div class="col-md-3"><select name="schedule_day[]" class="form-select form-select-sm"><option value="">Day</option><option value="1">Monday</option><option value="2">Tuesday</option><option value="3">Wednesday</option><option value="4">Thursday</option><option value="5">Friday</option><option value="6">Saturday</option><option value="7">Sunday</option></select></div>
        <div class="col-md-3"><input type="time" name="schedule_start[]" class="form-control form-control-sm" placeholder="Start"></div>
        <div class="col-md-3"><input type="time" name="schedule_end[]" class="form-control form-control-sm" placeholder="End"></div>
        <div class="col-md-3"><button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.schedule-row').remove()"><i class="fas fa-trash"></i></button></div>
    </div>
</div>
<button type="button" class="btn btn-sm btn-outline-secondary mb-3" onclick="addScheduleRow()"><i class="fas fa-plus me-1"></i>Add Schedule</button>
<div class="mt-3 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/classes" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
<script>
function addScheduleRow(){const c=document.querySelector('.schedule-row').cloneNode(true);c.querySelectorAll('input,select').forEach(i=>i.value='');document.getElementById('scheduleRows').appendChild(c);}
</script>
```

## File: modules\sports\views\classes\edit.php

```php
<div class="row justify-content-center"><div class="col-lg-10">
<div class="card"><div class="card-header"><h5 class="mb-0"><i class="fas fa-edit me-2"></i>Edit Class</h5></div>
<div class="card-body">
<form method="POST" action="/sports/classes/<?= $class['id'] ?>" data-ajax="true">
<?= csrf_field() ?>
<div class="row g-3">
    <div class="col-md-3"><label class="form-label">Code</label><input type="text" class="form-control" value="<?=e($class['class_code'])?>" disabled></div>
    <div class="col-md-3"><label class="form-label">Coach</label><select name="coach_id" class="form-select"><option value="">--</option><?php foreach($coaches as $c): ?><option value="<?=$c['id']?>" <?=selected($c['id'],$class['coach_id'])?>><?=e($c['name'])?></option><?php endforeach; ?></select></div>
    <div class="col-md-3"><label class="form-label">Facility</label><select name="facility_id" class="form-select"><option value="">--</option><?php foreach($facilities as $f): ?><option value="<?=$f['id']?>" <?=selected($f['id'],$class['facility_id'])?>><?=e($f['name_en'])?></option><?php endforeach; ?></select></div>
    <div class="col-md-3"><label class="form-label">Level</label><select name="class_type" class="form-select"><?php foreach(['all_levels','beginner','intermediate','advanced','kids','youth','adult','senior'] as $t): ?><option value="<?=$t?>" <?=selected($t,$class['class_type'])?>><?=ucfirst(str_replace('_',' ',$t))?></option><?php endforeach; ?></select></div>
    <div class="col-md-6"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" value="<?=e($class['name_en'])?>" required></div>
    <div class="col-md-6"><label class="form-label">Name (AR)</label><input type="text" name="name_ar" class="form-control" dir="rtl" value="<?=e($class['name_ar']??'')?>"></div>
    <div class="col-md-3"><label class="form-label">Max Participants</label><input type="number" name="max_participants" class="form-control" value="<?=$class['max_participants']?>" required></div>
    <div class="col-md-3"><label class="form-label">Fee/Month</label><input type="number" name="fee_per_month" class="form-control" step="0.01" value="<?=$class['fee_per_month']?>"></div>
    <div class="col-md-3"><label class="form-label">Status</label><select name="status" class="form-select"><?php foreach(['planned','enrollment_open','active','full','completed','cancelled'] as $st): ?><option value="<?=$st?>" <?=selected($st,$class['status'])?>><?=ucfirst(str_replace('_',' ',$st))?></option><?php endforeach; ?></select></div>
</div>
<div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/classes/<?=$class['id']?>" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\classes\index.php

```php
<?php $this->push('styles') ?>
<link rel="stylesheet" href="<?= module_asset('sports', 'css/sports.css') ?>">
<?php $this->endPush() ?>

<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-chalkboard-teacher me-2"></i><?= lang('sports.classes') ?></h4>
    <?php if (auth_can('classes.manage')): ?>
    <a href="/sports/classes/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>New Class</a>
    <?php endif; ?>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <form class="row g-2 align-items-end" data-filter-table="classes-table" id="filterForm">
            <div class="col-md-3">
                <label class="form-label small">Sport</label>
                <select name="sport_id" class="form-select form-select-sm" onchange="DataTableHelper.reload('classes-table')">
                    <option value="">All Sports</option>
                    <?php foreach ($sports as $s): ?><option value="<?= $s['id'] ?>"><?= e($s['name_en']) ?></option><?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-3">
                <label class="form-label small">Status</label>
                <select name="status" class="form-select form-select-sm" onchange="DataTableHelper.reload('classes-table')">
                    <option value="">All</option>
                    <?php foreach ($statuses as $st): ?><option value="<?= $st ?>"><?= ucfirst(str_replace('_',' ',$st)) ?></option><?php endforeach; ?>
                </select>
            </div>
        </form>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="classes-table" class="table table-striped table-hover w-100"
               data-url="/api/sports/classes/datatable"
               data-columns='[
                   {"data":"class_code","title":"Code"},
                   {"data":"name_en","title":"Class Name"},
                   {"data":"sport_name","title":"Sport"},
                   {"data":"coach_name","title":"Coach"},
                   {"data":"class_type","title":"Level"},
                   {"data":"spots","title":"Enrolled"},
                   {"data":"status_badge","title":"Status","orderable":false},
                   {"data":"actions","title":"","orderable":false}
               ]'>
        </table>
    </div>
</div>

<?php $this->push('scripts') ?>
<script src="<?= module_asset('sports', 'js/sports.js') ?>"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
    DataTableHelper.init('classes-table');
});
</script>
<?php $this->endPush() ?>
```

## File: modules\sports\views\classes\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-chalkboard me-2"></i><?= e($class['name_en']) ?></h4>
    <div>
        <?php if (auth_can('classes.attendance')): ?><a href="/sports/classes/<?= $class['id'] ?>/attendance" class="btn btn-info btn-sm"><i class="fas fa-clipboard-list me-1"></i>Attendance</a><?php endif; ?>
        <?php if (auth_can('classes.manage')): ?><a href="/sports/classes/<?= $class['id'] ?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a><?php endif; ?>
        <a href="/sports/classes" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Class Details</h6></div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <tr><th>Code</th><td><?= e($class['class_code']) ?></td></tr>
                    <tr><th>Sport</th><td><?= e($class['sport_name'] ?? '') ?></td></tr>
                    <tr><th>Coach</th><td><?= e($class['coach_name'] ?? 'Unassigned') ?></td></tr>
                    <tr><th>Facility</th><td><?= e($class['facility_name'] ?? 'N/A') ?></td></tr>
                    <tr><th>Level</th><td><?= ucfirst(str_replace('_',' ',$class['class_type'])) ?></td></tr>
                    <tr><th>Capacity</th><td><?= $enrolledCount ?> / <?= $class['max_participants'] ?></td></tr>
                    <tr><th>Fee/Month</th><td><?= format_money($class['fee_per_month']) ?></td></tr>
                    <tr><th>Duration</th><td><?= $class['session_duration_minutes'] ?> min</td></tr>
                    <tr><th>Gender</th><td><?= ucfirst($class['gender_restriction']) ?></td></tr>
                    <tr><th>Status</th><td><?= status_badge($class['status']) ?></td></tr>
                </table>
            </div>
        </div>

        <?php if (!empty($schedules)): ?>
        <div class="card mt-3">
            <div class="card-header"><h6 class="mb-0">Weekly Schedule</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Day</th><th>Time</th></tr></thead>
                    <tbody>
                    <?php
                    $dayNames = ['','Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
                    foreach ($schedules as $sched): ?>
                    <tr><td><?= $dayNames[$sched['day_of_week']] ?? $sched['day_of_week'] ?></td><td><?= substr($sched['start_time'],0,5) ?> - <?= substr($sched['end_time'],0,5) ?></td></tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>
    </div>

    <div class="col-md-8">
        <ul class="nav nav-tabs">
            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-enrolled">Enrolled (<?= count($enrollments) ?>)</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-waitlist">Waitlist (<?= count($waitlist) ?>)</a></li>
        </ul>
        <div class="tab-content border border-top-0 p-3">
            <div class="tab-pane fade show active" id="tab-enrolled">
                <?php if (auth_can('classes.manage')): ?>
                <form method="POST" action="/sports/classes/<?= $class['id'] ?>/enroll" data-ajax="true" class="row g-2 mb-3">
                    <?= csrf_field() ?>
                    <div class="col-md-8">
                        <input type="number" name="member_id" class="form-control form-control-sm" placeholder="Member ID" required>
                    </div>
                    <div class="col-md-4">
                        <button type="submit" class="btn btn-primary btn-sm w-100"><i class="fas fa-user-plus me-1"></i>Enroll</button>
                    </div>
                </form>
                <?php endif; ?>
                <div class="table-responsive">
                <table class="table table-sm table-hover">
                    <thead><tr><th>#</th><th>Member</th><th>Date</th><th>Status</th><th>Fee</th><th></th></tr></thead>
                    <tbody>
                    <?php foreach ($enrollments as $i => $en): ?>
                    <tr>
                        <td><?= $i+1 ?></td>
                        <td><?= e($en['member_name'] ?? '') ?> <small class="text-muted"><?= e($en['membership_number'] ?? '') ?></small><?= $en['dependent_name'] ? '<br><small>Dep: ' . e($en['dependent_name']) . '</small>' : '' ?></td>
                        <td><?= format_date($en['enrollment_date']) ?></td>
                        <td><?= status_badge($en['status']) ?></td>
                        <td><?= format_money($en['fee_amount']) ?></td>
                        <td>
                            <?php if (auth_can('classes.manage') && in_array($en['status'], ['enrolled','active'])): ?>
                            <form method="POST" action="/sports/classes/<?= $class['id'] ?>/withdraw" data-ajax="true" style="display:inline">
                                <?= csrf_field() ?>
                                <input type="hidden" name="enrollment_id" value="<?= $en['id'] ?>">
                                <button type="submit" class="btn btn-sm btn-outline-danger" title="Withdraw"><i class="fas fa-user-minus"></i></button>
                            </form>
                            <?php endif; ?>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($enrollments)): ?><tr><td colspan="6" class="text-muted text-center">No enrollments</td></tr><?php endif; ?>
                    </tbody>
                </table>
                </div>
            </div>
            <div class="tab-pane fade" id="tab-waitlist">
                <table class="table table-sm">
                    <thead><tr><th>Position</th><th>Member</th><th>Registered</th><th>Status</th></tr></thead>
                    <tbody>
                    <?php foreach ($waitlist as $w): ?>
                    <tr><td><?= $w['queue_position'] ?></td><td><?= e($w['member_name'] ?? '') ?></td><td><?= format_date($w['registered_at']) ?></td><td><?= status_badge($w['status']) ?></td></tr>
                    <?php endforeach; ?>
                    <?php if (empty($waitlist)): ?><tr><td colspan="4" class="text-muted text-center">Empty waitlist</td></tr><?php endif; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
```

## File: modules\sports\views\coaches\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">New Coach</h5></div><div class="card-body">
<form method="POST" action="/sports/coaches" data-ajax="true" enctype="multipart/form-data"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-4"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="coach_code" class="form-control" required><div class="invalid-feedback" data-field="coach_code"></div></div>
<div class="col-md-4"><label class="form-label">Status</label><select name="status" class="form-select"><option value="active">Active</option><option value="inactive">Inactive</option><option value="on_leave">On Leave</option></select></div>
<div class="col-md-4"><label class="form-label">Hire Date</label><input type="date" name="hire_date" class="form-control datepicker"></div>
<div class="col-md-6"><label class="form-label">First Name (EN) <span class="text-danger">*</span></label><input type="text" name="first_name_en" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">Last Name (EN) <span class="text-danger">*</span></label><input type="text" name="last_name_en" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">First Name (AR)</label><input type="text" name="first_name_ar" class="form-control" dir="rtl"></div>
<div class="col-md-6"><label class="form-label">Last Name (AR)</label><input type="text" name="last_name_ar" class="form-control" dir="rtl"></div>
<div class="col-md-4"><label class="form-label">Phone</label><input type="text" name="phone" class="form-control"></div>
<div class="col-md-4"><label class="form-label">Email</label><input type="email" name="email" class="form-control"></div>
<div class="col-md-2"><label class="form-label">Experience (yr)</label><input type="number" name="experience_years" class="form-control"></div>
<div class="col-md-2"><label class="form-label">Hourly Rate</label><input type="number" name="hourly_rate" class="form-control" step="0.01"></div>
<div class="col-md-4"><label class="form-label">Photo</label><input type="file" name="photo" class="form-control" accept="image/*"></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/coaches" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\coaches\edit.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">Edit Coach</h5></div><div class="card-body">
<form method="POST" action="/sports/coaches/<?=$coach['id']?>" data-ajax="true" enctype="multipart/form-data"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-6"><label class="form-label">First Name (EN)</label><input type="text" name="first_name_en" class="form-control" value="<?=e($coach['first_name_en'])?>" required></div>
<div class="col-md-6"><label class="form-label">Last Name (EN)</label><input type="text" name="last_name_en" class="form-control" value="<?=e($coach['last_name_en'])?>" required></div>
<div class="col-md-6"><label class="form-label">First Name (AR)</label><input type="text" name="first_name_ar" class="form-control" dir="rtl" value="<?=e($coach['first_name_ar']??'')?>"></div>
<div class="col-md-6"><label class="form-label">Last Name (AR)</label><input type="text" name="last_name_ar" class="form-control" dir="rtl" value="<?=e($coach['last_name_ar']??'')?>"></div>
<div class="col-md-4"><label class="form-label">Phone</label><input type="text" name="phone" class="form-control" value="<?=e($coach['phone']??'')?>"></div>
<div class="col-md-4"><label class="form-label">Email</label><input type="email" name="email" class="form-control" value="<?=e($coach['email']??'')?>"></div>
<div class="col-md-2"><label class="form-label">Exp (yr)</label><input type="number" name="experience_years" class="form-control" value="<?=$coach['experience_years']?>"></div>
<div class="col-md-2"><label class="form-label">Rate/hr</label><input type="number" name="hourly_rate" class="form-control" step="0.01" value="<?=$coach['hourly_rate']?>"></div>
<div class="col-md-4"><label class="form-label">Status</label><select name="status" class="form-select"><?php foreach(['active','inactive','on_leave','terminated'] as $st):?><option value="<?=$st?>" <?=selected($st,$coach['status'])?>><?=ucfirst(str_replace('_',' ',$st))?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Photo</label><input type="file" name="photo" class="form-control" accept="image/*"></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/coaches/<?=$coach['id']?>" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\coaches\index.php

```php
<?php $this->push('styles')?><link rel="stylesheet" href="<?=module_asset('sports','css/sports.css')?>"><?php $this->endPush()?>
<div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0"><i class="fas fa-user-tie me-2"></i><?=lang('sports.coaches')?></h4>
<?php if(auth_can('sports.manage')):?><a href="/sports/coaches/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>New Coach</a><?php endif;?></div>
<div class="card"><div class="card-body">
<table id="coaches-table" class="table table-striped w-100" data-url="/api/sports/coaches/datatable"
    data-columns='[{"data":"coach_code","title":"Code"},{"data":"full_name","title":"Name","orderable":false},{"data":"phone","title":"Phone"},{"data":"experience_years","title":"Exp (yr)"},{"data":"status_badge","title":"Status","orderable":false},{"data":"actions","title":"","orderable":false}]'></table>
</div></div>
<?php $this->push('scripts')?><script>document.addEventListener('DOMContentLoaded',()=>DataTableHelper.init('coaches-table'));</script><?php $this->endPush()?>
```

## File: modules\sports\views\coaches\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0"><i class="fas fa-user-tie me-2"></i><?=e($coach['first_name_en'].' '.$coach['last_name_en'])?></h4>
<div><?php if(auth_can('sports.manage')):?><a href="/sports/coaches/<?=$coach['id']?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a><?php endif;?> <a href="/sports/coaches" class="btn btn-secondary btn-sm">Back</a></div></div>
<div class="row g-3">
<div class="col-md-4"><div class="card"><div class="card-body text-center">
<?php if($coach['photo_path']):?><img src="/<?=e($coach['photo_path'])?>" class="rounded-circle mb-3" style="width:120px;height:120px;object-fit:cover"><?php else:?><i class="fas fa-user-circle fa-5x text-muted mb-3"></i><?php endif;?>
<h5><?=e($coach['first_name_en'].' '.$coach['last_name_en'])?></h5>
<p class="text-muted"><?=e($coach['first_name_ar']??'')?> <?=e($coach['last_name_ar']??'')?></p>
<?=status_badge($coach['status'])?>
<table class="table table-sm text-start mt-3"><tr><th>Code</th><td><?=e($coach['coach_code'])?></td></tr><tr><th>Phone</th><td><?=e($coach['phone']??'')?></td></tr><tr><th>Email</th><td><?=e($coach['email']??'')?></td></tr><tr><th>Experience</th><td><?=$coach['experience_years']??'N/A'?> years</td></tr><tr><th>Hourly Rate</th><td><?=$coach['hourly_rate']?format_money($coach['hourly_rate']):'N/A'?></td></tr></table>
</div></div></div>
<div class="col-md-8">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-classes">Classes (<?=count($classes)?>)</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-certs">Certifications (<?=count($certifications)?>)</a></li></ul>
<div class="tab-content border border-top-0 p-3">
<div class="tab-pane fade show active" id="tab-classes">
<?php if(empty($classes)):?><p class="text-muted">No classes assigned</p><?php else:?>
<table class="table table-sm"><thead><tr><th>Code</th><th>Name</th><th>Sport</th><th>Status</th></tr></thead><tbody>
<?php foreach($classes as $cl):?><tr><td><?=e($cl['class_code'])?></td><td><a href="/sports/classes/<?=$cl['id']?>"><?=e($cl['name_en'])?></a></td><td><?=e($cl['sport_name']??'')?></td><td><?=status_badge($cl['status'])?></td></tr><?php endforeach;?>
</tbody></table><?php endif;?>
</div>
<div class="tab-pane fade" id="tab-certs">
<?php if(empty($certifications)):?><p class="text-muted">No certifications</p><?php else:?>
<table class="table table-sm"><thead><tr><th>Certification</th><th>Issuing Body</th><th>Date</th><th>Expiry</th><th>Verified</th></tr></thead><tbody>
<?php foreach($certifications as $cert):?><tr><td><?=e($cert['certification_name'])?></td><td><?=e($cert['issuing_body']??'')?></td><td><?=format_date($cert['issue_date'])?></td><td><?=format_date($cert['expiry_date'])?></td><td><?=$cert['is_verified']?'<i class="fas fa-check text-success"></i>':'<i class="fas fa-times text-muted"></i>'?></td></tr><?php endforeach;?>
</tbody></table><?php endif;?>
</div></div></div></div>
```

## File: modules\sports\views\leagues\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">New League</h5></div><div class="card-body">
<form method="POST" action="/sports/leagues" data-ajax="true"><?=csrf_field()?>
<div class="row g-3

---

Right, that file got cut off mid-sentence like a cheap cell signal. Let me rewrite it properly and deliver every single remaining file.
```

## File: modules\sports\views\leagues\index.php

```php
<?php $this->push('styles')?><link rel="stylesheet" href="<?=module_asset('sports','css/sports.css')?>"><?php $this->endPush()?>
<div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0"><i class="fas fa-trophy me-2"></i><?=lang('sports.leagues')?></h4>
<?php if(auth_can('sports.manage')):?><a href="/sports/leagues/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>New League</a><?php endif;?></div>
<div class="card"><div class="card-body">
<table id="leagues-table" class="table table-striped w-100" data-url="/api/sports/leagues/datatable"
    data-columns='[{"data":"league_code","title":"Code"},{"data":"name_en","title":"League"},{"data":"sport_name","title":"Sport"},{"data":"season_name","title":"Season"},{"data":"league_type","title":"Type"},{"data":"team_count","title":"Teams"},{"data":"status_badge","title":"Status","orderable":false},{"data":"actions","title":"","orderable":false}]'></table>
</div></div>
<?php $this->push('scripts')?><script>document.addEventListener('DOMContentLoaded',()=>DataTableHelper.init('leagues-table'));</script><?php $this->endPush()?>
```

## File: modules\sports\views\leagues\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0"><i class="fas fa-trophy me-2"></i><?=e($league['name_en'])?></h4>
<div><?php if(auth_can('sports.manage')):?><a href="/sports/leagues/<?=$league['id']?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a><?php endif;?> <a href="/sports/leagues" class="btn btn-secondary btn-sm">Back</a></div></div>
<div class="row g-3">
<div class="col-md-4"><div class="card"><div class="card-body">
<table class="table table-sm mb-0"><tr><th>Code</th><td><?=e($league['league_code'])?></td></tr><tr><th>Sport</th><td><?=e($league['sport_name']??'')?></td></tr><tr><th>Season</th><td><?=e($league['season_name']??'')?></td></tr><tr><th>Type</th><td><?=ucfirst(str_replace('_',' ',$league['league_type']))?></td></tr><tr><th>Dates</th><td><?=format_date($league['start_date'])?> â€” <?=format_date($league['end_date'])?></td></tr><tr><th>Status</th><td><?=status_badge($league['status'])?></td></tr></table>
<?php if(!empty($availableTeams) && auth_can('sports.manage')):?>
<hr><form method="POST" action="/sports/leagues/<?=$league['id']?>/register-team" data-ajax="true"><?=csrf_field()?>
<div class="row g-2"><div class="col-8"><select name="team_id" class="form-select form-select-sm" required><option value="">-- Add Team --</option><?php foreach($availableTeams as $t):?><option value="<?=$t['id']?>"><?=e($t['name_en'])?></option><?php endforeach;?></select></div>
<div class="col-4"><button type="submit" class="btn btn-primary btn-sm w-100">Add</button></div></div></form>
<?php endif;?>
</div></div></div>
<div class="col-md-8">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-standings">Standings</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-fixtures">Fixtures</a></li></ul>
<div class="tab-content border border-top-0 p-3">
<div class="tab-pane fade show active" id="tab-standings">
<table class="table table-sm table-hover"><thead><tr><th>#</th><th>Team</th><th>P</th><th>W</th><th>D</th><th>L</th><th>GF</th><th>GA</th><th>GD</th><th>Pts</th></tr></thead><tbody>
<?php foreach($standings as $i=>$s):?>
<tr><td><?=$i+1?></td><td><?=e($s['team_name']??'')?></td><td><?=$s['played']?></td><td><?=$s['won']?></td><td><?=$s['drawn']?></td><td><?=$s['lost']?></td><td><?=$s['goals_for']?></td><td><?=$s['goals_against']?></td><td><?=$s['goal_difference']?></td><td><strong><?=$s['points']?></strong></td></tr>
<?php endforeach;?>
<?php if(empty($standings)):?><tr><td colspan="10" class="text-muted text-center">No teams registered</td></tr><?php endif;?>
</tbody></table>
</div>
<div class="tab-pane fade" id="tab-fixtures">
<table class="table table-sm"><thead><tr><th>Date</th><th>Home</th><th>Score</th><th>Away</th><th>Status</th></tr></thead><tbody>
<?php foreach($matches as $m):?>
<tr><td><?=format_date($m['match_date'])?></td><td><?=e($m['home_name']??'TBD')?></td><td><?=$m['status']==='completed'?$m['home_score'].' - '.$m['away_score']:'-'?></td><td><?=e($m['away_name']??'TBD')?></td><td><?=status_badge($m['status'])?></td></tr>
<?php endforeach;?>
<?php if(empty($matches)):?><tr><td colspan="5" class="text-muted text-center">No fixtures</td></tr><?php endif;?>
</tbody></table>
</div></div></div></div>
```

## File: modules\sports\views\matches\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">New Match</h5></div><div class="card-body">
<form method="POST" action="/sports/matches" data-ajax="true"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-6"><label class="form-label">Sport <span class="text-danger">*</span></label><select name="sport_id" class="form-select" required id="matchSport"><option value="">--</option><?php foreach($sports as $s):?><option value="<?=$s['id']?>"><?=e($s['name_en'])?></option><?php endforeach;?></select></div>
<div class="col-md-6"><label class="form-label">Type</label><select name="match_type" class="form-select"><?php foreach(['league','friendly','tournament','playoff','final','exhibition'] as $t):?><option value="<?=$t?>"><?=ucfirst($t)?></option><?php endforeach;?></select></div>
<div class="col-md-6"><label class="form-label">Home Team <span class="text-danger">*</span></label><select name="home_team_id" class="form-select" required id="homeTeam"><option value="">-- Select sport first --</option></select></div>
<div class="col-md-6"><label class="form-label">Away Team <span class="text-danger">*</span></label><select name="away_team_id" class="form-select" required id="awayTeam"><option value="">-- Select sport first --</option></select></div>
<div class="col-md-4"><label class="form-label">Date <span class="text-danger">*</span></label><input type="date" name="match_date" class="form-control datepicker" required></div>
<div class="col-md-4"><label class="form-label">Start Time</label><input type="time" name="start_time" class="form-control"></div>
<div class="col-md-4"><label class="form-label">Facility</label><select name="facility_id" class="form-select"><option value="">--</option><?php foreach($facilities as $f):?><option value="<?=$f['id']?>"><?=e($f['name_en'])?></option><?php endforeach;?></select></div>
<div class="col-md-6"><label class="form-label">League</label><select name="league_id" class="form-select"><option value="">None</option><?php foreach($leagues as $l):?><option value="<?=$l['id']?>"><?=e($l['name'])?></option><?php endforeach;?></select></div>
<div class="col-md-3"><label class="form-label">Round</label><input type="number" name="round_number" class="form-control"></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/matches" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
<script>
document.getElementById('matchSport').addEventListener('change',function(){
    const sportId=this.value;if(!sportId)return;
    fetch('/api/sports/teams/list?sport_id='+sportId).then(r=>r.json()).then(teams=>{
        ['homeTeam','awayTeam'].forEach(id=>{
            const sel=document.getElementById(id);sel.innerHTML='<option value="">--</option>';
            teams.forEach(t=>{sel.innerHTML+='<option value="'+t.id+'">'+t.name_en+'</option>';});
        });
    });
});
</script>
```

## File: modules\sports\views\matches\index.php

```php
<?php $this->push('styles')?><link rel="stylesheet" href="<?=module_asset('sports','css/sports.css')?>"><?php $this->endPush()?>
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-futbol me-2"></i><?=lang('sports.matches')?></h4>
    <?php if(auth_can('sports.manage')):?><a href="/sports/matches/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>New Match</a><?php endif;?>
</div>
<div class="card mb-3"><div class="card-body py-2"><form class="row g-2" data-filter-table="matches-table">
<div class="col-md-3"><select name="sport_id" class="form-select form-select-sm" onchange="DataTableHelper.reload('matches-table')"><option value="">All Sports</option><?php foreach($sports as $s):?><option value="<?=$s['id']?>"><?=e($s['name_en'])?></option><?php endforeach;?></select></div>
<div class="col-md-3"><select name="status" class="form-select form-select-sm" onchange="DataTableHelper.reload('matches-table')"><option value="">All Status</option><?php foreach(['scheduled','in_progress','completed','postponed','cancelled'] as $st):?><option value="<?=$st?>"><?=ucfirst(str_replace('_',' ',$st))?></option><?php endforeach;?></select></div>
</form></div></div>
<div class="card"><div class="card-body">
<table id="matches-table" class="table table-striped table-hover w-100" data-url="/api/sports/matches/datatable"
    data-columns='[{"data":"match_number","title":"Match#"},{"data":"sport_name","title":"Sport"},{"data":"home_name","title":"Home"},{"data":"score","title":"Score"},{"data":"away_name","title":"Away"},{"data":"match_date","title":"Date"},{"data":"facility_name","title":"Venue"},{"data":"status_badge","title":"Status","orderable":false},{"data":"actions","title":"","orderable":false}]'></table>
</div></div>
<?php $this->push('scripts')?><script src="<?=module_asset('sports','js/sports.js')?>"></script><script>document.addEventListener('DOMContentLoaded',()=>DataTableHelper.init('matches-table'));</script><?php $this->endPush()?>
```

## File: modules\sports\views\matches\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-futbol me-2"></i>Match <?=e($match['match_number'])?></h4>
    <a href="/sports/matches" class="btn btn-secondary btn-sm">Back</a>
</div>
<div class="row g-3">
<div class="col-md-8">
    <div class="card"><div class="card-body text-center">
        <div class="row align-items-center">
            <div class="col-md-4"><h5><?=e($match['home_name']??'TBD')?></h5><span class="text-muted">Home</span></div>
            <div class="col-md-4">
                <?php if($match['status']==='completed'):?><h2 class="display-6"><?=$match['home_score']?> - <?=$match['away_score']?></h2>
                <?php else:?><h4 class="text-muted">vs</h4><?php endif;?>
                <div><?=status_badge($match['status'])?></div>
                <small class="text-muted"><?=format_date($match['match_date'])?> <?=$match['start_time']?substr($match['start_time'],0,5):''?></small>
            </div>
            <div class="col-md-4"><h5><?=e($match['away_name']??'TBD')?></h5><span class="text-muted">Away</span></div>
        </div>
    </div></div>
    <?php if($match['status']!=='completed' && auth_can('sports.manage')):?>
    <div class="card mt-3"><div class="card-header">Record Result</div><div class="card-body">
        <form method="POST" action="/sports/matches/<?=$match['id']?>/result" data-ajax="true"><?=csrf_field()?>
        <div class="row g-2 align-items-end">
            <div class="col-md-4"><label class="form-label">Home Score</label><input type="number" name="home_score" class="form-control" min="0" value="0"></div>
            <div class="col-md-4"><label class="form-label">Away Score</label><input type="number" name="away_score" class="form-control" min="0" value="0"></div>
            <div class="col-md-4"><button type="submit" class="btn btn-success w-100"><i class="fas fa-check me-1"></i>Save Result</button></div>
        </div></form>
    </div></div>
    <?php endif;?>
    <?php if(!empty($events)):?>
    <div class="card mt-3"><div class="card-header">Match Events</div><div class="card-body p-0">
        <table class="table table-sm mb-0"><thead><tr><th>Min</th><th>Event</th><th>Team</th><th>Player</th></tr></thead><tbody>
        <?php foreach($events as $ev):?>
        <tr><td><?=$ev['event_minute']?>'</td><td><?=ucfirst(str_replace('_',' ',$ev['event_type']))?></td><td><?=e($ev['team_name']??'')?></td><td><?=e($ev['member_name']??'')?></td></tr>
        <?php endforeach;?>
        </tbody></table>
    </div></div>
    <?php endif;?>
</div>
<div class="col-md-4"><div class="card"><div class="card-body">
    <table class="table table-sm mb-0">
        <tr><th>Sport</th><td><?=e($match['sport_name']??'')?></td></tr>
        <tr><th>Type</th><td><?=ucfirst($match['match_type'])?></td></tr>
        <tr><th>Venue</th><td><?=e($match['facility_name']??'TBD')?></td></tr>
        <?php if($match['league_name']):?><tr><th>League</th><td><?=e($match['league_name'])?></td></tr><?php endif;?>
        <?php if($match['round_number']):?><tr><th>Round</th><td><?=$match['round_number']?></td></tr><?php endif;?>
        <?php if($match['referee_name']):?><tr><th>Referee</th><td><?=e($match['referee_name'])?></td></tr><?php endif;?>
    </table>
</div></div></div>
</div>
```

## File: modules\sports\views\seasons\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">New Season</h5></div><div class="card-body">
<form method="POST" action="/sports/seasons" data-ajax="true"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-4"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="season_code" class="form-control" required><div class="invalid-feedback" data-field="season_code"></div></div>
<div class="col-md-4"><label class="form-label">Sport <span class="text-danger">*</span></label><select name="sport_id" class="form-select" required><option value="">--</option><?php foreach($sports as $s):?><option value="<?=$s['id']?>"><?=e($s['name_en'])?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Status</label><select name="status" class="form-select"><?php foreach(['upcoming','registration','active','completed','cancelled'] as $st):?><option value="<?=$st?>"><?=ucfirst($st)?></option><?php endforeach;?></select></div>
<div class="col-md-6"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" dir="rtl" required></div>
<div class="col-md-3"><label class="form-label">Start Date</label><input type="date" name="start_date" class="form-control datepicker" required></div>
<div class="col-md-3"><label class="form-label">End Date</label><input type="date" name="end_date" class="form-control datepicker" required></div>
<div class="col-md-3"><label class="form-label">Reg Start</label><input type="date" name="registration_start" class="form-control datepicker"></div>
<div class="col-md-3"><label class="form-label">Reg End</label><input type="date" name="registration_end" class="form-control datepicker"></div>
<div class="col-md-3"><label class="form-label">Max Participants</label><input type="number" name="max_participants" class="form-control"></div>
<div class="col-md-3"><label class="form-label">Fee</label><input type="number" name="fee_amount" class="form-control" step="0.01" value="0"></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/seasons" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\seasons\edit.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">Edit Season</h5></div><div class="card-body">
<form method="POST" action="/sports/seasons/<?=$season['id']?>" data-ajax="true"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-6"><label class="form-label">Name (EN)</label><input type="text" name="name_en" class="form-control" value="<?=e($season['name_en'])?>" required></div>
<div class="col-md-6"><label class="form-label">Name (AR)</label><input type="text" name="name_ar" class="form-control" dir="rtl" value="<?=e($season['name_ar'])?>" required></div>
<div class="col-md-3"><label class="form-label">Start Date</label><input type="date" name="start_date" class="form-control" value="<?=$season['start_date']?>" required></div>
<div class="col-md-3"><label class="form-label">End Date</label><input type="date" name="end_date" class="form-control" value="<?=$season['end_date']?>" required></div>
<div class="col-md-3"><label class="form-label">Reg Start</label><input type="date" name="registration_start" class="form-control" value="<?=$season['registration_start']?>"></div>
<div class="col-md-3"><label class="form-label">Reg End</label><input type="date" name="registration_end" class="form-control" value="<?=$season['registration_end']?>"></div>
<div class="col-md-4"><label class="form-label">Status</label><select name="status" class="form-select"><?php foreach(['upcoming','registration','active','completed','cancelled'] as $st):?><option value="<?=$st?>" <?=selected($st,$season['status'])?>><?=ucfirst($st)?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Max Participants</label><input type="number" name="max_participants" class="form-control" value="<?=$season['max_participants']?>"></div>
<div class="col-md-4"><label class="form-label">Fee</label><input type="number" name="fee_amount" class="form-control" step="0.01" value="<?=$season['fee_amount']?>"></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/seasons" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\seasons\index.php

```php
<?php $this->push('styles')?><link rel="stylesheet" href="<?=module_asset('sports','css/sports.css')?>"><?php $this->endPush()?>
<div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0"><i class="fas fa-calendar me-2"></i><?=lang('sports.seasons')?></h4>
<?php if(auth_can('sports.manage')):?><a href="/sports/seasons/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>New Season</a><?php endif;?></div>
<div class="card"><div class="card-body">
<table id="seasons-table" class="table table-striped w-100" data-url="/api/sports/seasons/datatable"
    data-columns='[{"data":"season_code","title":"Code"},{"data":"name_en","title":"Season"},{"data":"sport_name","title":"Sport"},{"data":"start_date","title":"Start"},{"data":"end_date","title":"End"},{"data":"status_badge","title":"Status","orderable":false},{"data":"actions","title":"","orderable":false}]'></table>
</div></div>
<?php $this->push('scripts')?><script>document.addEventListener('DOMContentLoaded',()=>DataTableHelper.init('seasons-table'));</script><?php $this->endPush()?>
```

## File: modules\sports\views\sports\create.php

```php
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-plus me-2"></i><?= lang('sports.create') ?></h5></div>
    <div class="card-body">
        <form method="POST" action="/sports" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4">
                    <label class="form-label">Sport Code <span class="text-danger">*</span></label>
                    <input type="text" name="sport_code" class="form-control" required maxlength="20">
                    <div class="invalid-feedback" data-field="sport_code"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Category <span class="text-danger">*</span></label>
                    <select name="category_id" class="form-select" required>
                        <option value="">-- Select --</option>
                        <?php foreach ($categories as $c): ?>
                        <option value="<?= $c['id'] ?>"><?= e($c['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <div class="invalid-feedback" data-field="category_id"></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Gender Restriction</label>
                    <select name="gender_restriction" class="form-select">
                        <option value="both">Both</option>
                        <option value="male">Male</option>
                        <option value="female">Female</option>
                        <option value="mixed">Mixed</option>
                    </select>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Name (English) <span class="text-danger">*</span></label>
                    <input type="text" name="name_en" class="form-control" required>
                    <div class="invalid-feedback" data-field="name_en"></div>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Name (Arabic) <span class="text-danger">*</span></label>
                    <input type="text" name="name_ar" class="form-control" dir="rtl" required>
                    <div class="invalid-feedback" data-field="name_ar"></div>
                </div>
                <div class="col-12">
                    <label class="form-label">Description (English)</label>
                    <textarea name="description_en" class="form-control" rows="3"></textarea>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Min Players</label>
                    <input type="number" name="min_players" class="form-control" min="1">
                </div>
                <div class="col-md-4">
                    <label class="form-label">Max Players</label>
                    <input type="number" name="max_players" class="form-control" min="1">
                </div>
                <div class="col-md-4">
                    <label class="form-label">Sort Order</label>
                    <input type="number" name="sort_order" class="form-control" value="0">
                </div>
                <div class="col-md-4">
                    <div class="form-check mt-4">
                        <input type="checkbox" name="team_sport" value="1" class="form-check-input" id="teamSport">
                        <label class="form-check-label" for="teamSport">Team Sport</label>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="form-check mt-4">
                        <input type="checkbox" name="requires_medical_clearance" value="1" class="form-check-input" id="medClear">
                        <label class="form-check-label" for="medClear">Requires Medical Clearance</label>
                    </div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/sports" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div>
</div>
</div>
```

## File: modules\sports\views\sports\edit.php

```php
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-edit me-2"></i>Edit Sport</h5></div>
    <div class="card-body">
        <form method="POST" action="/sports/<?= $sport['id'] ?>" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3">
                <div class="col-md-4">
                    <label class="form-label">Sport Code</label>
                    <input type="text" class="form-control" value="<?= e($sport['sport_code']) ?>" disabled>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Category <span class="text-danger">*</span></label>
                    <select name="category_id" class="form-select" required>
                        <?php foreach ($categories as $c): ?>
                        <option value="<?= $c['id'] ?>" <?= selected($c['id'], $sport['category_id']) ?>><?= e($c['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-4">
                    <label class="form-label">Gender</label>
                    <select name="gender_restriction" class="form-select">
                        <?php foreach (['both','male','female','mixed'] as $g): ?>
                        <option value="<?= $g ?>" <?= selected($g, $sport['gender_restriction']) ?>><?= ucfirst($g) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Name (EN) <span class="text-danger">*</span></label>
                    <input type="text" name="name_en" class="form-control" value="<?= e($sport['name_en']) ?>" required>
                </div>
                <div class="col-md-6">
                    <label class="form-label">Name (AR) <span class="text-danger">*</span></label>
                    <input type="text" name="name_ar" class="form-control" dir="rtl" value="<?= e($sport['name_ar']) ?>" required>
                </div>
                <div class="col-12">
                    <label class="form-label">Description</label>
                    <textarea name="description_en" class="form-control" rows="3"><?= e($sport['description_en'] ?? '') ?></textarea>
                </div>
                <div class="col-md-3"><label class="form-label">Min Players</label><input type="number" name="min_players" class="form-control" value="<?= $sport['min_players'] ?>"></div>
                <div class="col-md-3"><label class="form-label">Max Players</label><input type="number" name="max_players" class="form-control" value="<?= $sport['max_players'] ?>"></div>
                <div class="col-md-3"><label class="form-label">Sort Order</label><input type="number" name="sort_order" class="form-control" value="<?= $sport['sort_order'] ?>"></div>
                <div class="col-md-3">
                    <div class="form-check mt-4"><input type="checkbox" name="team_sport" value="1" class="form-check-input" <?= checked(1, $sport['team_sport']) ?>><label class="form-check-label">Team Sport</label></div>
                    <div class="form-check"><input type="checkbox" name="is_active" value="1" class="form-check-input" <?= checked(1, $sport['is_active']) ?>><label class="form-check-label">Active</label></div>
                    <div class="form-check"><input type="checkbox" name="requires_medical_clearance" value="1" class="form-check-input" <?= checked(1, $sport['requires_medical_clearance']) ?>><label class="form-check-label">Medical Clearance</label></div>
                </div>
            </div>
            <div class="mt-4 d-flex gap-2">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                <a href="/sports" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
            </div>
        </form>
    </div>
</div>
</div>
</div>
```

## File: modules\sports\views\sports\index.php

```php
<?php $this->push('styles') ?>
<link rel="stylesheet" href="<?= module_asset('sports', 'css/sports.css') ?>">
<?php $this->endPush() ?>

<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-running me-2"></i><?= lang('sports.title') ?></h4>
    <div>
        <?php if (auth_can('sports.manage')): ?>
        <a href="/sports/categories" class="btn btn-outline-secondary btn-sm me-1"><i class="fas fa-tags me-1"></i><?= lang('sports.categories') ?></a>
        <a href="/sports/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i><?= lang('sports.create') ?></a>
        <?php endif; ?>
    </div>
</div>

<?php if (!empty($categories)): ?>
<ul class="nav nav-pills mb-3" id="sportCatTabs">
    <li class="nav-item"><a class="nav-link active" data-bs-toggle="pill" href="#cat-all">All</a></li>
    <?php foreach ($categories as $cat): ?>
    <li class="nav-item"><a class="nav-link" data-bs-toggle="pill" href="#cat-<?= $cat['id'] ?>"><?= e(current_lang() === 'ar' ? $cat['name_ar'] : $cat['name_en']) ?></a></li>
    <?php endforeach; ?>
</ul>
<?php endif; ?>

<div class="row g-3">
<?php foreach ($sports as $sport): ?>
    <div class="col-md-4 col-lg-3 sport-card" data-category="<?= $sport['category_id'] ?>">
        <div class="card h-100 shadow-sm">
            <?php if ($sport['image_path']): ?>
            <img src="<?= e($sport['image_path']) ?>" class="card-img-top" alt="" style="height:140px;object-fit:cover">
            <?php else: ?>
            <div class="card-img-top bg-light d-flex align-items-center justify-content-center" style="height:140px">
                <i class="fas fa-futbol fa-3x text-muted"></i>
            </div>
            <?php endif; ?>
            <div class="card-body">
                <h6 class="card-title mb-1"><?= e($sport['name_en']) ?></h6>
                <small class="text-muted"><?= e($sport['category_name'] ?? '') ?></small>
                <div class="mt-2">
                    <span class="badge bg-primary"><?= (int)$sport['active_classes'] ?> classes</span>
                    <span class="badge bg-info"><?= (int)$sport['active_teams'] ?> teams</span>
                    <?php if ($sport['team_sport']): ?><span class="badge bg-secondary">Team</span><?php endif; ?>
                </div>
            </div>
            <div class="card-footer bg-transparent border-top-0">
                <a href="/sports/view/<?= $sport['id'] ?>" class="btn btn-sm btn-outline-primary w-100"><i class="fas fa-eye me-1"></i>View</a>
            </div>
        </div>
    </div>
<?php endforeach; ?>
<?php if (empty($sports)): ?>
    <div class="col-12"><div class="empty-state"><i class="fas fa-running"></i><p>No sports configured yet</p></div></div>
<?php endif; ?>
</div>

<?php $this->push('scripts') ?>
<script src="<?= module_asset('sports', 'js/sports.js') ?>"></script>
<script>
document.querySelectorAll('#sportCatTabs .nav-link').forEach(tab => {
    tab.addEventListener('click', function() {
        const target = this.getAttribute('href').replace('#cat-','');
        document.querySelectorAll('.sport-card').forEach(card => {
            card.style.display = (target === 'all' || card.dataset.category === target) ? '' : 'none';
        });
    });
});
</script>
<?php $this->endPush() ?>
```

## File: modules\sports\views\sports\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-futbol me-2"></i><?= e($sport['name_en']) ?></h4>
    <div>
        <?php if (auth_can('sports.manage')): ?>
        <a href="/sports/<?= $sport['id'] ?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a>
        <?php endif; ?>
        <a href="/sports" class="btn btn-secondary btn-sm"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-3">
    <div class="col-md-4">
        <div class="card">
            <div class="card-body text-center">
                <?php if ($sport['image_path']): ?>
                <img src="<?= e($sport['image_path']) ?>" class="img-fluid rounded mb-3" style="max-height:200px">
                <?php else: ?>
                <i class="fas fa-futbol fa-5x text-muted my-3"></i>
                <?php endif; ?>
                <h5><?= e($sport['name_en']) ?></h5>
                <p class="text-muted mb-1"><?= e($sport['name_ar'] ?? '') ?></p>
                <p><span class="badge bg-info"><?= e($sport['category_name'] ?? '') ?></span></p>
                <table class="table table-sm text-start">
                    <tr><th>Code</th><td><?= e($sport['sport_code']) ?></td></tr>
                    <tr><th>Type</th><td><?= $sport['team_sport'] ? 'Team Sport' : 'Individual' ?></td></tr>
                    <tr><th>Gender</th><td><?= ucfirst($sport['gender_restriction']) ?></td></tr>
                    <?php if ($sport['min_players'] || $sport['max_players']): ?>
                    <tr><th>Players</th><td><?= $sport['min_players'] ?? '?' ?> - <?= $sport['max_players'] ?? '?' ?></td></tr>
                    <?php endif; ?>
                    <tr><th>Active</th><td><?= $sport['is_active'] ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>' ?></td></tr>
                </table>
            </div>
        </div>
    </div>
    <div class="col-md-8">
        <ul class="nav nav-tabs" id="sportTabs">
            <li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-classes">Classes (<?= count($classes) ?>)</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-teams">Teams (<?= count($teams) ?>)</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-seasons">Seasons</a></li>
            <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-rules">Rules</a></li>
        </ul>
        <div class="tab-content border border-top-0 p-3">
            <div class="tab-pane fade show active" id="tab-classes">
                <?php if (empty($classes)): ?>
                <p class="text-muted">No active classes</p>
                <?php else: ?>
                <div class="table-responsive">
                <table class="table table-sm table-hover">
                    <thead><tr><th>Code</th><th>Name</th><th>Coach</th><th>Type</th><th>Status</th><th></th></tr></thead>
                    <tbody>
                    <?php foreach ($classes as $cl): ?>
                    <tr>
                        <td><?= e($cl['class_code']) ?></td>
                        <td><?= e($cl['name_en']) ?></td>
                        <td><?= e($cl['coach_name'] ?? '-') ?></td>
                        <td><?= ucfirst(str_replace('_',' ',$cl['class_type'])) ?></td>
                        <td><?= status_badge($cl['status']) ?></td>
                        <td><a href="/sports/classes/<?= $cl['id'] ?>" class="btn btn-sm btn-outline-info"><i class="fas fa-eye"></i></a></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
                </div>
                <?php endif; ?>
            </div>
            <div class="tab-pane fade" id="tab-teams">
                <?php if (empty($teams)): ?><p class="text-muted">No active teams</p>
                <?php else: ?>
                <div class="table-responsive">
                <table class="table table-sm table-hover">
                    <thead><tr><th>Code</th><th>Name</th><th>Type</th><th>Players</th><th>W-L-D</th><th></th></tr></thead>
                    <tbody>
                    <?php foreach ($teams as $t): ?>
                    <tr>
                        <td><?= e($t['team_code']) ?></td><td><?= e($t['name_en']) ?></td>
                        <td><?= ucfirst($t['team_type']) ?></td>
                        <td><?= $t['current_players'] ?>/<?= $t['max_players'] ?: 'âˆž' ?></td>
                        <td><?= $t['wins'] ?>-<?= $t['losses'] ?>-<?= $t['draws'] ?></td>
                        <td><a href="/sports/teams/<?= $t['id'] ?>" class="btn btn-sm btn-outline-info"><i class="fas fa-eye"></i></a></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
                </div>
                <?php endif; ?>
            </div>
            <div class="tab-pane fade" id="tab-seasons">
                <?php if (empty($seasons)): ?><p class="text-muted">No seasons</p>
                <?php else: ?>
                <table class="table table-sm">
                    <thead><tr><th>Code</th><th>Name</th><th>Dates</th><th>Status</th></tr></thead>
                    <tbody>
                    <?php foreach ($seasons as $s): ?>
                    <tr><td><?= e($s['season_code']) ?></td><td><?= e($s['name_en']) ?></td><td><?= format_date($s['start_date']) ?> â€” <?= format_date($s['end_date']) ?></td><td><?= status_badge($s['status']) ?></td></tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
                <?php endif; ?>
            </div>
            <div class="tab-pane fade" id="tab-rules">
                <?php if (empty($rules)): ?><p class="text-muted">No rules defined</p>
                <?php else: ?>
                <?php foreach ($rules as $rule): ?>
                <div class="mb-3">
                    <h6><?= e($rule['title_en']) ?></h6>
                    <p class="text-muted small"><?= nl2br(e($rule['description_en'])) ?></p>
                </div>
                <?php endforeach; ?>
                <?php endif; ?>
            </div>
        </div>
    </div>
</div>
```

## File: modules\sports\views\teams\create.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">New Team</h5></div><div class="card-body">
<form method="POST" action="/sports/teams" data-ajax="true"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-4"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="team_code" class="form-control" required><div class="invalid-feedback" data-field="team_code"></div></div>
<div class="col-md-4"><label class="form-label">Sport <span class="text-danger">*</span></label><select name="sport_id" class="form-select" required><option value="">--</option><?php foreach($sports as $s):?><option value="<?=$s['id']?>"><?=e($s['name_en'])?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Type</label><select name="team_type" class="form-select"><?php foreach(['club','amateur','youth','senior','women','mixed'] as $t):?><option value="<?=$t?>"><?=ucfirst($t)?></option><?php endforeach;?></select></div>
<div class="col-md-6"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required></div>
<div class="col-md-6"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" dir="rtl" required></div>
<div class="col-md-4"><label class="form-label">Coach</label><select name="coach_id" class="form-select"><option value="">--</option><?php foreach($coaches as $c):?><option value="<?=$c['id']?>"><?=e($c['name'])?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Max Players</label><input type="number" name="max_players" class="form-control"></div>
<div class="col-md-2"><label class="form-label">Primary Color</label><input type="color" name="color_primary" class="form-control form-control-color"></div>
<div class="col-md-2"><label class="form-label">Secondary</label><input type="color" name="color_secondary" class="form-control form-control-color"></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/teams" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\teams\edit.php

```php
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"><div class="card-header"><h5 class="mb-0">Edit Team</h5></div><div class="card-body">
<form method="POST" action="/sports/teams/<?=$team['id']?>" data-ajax="true"><?=csrf_field()?>
<div class="row g-3">
<div class="col-md-6"><label class="form-label">Name (EN)</label><input type="text" name="name_en" class="form-control" value="<?=e($team['name_en'])?>" required></div>
<div class="col-md-6"><label class="form-label">Name (AR)</label><input type="text" name="name_ar" class="form-control" dir="rtl" value="<?=e($team['name_ar'])?>" required></div>
<div class="col-md-4"><label class="form-label">Type</label><select name="team_type" class="form-select"><?php foreach(['club','amateur','youth','senior','women','mixed'] as $t):?><option value="<?=$t?>" <?=selected($t,$team['team_type'])?>><?=ucfirst($t)?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Coach</label><select name="coach_id" class="form-select"><option value="">--</option><?php foreach($coaches as $c):?><option value="<?=$c['id']?>" <?=selected($c['id'],$team['coach_id'])?>><?=e($c['name'])?></option><?php endforeach;?></select></div>
<div class="col-md-4"><label class="form-label">Max Players</label><input type="number" name="max_players" class="form-control" value="<?=$team['max_players']?>"></div>
<div class="col-md-4"><label class="form-label">Status</label><select name="status" class="form-select"><?php foreach(['active','inactive','disbanded','suspended'] as $st):?><option value="<?=$st?>" <?=selected($st,$team['status'])?>><?=ucfirst($st)?></option><?php endforeach;?></select></div>
</div><div class="mt-4 d-flex gap-2"><button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button><a href="/sports/teams/<?=$team['id']?>" class="btn btn-secondary">Cancel</a></div>
</form></div></div></div></div>
```

## File: modules\sports\views\teams\index.php

```php
<?php $this->push('styles') ?><link rel="stylesheet" href="<?= module_asset('sports', 'css/sports.css') ?>"><?php $this->endPush() ?>
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-users me-2"></i><?= lang('sports.teams') ?></h4>
    <?php if (auth_can('teams.manage')): ?><a href="/sports/teams/create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>New Team</a><?php endif; ?>
</div>
<div class="card"><div class="card-body">
<table id="teams-table" class="table table-striped table-hover w-100" data-url="/api/sports/teams/datatable"
       data-columns='[{"data":"team_code","title":"Code"},{"data":"name_en","title":"Team"},{"data":"sport_name","title":"Sport"},{"data":"team_type","title":"Type"},{"data":"players","title":"Players"},{"data":"record","title":"Record"},{"data":"status_badge","title":"Status","orderable":false},{"data":"actions","title":"","orderable":false}]'></table>
</div></div>
<?php $this->push('scripts') ?><script src="<?= module_asset('sports', 'js/sports.js') ?>"></script><script>document.addEventListener('DOMContentLoaded',()=>DataTableHelper.init('teams-table'));</script><?php $this->endPush() ?>
```

## File: modules\sports\views\teams\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?php if($team['color_primary']): ?><span style="display:inline-block;width:20px;height:20px;background:<?=e($team['color_primary'])?>;border-radius:50%;margin-right:8px"></span><?php endif; ?><?= e($team['name_en']) ?></h4>
    <div><?php if(auth_can('teams.manage')): ?><a href="/sports/teams/<?=$team['id']?>/edit" class="btn btn-warning btn-sm"><i class="fas fa-edit me-1"></i>Edit</a><?php endif; ?> <a href="/sports/teams" class="btn btn-secondary btn-sm">Back</a></div>
</div>
<div class="row g-3">
    <div class="col-md-4"><div class="card"><div class="card-body">
        <table class="table table-sm mb-0">
            <tr><th>Code</th><td><?=e($team['team_code'])?></td></tr>
            <tr><th>Sport</th><td><?=e($team['sport_name']??'')?></td></tr>
            <tr><th>Type</th><td><?=ucfirst($team['team_type'])?></td></tr>
            <tr><th>Coach</th><td><?=e($team['coach_name']??'N/A')?></td></tr>
            <tr><th>Season</th><td><?=e($team['season_name']??'N/A')?></td></tr>
            <tr><th>Players</th><td><?=$team['current_players']?>/<?=$team['max_players']?:'âˆž'?></td></tr>
            <tr><th>Record</th><td><span class="text-success"><?=$team['wins']?>W</span> <span class="text-danger"><?=$team['losses']?>L</span> <span class="text-muted"><?=$team['draws']?>D</span></td></tr>
            <tr><th>Points</th><td><strong><?=$team['points']?></strong></td></tr>
            <tr><th>Status</th><td><?=status_badge($team['status'])?></td></tr>
        </table>
    </div></div></div>
    <div class="col-md-8">
        <ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#tab-roster">Roster (<?=count($members)?>)</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#tab-matches">Recent Matches</a></li></ul>
        <div class="tab-content border border-top-0 p-3">
            <div class="tab-pane fade show active" id="tab-roster">
                <?php if(auth_can('teams.manage')): ?>
                <form method="POST" action="/sports/teams/<?=$team['id']?>/add-member" data-ajax="true" class="row g-2 mb-3">
                    <?=csrf_field()?><div class="col-md-3"><input type="number" name="member_id" class="form-control form-control-sm" placeholder="Member ID" required></div>
                    <div class="col-md-2"><input type="number" name="jersey_number" class="form-control form-control-sm" placeholder="Jersey #"></div>
                    <div class="col-md-3"><input type="text" name="position" class="form-control form-control-sm" placeholder="Position"></div>
                    <div class="col-md-2"><select name="role" class="form-select form-select-sm"><option value="player">Player</option><option value="captain">Captain</option><option value="substitute">Sub</option></select></div>
                    <div class="col-md-2"><button type="submit" class="btn btn-primary btn-sm w-100"><i class="fas fa-plus"></i></button></div>
                </form>
                <?php endif; ?>
                <table class="table table-sm table-hover">
                    <thead><tr><th>#</th><th>Player</th><th>Position</th><th>Role</th><th></th></tr></thead>
                    <tbody>
                    <?php foreach($members as $m): ?>
                    <tr><td><?=e($m['jersey_number']??'-')?></td><td><?=e($m['member_name'])?> <small class="text-muted"><?=e($m['membership_number'])?></small></td><td><?=e($m['position']??'')?></td><td><span class="badge bg-<?=$m['role']==='captain'?'warning':'secondary'?>"><?=ucfirst($m['role'])?></span></td>
                    <td><?php if(auth_can('teams.manage')): ?><form method="POST" action="/sports/teams/<?=$team['id']?>/remove-member" data-ajax="true" style="display:inline"><?=csrf_field()?><input type="hidden" name="team_member_id" value="<?=$m['id']?>"><button type="submit" class="btn btn-sm btn-outline-danger"><i class="fas fa-user-minus"></i></button></form><?php endif; ?></td></tr>
                    <?php endforeach; ?>
                    <?php if(empty($members)): ?><tr><td colspan="5" class="text-muted text-center">No players</td></tr><?php endif; ?>
                    </tbody>
                </table>
            </div>
            <div class="tab-pane fade" id="tab-matches">
                <table class="table table-sm"><thead><tr><th>Date</th><th>Home</th><th>Score</th><th>Away</th><th>Status</th></tr></thead><tbody>
                <?php foreach($recentMatches as $rm): ?>
                <tr><td><?=format_date($rm['match_date'])?></td><td><?=e($rm['home_name']??'')?></td><td><?=$rm['status']==='completed'?$rm['home_score'].' - '.$rm['away_score']:'-'?></td><td><?=e($rm['away_name']??'')?></td><td><?=status_badge($rm['status'])?></td></tr>
                <?php endforeach; ?>
                <?php if(empty($recentMatches)): ?><tr><td colspan="5" class="text-muted text-center">No matches</td></tr><?php endif; ?>
                </tbody></table>
            </div>
        </div>
    </div>
</div>
```

## File: modules\subscriptions\Controllers\FreezeApiController.php

```php
<?php

class FreezeApiController extends Controller
{
    public function datatable(Request $request): void
    {
        $db     = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';
        $status = $request->get('status', '');

        $where  = "1=1";
        $params = [];

        if ($status !== '') { $where .= " AND mf.status = ?"; $params[] = $status; }
        if ($search !== '') {
            $where .= " AND (m.membership_number LIKE ? OR m.full_name_en LIKE ? OR mf.freeze_number LIKE ?)";
            $s = "%{$search}%"; $params = array_merge($params, [$s, $s, $s]);
        }

        $total = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes mf JOIN members m ON mf.member_id = m.id WHERE {$where}", $params);

        $rows = $db->fetchAll(
            "SELECT mf.id, mf.freeze_number, mf.freeze_reason, mf.start_date, mf.original_end_date, mf.actual_end_date,
                    mf.duration_days, mf.status, mf.fee_amount, mf.fee_paid,
                    m.membership_number, m.full_name_en AS member_name
             FROM member_freezes mf JOIN members m ON mf.member_id = m.id
             WHERE {$where} ORDER BY mf.created_at DESC LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '<a href="/freezes/' . $r['id'] . '" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i></a>';
            if ($r['status'] === 'pending' && Gate::can('freezes.approve')) {
                $actions .= ' <button class="btn btn-sm btn-outline-success btn-approve-freeze" data-id="' . $r['id'] . '"><i class="fas fa-check"></i></button>';
                $actions .= ' <button class="btn btn-sm btn-outline-danger btn-reject-freeze" data-id="' . $r['id'] . '"><i class="fas fa-times"></i></button>';
            }
            $data[] = [
                'freeze_number'     => e($r['freeze_number']),
                'membership_number' => e($r['membership_number']),
                'member_name'       => e($r['member_name']),
                'reason'            => str_limit(e($r['freeze_reason']), 40),
                'start_date'        => format_date($r['start_date']),
                'end_date'          => format_date($r['actual_end_date'] ?? $r['original_end_date']),
                'duration'          => $r['duration_days'] . ' days',
                'fee'               => format_money($r['fee_amount']),
                'status'            => status_badge($r['status']),
                'actions'           => $actions,
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => $total, 'recordsFiltered' => $total, 'data' => $data]);
    }

    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $v = new Validator($request->all(), [
            'member_id'       => 'required|integer|exists:members,id',
            'freeze_reason'   => 'required|string|max:500',
            'reason_category' => 'nullable|string|max:50',
            'start_date'      => 'required|date',
            'end_date'        => 'required|date',
        ]);
        if ($v->fails()) { $this->json(['success' => false, 'errors' => $v->errors()], 422); return; }

        $d  = $v->validated();
        $db = $this->db();

        try {
            $db->execute(
                "CALL sp_create_freeze(?, ?, ?, ?, ?, ?, @freeze_id, @result)",
                [$d['member_id'], $d['freeze_reason'], $d['reason_category'] ?? 'general', $d['start_date'], $d['end_date'], Auth::id()]
            );
            $result = $db->fetchOne("SELECT @freeze_id AS id, @result AS msg");

            if ($result['id']) {
                $this->audit('create', 'member_freezes', (int)$result['id'], $result['msg']);
                $this->json(['success' => true, 'message' => $result['msg'], 'redirect' => '/freezes/' . $result['id']]);
            } else {
                $this->json(['success' => false, 'message' => $result['msg']], 422);
            }
        } catch (\Exception $e) {
            Logger::error('Freeze creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create freeze request'], 500);
        }
    }

    public function approve(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int) $request->routeParam('id');
        $db = $this->db();
        $freeze = $db->fetchOne("SELECT id, status, member_id, start_date FROM member_freezes WHERE id = ?", [$id]);

        if (!$freeze || $freeze['status'] !== 'pending') {
            $this->json(['success' => false, 'message' => 'Freeze not pending'], 422); return;
        }

        $newStatus = ($freeze['start_date'] <= date('Y-m-d')) ? 'active' : 'approved';

        $db->update('member_freezes', [
            'status'      => $newStatus,
            'approved_by' => Auth::id(),
            'approved_at' => date('Y-m-d H:i:s'),
            'activated_at' => $newStatus === 'active' ? date('Y-m-d H:i:s') : null,
            'updated_at'  => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('approve', 'member_freezes', $id, "Freeze approved â†’ {$newStatus}");
        $this->json(['success' => true, 'message' => 'Freeze approved']);
    }

    public function reject(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int) $request->routeParam('id');
        $reason = $request->post('rejection_reason', '');
        $db = $this->db();

        $freeze = $db->fetchOne("SELECT id, status FROM member_freezes WHERE id = ?", [$id]);
        if (!$freeze || $freeze['status'] !== 'pending') {
            $this->json(['success' => false, 'message' => 'Freeze not pending'], 422); return;
        }

        $db->update('member_freezes', [
            'status'           => 'rejected',
            'rejection_reason' => $reason,
            'updated_at'       => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('reject', 'member_freezes', $id, 'Freeze rejected: ' . $reason);
        $this->json(['success' => true, 'message' => 'Freeze rejected']);
    }

    public function endFreeze(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int) $request->routeParam('id');
        $reason = $request->post('reason', 'Early termination');
        $db = $this->db();

        $freeze = $db->fetchOne("SELECT id, status, member_id FROM member_freezes WHERE id = ?", [$id]);
        if (!$freeze || !in_array($freeze['status'], ['active', 'approved'])) {
            $this->json(['success' => false, 'message' => 'Freeze cannot be ended'], 422); return;
        }

        $db->update('member_freezes', [
            'status'          => 'ended',
            'actual_end_date' => date('Y-m-d'),
            'ended_at'        => date('Y-m-d H:i:s'),
            'ended_by'        => Auth::id(),
            'early_end_reason' => $reason,
            'updated_at'      => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        // Restore member status
        $db->update('members', [
            'status'            => 'active',
            'status_reason'     => 'Freeze ended early',
            'status_changed_at' => date('Y-m-d H:i:s'),
            'status_changed_by' => Auth::id(),
        ], "id = ? AND status = 'frozen'", [$freeze['member_id']]);

        $this->audit('update', 'member_freezes', $id, 'Freeze ended early: ' . $reason);
        $this->json(['success' => true, 'message' => 'Freeze ended successfully']);
    }

    public function extend(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int) $request->routeParam('id');
        $v = new Validator($request->all(), [
            'extension_days' => 'required|integer|min:1|max:365',
            'reason'         => 'required|string|max:500',
        ]);
        if ($v->fails()) { $this->json(['success' => false, 'errors' => $v->errors()], 422); return; }

        $d = $v->validated();
        $db = $this->db();

        $freeze = $db->fetchOne("SELECT * FROM member_freezes WHERE id = ? AND status IN ('active','approved')", [$id]);
        if (!$freeze) { $this->json(['success' => false, 'message' => 'Freeze not found or cannot be extended'], 422); return; }

        $currentEnd = $freeze['actual_end_date'] ?? $freeze['original_end_date'];
        $newEnd = date('Y-m-d', strtotime($currentEnd . ' + ' . $d['extension_days'] . ' days'));

        $extId = $db->insert('freeze_extensions', [
            'freeze_id'      => $id,
            'extension_days' => $d['extension_days'],
            'new_end_date'   => $newEnd,
            'reason'         => $d['reason'],
            'additional_fee' => 0,
            'status'         => 'approved',
            'approved_by'    => Auth::id(),
            'approved_at'    => date('Y-m-d H:i:s'),
            'created_by'     => Auth::id(),
        ]);

        $db->update('member_freezes', [
            'actual_end_date' => $newEnd,
            'duration_days'   => $freeze['duration_days'] + $d['extension_days'],
            'extension_days'  => ($freeze['extension_days'] ?? 0) + $d['extension_days'],
            'updated_at'      => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        $this->audit('update', 'member_freezes', $id, "Extended by {$d['extension_days']} days to {$newEnd}");
        $this->json(['success' => true, 'message' => "Freeze extended to {$newEnd}"]);
    }

    // â”€â”€ POLICIES â”€â”€
    public function policiesDatatable(Request $request): void
    {
        $rows = $this->db()->fetchAll(
            "SELECT fp.*, mt.name_en AS type_name
             FROM freeze_policies fp LEFT JOIN membership_types mt ON fp.membership_type_id = mt.id
             ORDER BY fp.policy_code"
        );
        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'          => $r['id'],
                'policy_code' => e($r['policy_code']),
                'name_en'     => e($r['name_en']),
                'name_ar'     => e($r['name_ar']),
                'type_name'   => $r['type_name'] ? e($r['type_name']) : '<em>All Types</em>',
                'max_days'    => $r['max_days'] ?? 'âˆž',
                'max_times'   => $r['max_times_per_year'] ?? 'âˆž',
                'fee_type'    => e($r['fee_type']),
                'fee_amount'  => format_money($r['fee_amount']),
                'is_active'   => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions'     => '<button class="btn btn-sm btn-outline-warning btn-edit-policy" data-id="' . $r['id'] . '"><i class="fas fa-edit"></i></button>',
            ];
        }
        $this->json(['draw' => 1, 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function getPolicy(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $row = $this->db()->fetchOne("SELECT * FROM freeze_policies WHERE id = ?", [$id]);
        $this->json($row ? ['success' => true, 'data' => $row] : ['success' => false], $row ? 200 : 404);
    }

    public function storePolicy(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }
        $v = new Validator($request->all(), [
            'policy_code' => 'required|string|max:30', 'name_en' => 'required|string|max:200', 'name_ar' => 'required|string|max:200',
            'membership_type_id' => 'nullable|integer', 'min_days' => 'required|integer|min:1', 'max_days' => 'nullable|integer',
            'max_times_per_year' => 'nullable|integer', 'max_total_days_per_year' => 'nullable|integer',
            'fee_type' => 'required|in:none,fixed,per_day,percentage_of_subscription', 'fee_amount' => 'required|decimal',
            'requires_approval' => 'nullable|boolean', 'extend_subscription' => 'nullable|boolean', 'is_active' => 'nullable|boolean',
        ]);
        if ($v->fails()) { $this->json(['success' => false, 'errors' => $v->errors()], 422); return; }
        $d = $v->validated();
        $d['requires_approval'] = $d['requires_approval'] ?? 0;
        $d['extend_subscription'] = $d['extend_subscription'] ?? 1;
        $d['is_active'] = $d['is_active'] ?? 1;
        if (empty($d['membership_type_id'])) $d['membership_type_id'] = null;

        $id = $this->db()->insert('freeze_policies', $d);
        $this->audit('create', 'freeze_policies', $id, 'Created freeze policy: ' . $d['policy_code']);
        $this->json(['success' => true, 'message' => 'Policy created']);
    }

    public function updatePolicy(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }
        $id = (int) $request->routeParam('id');
        $v = new Validator($request->all(), [
            'policy_code' => 'required|string|max:30', 'name_en' => 'required|string|max:200', 'name_ar' => 'required|string|max:200',
            'membership_type_id' => 'nullable|integer', 'min_days' => 'required|integer|min:1', 'max_days' => 'nullable|integer',
            'max_times_per_year' => 'nullable|integer', 'max_total_days_per_year' => 'nullable|integer',
            'fee_type' => 'required|in:none,fixed,per_day,percentage_of_subscription', 'fee_amount' => 'required|decimal',
            'requires_approval' => 'nullable|boolean', 'extend_subscription' => 'nullable|boolean', 'is_active' => 'nullable|boolean',
        ]);
        if ($v->fails()) { $this->json(['success' => false, 'errors' => $v->errors()], 422); return; }
        $d = $v->validated();
        $d['requires_approval'] = $d['requires_approval'] ?? 0;
        $d['extend_subscription'] = $d['extend_subscription'] ?? 1;
        $d['is_active'] = $d['is_active'] ?? 0;
        if (empty($d['membership_type_id'])) $d['membership_type_id'] = null;

        $this->db()->update('freeze_policies', $d, 'id = ?', [$id]);
        $this->audit('update', 'freeze_policies', $id, 'Updated freeze policy');
        $this->json(['success' => true, 'message' => 'Policy updated']);
    }
}
```

## File: modules\subscriptions\Controllers\FreezeController.php

```php
<?php

class FreezeController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();
        $totalPending = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status = 'pending'");
        $totalActive  = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status = 'active'");
        $totalApproved = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status = 'approved'");
        $totalEnded   = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status = 'ended'");

        $this->view('freezes/index', [
            'title' => lang('subs.freezes'),
            'breadcrumbs' => [['label' => lang('subs.freezes')]],
            'totalPending'  => $totalPending,
            'totalActive'   => $totalActive,
            'totalApproved' => $totalApproved,
            'totalEnded'    => $totalEnded,
        ]);
    }

    public function create(Request $request): void
    {
        $memberId = $request->get('member_id');
        $member = null;
        if ($memberId) {
            $member = $this->db()->fetchOne(
                "SELECT m.id, m.membership_number, m.full_name_en, m.full_name_ar, m.status, m.membership_type_id, mt.name_en AS type_name
                 FROM members m JOIN membership_types mt ON m.membership_type_id = mt.id
                 WHERE m.id = ? AND m.is_deleted = 0",
                [$memberId]
            );
        }

        $this->view('freezes/create', [
            'title' => lang('subs.create_freeze'),
            'breadcrumbs' => [
                ['label' => lang('subs.freezes'), 'url' => '/freezes'],
                ['label' => lang('subs.create_freeze')],
            ],
            'member' => $member,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $freeze = $db->fetchOne(
            "SELECT mf.*, m.membership_number, m.full_name_en AS member_name, m.primary_phone,
                    fp.name_en AS policy_name, fp.policy_code,
                    u1.full_name_en AS approved_by_name, u2.full_name_en AS ended_by_name, u3.full_name_en AS created_by_name
             FROM member_freezes mf
             JOIN members m ON mf.member_id = m.id
             LEFT JOIN freeze_policies fp ON mf.policy_id = fp.id
             LEFT JOIN auth_users u1 ON mf.approved_by = u1.id
             LEFT JOIN auth_users u2 ON mf.ended_by = u2.id
             LEFT JOIN auth_users u3 ON mf.created_by = u3.id
             WHERE mf.id = ?",
            [$id]
        );

        if (!$freeze) { $this->abort(404); return; }

        $extensions = $db->fetchAll(
            "SELECT fe.*, u.full_name_en AS approved_by_name
             FROM freeze_extensions fe LEFT JOIN auth_users u ON fe.approved_by = u.id
             WHERE fe.freeze_id = ? ORDER BY fe.created_at DESC",
            [$id]
        );

        $this->view('freezes/show', [
            'title' => lang('subs.freeze_details') . ' #' . $freeze['freeze_number'],
            'breadcrumbs' => [
                ['label' => lang('subs.freezes'), 'url' => '/freezes'],
                ['label' => $freeze['freeze_number']],
            ],
            'freeze'     => $freeze,
            'extensions' => $extensions,
        ]);
    }

    public function policies(Request $request): void
    {
        $types = $this->db()->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('freezes/policies', [
            'title' => lang('subs.freeze_policies'),
            'breadcrumbs' => [
                ['label' => lang('subs.freezes'), 'url' => '/freezes'],
                ['label' => lang('subs.freeze_policies')],
            ],
            'types' => $types,
        ]);
    }
}
```

## File: modules\subscriptions\Controllers\SubscriptionApiController.php

```php
<?php

class SubscriptionApiController extends Controller
{
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  SUBSCRIPTION DATATABLE
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function datatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $search = $request->get('search')['value'] ?? '';

        $statusFilter = $request->get('status', '');
        $periodFilter = $request->get('period_id', '');
        $typeFilter   = $request->get('type_id', '');

        $where = "1=1";
        $params = [];

        if ($statusFilter !== '') {
            $where .= " AND ms.status = ?";
            $params[] = $statusFilter;
        }
        if ($periodFilter !== '') {
            $where .= " AND ms.subscription_period_id = ?";
            $params[] = $periodFilter;
        }
        if ($typeFilter !== '') {
            $where .= " AND ms.membership_type_id = ?";
            $params[] = $typeFilter;
        }
        if ($search !== '') {
            $where .= " AND (m.membership_number LIKE ? OR m.full_name_en LIKE ? OR m.full_name_ar LIKE ? OR m.primary_phone LIKE ?)";
            $s = "%{$search}%";
            $params = array_merge($params, [$s, $s, $s, $s]);
        }

        $totalRecords = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_subscriptions ms JOIN members m ON ms.member_id = m.id");
        $filteredRecords = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM member_subscriptions ms JOIN members m ON ms.member_id = m.id WHERE {$where}",
            $params
        );

        $orderCol = (int) ($request->get('order')[0]['column'] ?? 0);
        $orderDir = strtoupper($request->get('order')[0]['dir'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
        $columns  = ['ms.id', 'm.membership_number', 'm.full_name_en', 'mt.name_en', 'sp.name_en', 'ms.start_date', 'ms.end_date', 'ms.net_amount', 'ms.paid_amount', 'ms.status'];
        $orderBy  = ($columns[$orderCol] ?? 'ms.id') . ' ' . $orderDir;

        $rows = $db->fetchAll(
            "SELECT ms.id, ms.start_date, ms.end_date, ms.net_amount, ms.paid_amount, ms.balance_due, ms.status,
                    m.membership_number, m.full_name_en AS member_name, m.id AS member_id,
                    mt.name_en AS type_name, sp.name_en AS period_name
             FROM member_subscriptions ms
             JOIN members m ON ms.member_id = m.id
             JOIN membership_types mt ON ms.membership_type_id = mt.id
             JOIN subscription_periods sp ON ms.subscription_period_id = sp.id
             WHERE {$where}
             ORDER BY {$orderBy}
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $actions = '<a href="/subscriptions/' . $r['id'] . '" class="btn btn-sm btn-outline-primary" data-bs-toggle="tooltip" title="View"><i class="fas fa-eye"></i></a>';
            $data[] = [
                'id'              => $r['id'],
                'membership_number' => e($r['membership_number']),
                'member_name'     => e($r['member_name']),
                'type_name'       => e($r['type_name']),
                'period_name'     => e($r['period_name']),
                'start_date'      => format_date($r['start_date']),
                'end_date'        => format_date($r['end_date']),
                'net_amount'      => format_money($r['net_amount']),
                'paid_amount'     => format_money($r['paid_amount']),
                'status'          => status_badge($r['status']),
                'actions'         => $actions,
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $totalRecords,
            'recordsFiltered' => $filteredRecords,
            'data'            => $data,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  EXPIRING DATATABLE
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function expiringDatatable(Request $request): void
    {
        $db = $this->db();
        $draw   = (int) $request->get('draw', 1);
        $start  = (int) $request->get('start', 0);
        $length = (int) $request->get('length', 25);
        $days   = (int) $request->get('days', 30);

        if ($days < 1 || $days > 365) $days = 30;

        $where  = "ms.status = 'active' AND ms.end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL ? DAY)";
        $params = [$days];

        $search = $request->get('search')['value'] ?? '';
        if ($search !== '') {
            $where .= " AND (m.membership_number LIKE ? OR m.full_name_en LIKE ?)";
            $s = "%{$search}%";
            $params[] = $s;
            $params[] = $s;
        }

        $total = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM member_subscriptions ms JOIN members m ON ms.member_id = m.id WHERE {$where}",
            $params
        );

        $rows = $db->fetchAll(
            "SELECT ms.id, ms.start_date, ms.end_date, ms.net_amount, ms.status,
                    DATEDIFF(ms.end_date, CURDATE()) AS days_remaining,
                    m.membership_number, m.full_name_en AS member_name, m.primary_phone, m.id AS member_id,
                    mt.name_en AS type_name
             FROM member_subscriptions ms
             JOIN members m ON ms.member_id = m.id
             JOIN membership_types mt ON ms.membership_type_id = mt.id
             WHERE {$where}
             ORDER BY ms.end_date ASC
             LIMIT {$length} OFFSET {$start}",
            $params
        );

        $data = [];
        foreach ($rows as $r) {
            $badge = $r['days_remaining'] <= 7 ? '<span class="badge bg-danger">' . $r['days_remaining'] . ' days</span>'
                : ($r['days_remaining'] <= 14 ? '<span class="badge bg-warning">' . $r['days_remaining'] . ' days</span>'
                : '<span class="badge bg-info">' . $r['days_remaining'] . ' days</span>');

            $data[] = [
                'membership_number' => e($r['membership_number']),
                'member_name'       => e($r['member_name']),
                'phone'             => e($r['primary_phone'] ?? ''),
                'type_name'         => e($r['type_name']),
                'end_date'          => format_date($r['end_date']),
                'days_remaining'    => $badge,
                'actions'           => '<a href="/subscriptions/create?member_id=' . $r['member_id'] . '" class="btn btn-sm btn-success"><i class="fas fa-redo me-1"></i>Renew</a>',
            ];
        }

        $this->json([
            'draw'            => $draw,
            'recordsTotal'    => $total,
            'recordsFiltered' => $total,
            'data'            => $data,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  CALCULATE FEE
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function calculateFee(Request $request): void
    {
        $db = $this->db();
        $memberId = (int) $request->get('member_id');
        $periodId = (int) $request->get('period_id');

        if (!$memberId || !$periodId) {
            $this->json(['success' => false, 'message' => 'Missing parameters'], 400);
            return;
        }

        $member = $db->fetchOne("SELECT id, membership_type_id, status FROM members WHERE id = ? AND is_deleted = 0", [$memberId]);
        if (!$member) {
            $this->json(['success' => false, 'message' => 'Member not found'], 404);
            return;
        }

        $period = $db->fetchOne("SELECT * FROM subscription_periods WHERE id = ? AND is_active = 1", [$periodId]);
        if (!$period) {
            $this->json(['success' => false, 'message' => 'Period not found'], 404);
            return;
        }

        $fee = $db->fetchOne(
            "SELECT mtf.amount, mtf.dependent_amount, mtf.dependent_percentage
             FROM membership_type_fees mtf
             WHERE mtf.membership_type_id = ? AND mtf.fee_type_id = (SELECT id FROM fee_types WHERE fee_code = 'ANNUAL_SUB' LIMIT 1)
               AND mtf.is_active = 1
               AND mtf.effective_from <= CURDATE()
               AND (mtf.effective_to IS NULL OR mtf.effective_to >= CURDATE())
             ORDER BY mtf.effective_from DESC LIMIT 1",
            [$member['membership_type_id']]
        );

        $baseAmount = $fee ? (float) $fee['amount'] : 0;

        $depCount = (int) $db->fetchColumn(
            "SELECT COUNT(*) FROM member_dependents WHERE primary_member_id = ? AND status = 'active' AND is_deleted = 0",
            [$memberId]
        );

        $depAmount = 0;
        if ($depCount > 0 && $fee) {
            if ($fee['dependent_amount'] > 0) {
                $depAmount = (float) $fee['dependent_amount'] * $depCount;
            } elseif ($fee['dependent_percentage'] > 0) {
                $depAmount = $baseAmount * ((float) $fee['dependent_percentage'] / 100) * $depCount;
            }
        }

        // Check for late fee
        $lateFee = 0;
        $currentSub = $db->fetchOne(
            "SELECT id, end_date FROM member_subscriptions WHERE member_id = ? AND status IN ('active','expired') ORDER BY end_date DESC LIMIT 1",
            [$memberId]
        );
        if ($currentSub && $currentSub['end_date'] < date('Y-m-d')) {
            $mt = $db->fetchOne("SELECT late_fee_type, late_fee_value, late_fee_max, grace_period_days FROM membership_types WHERE id = ?", [$member['membership_type_id']]);
            if ($mt && $mt['late_fee_type'] !== 'none') {
                $endDate = strtotime($currentSub['end_date']);
                $graceDays = (int) $mt['grace_period_days'];
                $daysPastGrace = max(0, (int) ((time() - $endDate) / 86400) - $graceDays);
                if ($daysPastGrace > 0) {
                    switch ($mt['late_fee_type']) {
                        case 'fixed': $lateFee = (float) $mt['late_fee_value']; break;
                        case 'percentage': $lateFee = $baseAmount * ((float) $mt['late_fee_value'] / 100); break;
                        case 'daily_fixed': $lateFee = (float) $mt['late_fee_value'] * $daysPastGrace; break;
                        case 'daily_percentage': $lateFee = $baseAmount * ((float) $mt['late_fee_value'] / 100) * $daysPastGrace; break;
                    }
                    if ($mt['late_fee_max'] && $lateFee > (float) $mt['late_fee_max']) {
                        $lateFee = (float) $mt['late_fee_max'];
                    }
                }
            }
        }

        $this->json([
            'success'        => true,
            'base_amount'    => $baseAmount,
            'dep_count'      => $depCount,
            'dep_amount'     => round($depAmount, 2),
            'late_fee'       => round($lateFee, 2),
            'total'          => round($baseAmount + $depAmount + $lateFee, 2),
            'start_date'     => $period['start_date'],
            'end_date'       => $period['end_date'],
            'is_renewal'     => $currentSub ? true : false,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  STORE SUBSCRIPTION
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function store(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $v = new Validator($request->all(), [
            'member_id'              => 'required|integer|exists:members,id',
            'subscription_period_id' => 'required|integer|exists:subscription_periods,id',
            'amount'                 => 'required|decimal',
            'discount_amount'        => 'nullable|decimal',
            'notes'                  => 'nullable|string|max:500',
        ]);

        if ($v->fails()) {
            $this->json(['success' => false, 'errors' => $v->errors()], 422);
            return;
        }

        $data = $v->validated();
        $db = $this->db();

        $member = $db->fetchOne("SELECT id, membership_type_id, status FROM members WHERE id = ? AND is_deleted = 0", [$data['member_id']]);
        if (!$member || !in_array($member['status'], ['active', 'pending_approval'])) {
            $this->json(['success' => false, 'message' => 'Member is not eligible for subscription'], 422);
            return;
        }

        $period = $db->fetchOne("SELECT * FROM subscription_periods WHERE id = ? AND is_active = 1", [$data['subscription_period_id']]);
        if (!$period) {
            $this->json(['success' => false, 'message' => 'Invalid subscription period'], 422);
            return;
        }

        // Check duplicate
        $existing = $db->fetchOne(
            "SELECT id FROM member_subscriptions WHERE member_id = ? AND subscription_period_id = ? AND status NOT IN ('cancelled')",
            [$data['member_id'], $data['subscription_period_id']]
        );
        if ($existing) {
            $this->json(['success' => false, 'message' => 'Member already has a subscription for this period'], 422);
            return;
        }

        $discount = (float) ($data['discount_amount'] ?? 0);
        $amount   = (float) $data['amount'];
        $net      = $amount - $discount;

        $depCount = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_dependents WHERE primary_member_id = ? AND status = 'active' AND is_deleted = 0", [$data['member_id']]);
        $depAmount = (float) ($request->post('dependents_amount', 0));
        $lateFee   = (float) ($request->post('late_fee', 0));

        $previousSub = $db->fetchOne(
            "SELECT id FROM member_subscriptions WHERE member_id = ? AND status IN ('active','expired') ORDER BY end_date DESC LIMIT 1",
            [$data['member_id']]
        );

        $db->beginTransaction();
        try {
            // Create invoice
            $db->execute("CALL sp_create_invoice(?, 'subscription', ?, ?, @inv_id, @inv_num)", [
                $data['member_id'], $period['end_date'], Auth::id()
            ]);
            $invResult = $db->fetchOne("SELECT @inv_id AS id, @inv_num AS num");
            $invoiceId = $invResult['id'];

            // Add line items
            $feeTypeId = $db->fetchColumn("SELECT id FROM fee_types WHERE fee_code = 'ANNUAL_SUB' LIMIT 1");
            if ($feeTypeId && $amount > 0) {
                $db->execute("CALL sp_add_invoice_item(?, ?, 'Annual Subscription', 'Ø§Ø´ØªØ±Ø§Ùƒ Ø³Ù†ÙˆÙŠ', 1, ?, ?, 0)", [
                    $invoiceId, $feeTypeId, $amount, $discount
                ]);
            }
            if ($depAmount > 0) {
                $depFeeTypeId = $db->fetchColumn("SELECT id FROM fee_types WHERE fee_code = 'DEP_ANNUAL' LIMIT 1") ?: $feeTypeId;
                $db->execute("CALL sp_add_invoice_item(?, ?, 'Dependent Annual Fees', 'Ø±Ø³ÙˆÙ… ØªØ§Ø¨Ø¹ÙŠÙ† Ø³Ù†ÙˆÙŠØ©', 1, ?, 0, 0)", [
                    $invoiceId, $depFeeTypeId, $depAmount
                ]);
            }
            if ($lateFee > 0) {
                $lateFeeTypeId = $db->fetchColumn("SELECT id FROM fee_types WHERE fee_code = 'LATE_FEE' LIMIT 1") ?: $feeTypeId;
                $db->execute("CALL sp_add_invoice_item(?, ?, 'Late Renewal Fee', 'ØºØ±Ø§Ù…Ø© ØªØ£Ø®ÙŠØ± ØªØ¬Ø¯ÙŠØ¯', 1, ?, 0, 0)", [
                    $invoiceId, $lateFeeTypeId, $lateFee
                ]);
            }

            // Issue invoice
            $db->execute("CALL sp_issue_invoice(?, ?, @issue_result)", [$invoiceId, Auth::id()]);

            // Create subscription record
            $subId = $db->insert('member_subscriptions', [
                'member_id'              => $data['member_id'],
                'subscription_period_id' => $data['subscription_period_id'],
                'membership_type_id'     => $member['membership_type_id'],
                'invoice_id'             => $invoiceId,
                'amount'                 => $amount,
                'discount_amount'        => $discount,
                'net_amount'             => $net + $depAmount + $lateFee,
                'paid_amount'            => 0,
                'balance_due'            => $net + $depAmount + $lateFee,
                'dependents_count'       => $depCount,
                'dependents_amount'      => $depAmount,
                'start_date'             => $period['start_date'],
                'end_date'               => $period['end_date'],
                'status'                 => 'pending',
                'renewed_from_id'        => $previousSub ? $previousSub['id'] : null,
                'is_renewal'             => $previousSub ? 1 : 0,
                'renewal_date'           => $previousSub ? date('Y-m-d') : null,
                'late_fee'               => $lateFee,
                'notes'                  => $data['notes'] ?? null,
                'created_by'             => Auth::id(),
            ]);

            // Log renewal
            if ($previousSub) {
                $db->insert('subscription_renewal_log', [
                    'member_id'           => $data['member_id'],
                    'old_subscription_id' => $previousSub['id'],
                    'new_subscription_id' => $subId,
                    'renewal_date'        => date('Y-m-d'),
                    'renewal_type'        => $lateFee > 0 ? 'late' : 'normal',
                    'previous_type_id'    => $member['membership_type_id'],
                    'new_type_id'         => $member['membership_type_id'],
                    'is_type_change'      => 0,
                    'late_days'           => 0,
                    'late_fee'            => $lateFee,
                    'notes'               => $data['notes'] ?? null,
                    'created_by'          => Auth::id(),
                ]);
            }

            $this->audit('create', 'member_subscriptions', $subId, 'Created subscription for member #' . $data['member_id']);
            $db->commit();

            $this->json([
                'success'  => true,
                'message'  => 'Subscription created successfully',
                'redirect' => '/subscriptions/' . $subId,
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            Logger::error('Subscription creation failed: ' . $e->getMessage());
            $this->json(['success' => false, 'message' => 'Failed to create subscription'], 500);
        }
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  ELIGIBLE MEMBERS FOR BULK RENEW
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function eligible(Request $request): void
    {
        $db = $this->db();
        $periodId = (int) $request->get('period_id');
        $typeId   = (int) $request->get('type_id', 0);

        $where  = "m.status = 'active' AND m.is_deleted = 0";
        $params = [];

        if ($typeId) {
            $where .= " AND m.membership_type_id = ?";
            $params[] = $typeId;
        }

        if ($periodId) {
            $where .= " AND m.id NOT IN (SELECT member_id FROM member_subscriptions WHERE subscription_period_id = ? AND status NOT IN ('cancelled'))";
            $params[] = $periodId;
        }

        $members = $db->fetchAll(
            "SELECT m.id, m.membership_number, m.full_name_en, m.primary_phone, mt.name_en AS type_name,
                    (SELECT ms.end_date FROM member_subscriptions ms WHERE ms.member_id = m.id AND ms.status IN ('active','expired') ORDER BY ms.end_date DESC LIMIT 1) AS last_sub_end
             FROM members m
             JOIN membership_types mt ON m.membership_type_id = mt.id
             WHERE {$where}
             ORDER BY m.membership_number
             LIMIT 500",
            $params
        );

        $this->json(['success' => true, 'data' => $members]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  PROCESS BULK RENEWAL
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function processBulkRenew(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) {
            $this->json(['success' => false, 'message' => 'Invalid security token'], 403);
            return;
        }

        $periodId  = (int) $request->post('period_id');
        $memberIds = $request->post('member_ids', []);

        if (!$periodId || empty($memberIds) || !is_array($memberIds)) {
            $this->json(['success' => false, 'message' => 'Select a period and at least one member'], 422);
            return;
        }

        $db = $this->db();
        $period = $db->fetchOne("SELECT * FROM subscription_periods WHERE id = ? AND is_active = 1", [$periodId]);
        if (!$period) {
            $this->json(['success' => false, 'message' => 'Invalid period'], 422);
            return;
        }

        $success = 0;
        $failed  = 0;
        $errors  = [];

        foreach ($memberIds as $mid) {
            $mid = (int) $mid;
            $member = $db->fetchOne("SELECT id, membership_type_id, status FROM members WHERE id = ? AND is_deleted = 0 AND status = 'active'", [$mid]);
            if (!$member) {
                $failed++;
                $errors[] = "Member #{$mid} not found or inactive";
                continue;
            }

            $existing = $db->fetchOne("SELECT id FROM member_subscriptions WHERE member_id = ? AND subscription_period_id = ? AND status NOT IN ('cancelled')", [$mid, $periodId]);
            if ($existing) {
                $failed++;
                $errors[] = "Member #{$mid} already has subscription for this period";
                continue;
            }

            $fee = $db->fetchOne(
                "SELECT amount FROM membership_type_fees WHERE membership_type_id = ? AND fee_type_id = (SELECT id FROM fee_types WHERE fee_code = 'ANNUAL_SUB' LIMIT 1) AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1",
                [$member['membership_type_id']]
            );
            $amount = $fee ? (float) $fee['amount'] : 0;

            try {
                $db->beginTransaction();

                $db->execute("CALL sp_create_invoice(?, 'subscription', ?, ?, @inv_id, @inv_num)", [$mid, $period['end_date'], Auth::id()]);
                $invResult = $db->fetchOne("SELECT @inv_id AS id, @inv_num AS num");
                $feeTypeId = $db->fetchColumn("SELECT id FROM fee_types WHERE fee_code = 'ANNUAL_SUB' LIMIT 1");
                if ($feeTypeId && $amount > 0) {
                    $db->execute("CALL sp_add_invoice_item(?, ?, 'Annual Subscription', 'Ø§Ø´ØªØ±Ø§Ùƒ Ø³Ù†ÙˆÙŠ', 1, ?, 0, 0)", [$invResult['id'], $feeTypeId, $amount]);
                }
                $db->execute("CALL sp_issue_invoice(?, ?, @r)", [$invResult['id'], Auth::id()]);

                $previousSub = $db->fetchOne("SELECT id FROM member_subscriptions WHERE member_id = ? AND status IN ('active','expired') ORDER BY end_date DESC LIMIT 1", [$mid]);

                $db->insert('member_subscriptions', [
                    'member_id' => $mid, 'subscription_period_id' => $periodId,
                    'membership_type_id' => $member['membership_type_id'], 'invoice_id' => $invResult['id'],
                    'amount' => $amount, 'discount_amount' => 0, 'net_amount' => $amount,
                    'paid_amount' => 0, 'balance_due' => $amount, 'dependents_count' => 0, 'dependents_amount' => 0,
                    'start_date' => $period['start_date'], 'end_date' => $period['end_date'],
                    'status' => 'pending', 'renewed_from_id' => $previousSub['id'] ?? null,
                    'is_renewal' => $previousSub ? 1 : 0, 'renewal_date' => $previousSub ? date('Y-m-d') : null,
                    'late_fee' => 0, 'created_by' => Auth::id(),
                ]);

                $db->commit();
                $success++;
            } catch (\Exception $e) {
                $db->rollBack();
                $failed++;
                $errors[] = "Member #{$mid}: " . $e->getMessage();
            }
        }

        $this->audit('create', 'member_subscriptions', null, "Bulk renewal: {$success} success, {$failed} failed");

        $this->json([
            'success' => true,
            'message' => "Processed: {$success} renewed, {$failed} failed",
            'success_count' => $success,
            'failed_count'  => $failed,
            'errors'        => $errors,
        ]);
    }

    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    //  PERIODS DATATABLE
    // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    public function periodsDatatable(Request $request): void
    {
        $db   = $this->db();
        $draw = (int) $request->get('draw', 1);

        $rows = $db->fetchAll(
            "SELECT sp.*,
                    (SELECT COUNT(*) FROM member_subscriptions ms WHERE ms.subscription_period_id = sp.id AND ms.status NOT IN ('cancelled')) AS sub_count
             FROM subscription_periods sp
             ORDER BY sp.start_date DESC"
        );

        $data = [];
        foreach ($rows as $r) {
            $data[] = [
                'id'         => $r['id'],
                'period_code'=> e($r['period_code']),
                'name_en'    => e($r['name_en']),
                'name_ar'    => e($r['name_ar']),
                'start_date' => format_date($r['start_date']),
                'end_date'   => format_date($r['end_date']),
                'grace_end'  => $r['grace_end_date'] ? format_date($r['grace_end_date']) : '-',
                'sub_count'  => number_format($r['sub_count']),
                'is_active'  => $r['is_active'] ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>',
                'actions'    => '<button class="btn btn-sm btn-outline-warning btn-edit-period" data-id="' . $r['id'] . '"><i class="fas fa-edit"></i></button>',
            ];
        }

        $this->json(['draw' => $draw, 'recordsTotal' => count($data), 'recordsFiltered' => count($data), 'data' => $data]);
    }

    public function getPeriod(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $row = $this->db()->fetchOne("SELECT * FROM subscription_periods WHERE id = ?", [$id]);
        if (!$row) { $this->json(['success' => false, 'message' => 'Not found'], 404); return; }
        $this->json(['success' => true, 'data' => $row]);
    }

    public function storePeriod(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $v = new Validator($request->all(), [
            'period_code' => 'required|string|max:20',
            'name_en'     => 'required|string|max:200',
            'name_ar'     => 'required|string|max:200',
            'start_date'  => 'required|date',
            'end_date'    => 'required|date',
            'grace_end_date' => 'nullable|date',
            'is_active'   => 'nullable|boolean',
        ]);
        if ($v->fails()) { $this->json(['success' => false, 'errors' => $v->errors()], 422); return; }

        $d = $v->validated();
        $d['is_active'] = $d['is_active'] ?? 1;

        $id = $this->db()->insert('subscription_periods', $d);
        $this->audit('create', 'subscription_periods', $id, 'Created subscription period: ' . $d['period_code']);
        $this->json(['success' => true, 'message' => 'Period created', 'id' => $id]);
    }

    public function updatePeriod(Request $request): void
    {
        if (!CSRF::validate($request->csrfToken())) { $this->json(['success' => false, 'message' => 'CSRF'], 403); return; }

        $id = (int) $request->routeParam('id');
        $v = new Validator($request->all(), [
            'period_code' => 'required|string|max:20',
            'name_en'     => 'required|string|max:200',
            'name_ar'     => 'required|string|max:200',
            'start_date'  => 'required|date',
            'end_date'    => 'required|date',
            'grace_end_date' => 'nullable|date',
            'is_active'   => 'nullable|boolean',
        ]);
        if ($v->fails()) { $this->json(['success' => false, 'errors' => $v->errors()], 422); return; }

        $d = $v->validated();
        $d['is_active'] = $d['is_active'] ?? 0;

        $this->db()->update('subscription_periods', $d, 'id = ?', [$id]);
        $this->audit('update', 'subscription_periods', $id, 'Updated subscription period');
        $this->json(['success' => true, 'message' => 'Period updated']);
    }
}
```

## File: modules\subscriptions\Controllers\SubscriptionController.php

```php
<?php

class SubscriptionController extends Controller
{
    public function index(Request $request): void
    {
        $db = $this->db();

        $totalActive = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_subscriptions WHERE status = 'active'");
        $totalExpired = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_subscriptions WHERE status = 'expired'");
        $totalPending = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_subscriptions WHERE status = 'pending'");
        $expiringIn30 = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_subscriptions WHERE status = 'active' AND end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)");

        $periods = $db->fetchAll("SELECT id, period_code, name_en, name_ar FROM subscription_periods WHERE is_active = 1 ORDER BY start_date DESC");
        $types = $db->fetchAll("SELECT id, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('subscriptions/index', [
            'title'        => lang('subs.subscriptions'),
            'breadcrumbs'  => [
                ['label' => lang('subs.subscriptions')],
            ],
            'totalActive'  => $totalActive,
            'totalExpired' => $totalExpired,
            'totalPending' => $totalPending,
            'expiringIn30' => $expiringIn30,
            'periods'      => $periods,
            'types'        => $types,
        ]);
    }

    public function create(Request $request): void
    {
        $db = $this->db();
        $periods = $db->fetchAll("SELECT id, period_code, name_en, name_ar, start_date, end_date FROM subscription_periods WHERE is_active = 1 ORDER BY start_date DESC");
        $types = $db->fetchAll("SELECT id, type_code, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $memberId = $request->get('member_id');
        $member = null;
        if ($memberId) {
            $member = $db->fetchOne("SELECT id, membership_number, full_name_en, full_name_ar, membership_type_id, status FROM members WHERE id = ? AND is_deleted = 0", [$memberId]);
        }

        $this->view('subscriptions/create', [
            'title'       => lang('subs.create_subscription'),
            'breadcrumbs' => [
                ['label' => lang('subs.subscriptions'), 'url' => '/subscriptions'],
                ['label' => lang('subs.create_subscription')],
            ],
            'periods' => $periods,
            'types'   => $types,
            'member'  => $member,
        ]);
    }

    public function show(Request $request): void
    {
        $id = (int) $request->routeParam('id');
        $db = $this->db();

        $sub = $db->fetchOne(
            "SELECT ms.*, sp.name_en AS period_name_en, sp.name_ar AS period_name_ar, sp.period_code,
                    mt.name_en AS type_name_en, mt.name_ar AS type_name_ar, mt.type_code,
                    m.membership_number, m.full_name_en AS member_name_en, m.full_name_ar AS member_name_ar,
                    m.primary_phone, m.primary_email
             FROM member_subscriptions ms
             JOIN subscription_periods sp ON ms.subscription_period_id = sp.id
             JOIN membership_types mt ON ms.membership_type_id = mt.id
             JOIN members m ON ms.member_id = m.id
             WHERE ms.id = ?",
            [$id]
        );

        if (!$sub) {
            $this->abort(404, 'Subscription not found');
            return;
        }

        $renewalLog = $db->fetchAll(
            "SELECT srl.*, u.full_name_en AS created_by_name
             FROM subscription_renewal_log srl
             LEFT JOIN auth_users u ON srl.created_by = u.id
             WHERE srl.member_id = ? AND (srl.old_subscription_id = ? OR srl.new_subscription_id = ?)
             ORDER BY srl.created_at DESC",
            [$sub['member_id'], $id, $id]
        );

        $freezes = $db->fetchAll(
            "SELECT * FROM member_freezes WHERE subscription_id = ? ORDER BY created_at DESC",
            [$id]
        );

        $invoice = null;
        if ($sub['invoice_id']) {
            $invoice = $db->fetchOne("SELECT id, invoice_number, total_amount, paid_amount, balance_due, status FROM invoices WHERE id = ?", [$sub['invoice_id']]);
        }

        $this->view('subscriptions/show', [
            'title'       => lang('subs.view_subscription') . ' #' . $id,
            'breadcrumbs' => [
                ['label' => lang('subs.subscriptions'), 'url' => '/subscriptions'],
                ['label' => lang('subs.view_subscription')],
            ],
            'sub'        => $sub,
            'renewalLog' => $renewalLog,
            'freezes'    => $freezes,
            'invoice'    => $invoice,
        ]);
    }

    public function expiring(Request $request): void
    {
        $this->view('subscriptions/expiring', [
            'title'       => lang('subs.expiring_subscriptions'),
            'breadcrumbs' => [
                ['label' => lang('subs.subscriptions'), 'url' => '/subscriptions'],
                ['label' => lang('subs.expiring_subscriptions')],
            ],
        ]);
    }

    public function bulkRenew(Request $request): void
    {
        $db = $this->db();
        $periods = $db->fetchAll("SELECT id, period_code, name_en, name_ar, start_date, end_date FROM subscription_periods WHERE is_active = 1 ORDER BY start_date DESC");
        $types = $db->fetchAll("SELECT id, type_code, name_en, name_ar FROM membership_types WHERE is_active = 1 ORDER BY sort_order");

        $this->view('subscriptions/bulk_renew', [
            'title'       => lang('subs.bulk_renewal'),
            'breadcrumbs' => [
                ['label' => lang('subs.subscriptions'), 'url' => '/subscriptions'],
                ['label' => lang('subs.bulk_renewal')],
            ],
            'periods' => $periods,
            'types'   => $types,
        ]);
    }

    public function periods(Request $request): void
    {
        $this->view('periods/index', [
            'title'       => lang('subs.subscription_periods'),
            'breadcrumbs' => [
                ['label' => lang('subs.subscriptions'), 'url' => '/subscriptions'],
                ['label' => lang('subs.subscription_periods')],
            ],
        ]);
    }
}
```

## File: modules\subscriptions\css\subscriptions.css

```css
/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   Subscriptions & Freezes Module Styles
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

.card.border-primary { border-left: 4px solid var(--bs-primary) !important; }
.card.border-success { border-left: 4px solid var(--bs-success) !important; }
.card.border-warning { border-left: 4px solid var(--bs-warning) !important; }
.card.border-danger  { border-left: 4px solid var(--bs-danger) !important; }
.card.border-info    { border-left: 4px solid var(--bs-info) !important; }
.card.border-secondary { border-left: 4px solid var(--bs-secondary) !important; }

#eligible-table tbody tr:hover {
    background-color: rgba(44, 82, 130, 0.06);
    cursor: pointer;
}

#eligible-table input[type="checkbox"] {
    cursor: pointer;
}

.table-sticky thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: #f8fafc;
}
```

## File: modules\subscriptions\js\subscriptions.js

```js
/**
 * Subscriptions & Freezes Module JS
 */
(function() {
    'use strict';
    // Module-specific initialization can go here.
    // All page-specific JS is in inline push('scripts') blocks.
})();
```

## File: modules\subscriptions\lang\ar.php

```php
<?php
return [
    'subs.subscriptions'         => 'Ø§Ù„Ø§Ø´ØªØ±Ø§ÙƒØ§Øª',
    'subs.subscription'          => 'Ø§Ø´ØªØ±Ø§Ùƒ',
    'subs.create_subscription'   => 'Ø¥Ù†Ø´Ø§Ø¡ Ø§Ø´ØªØ±Ø§Ùƒ',
    'subs.view_subscription'     => 'Ø¹Ø±Ø¶ Ø§Ù„Ø§Ø´ØªØ±Ø§Ùƒ',
    'subs.subscription_periods'  => 'ÙØªØ±Ø§Øª Ø§Ù„Ø§Ø´ØªØ±Ø§Ùƒ',
    'subs.add_period'            => 'Ø¥Ø¶Ø§ÙØ© ÙØªØ±Ø©',
    'subs.period'                => 'Ø§Ù„ÙØªØ±Ø©',
    'subs.membership_type'       => 'Ù†ÙˆØ¹ Ø§Ù„Ø¹Ø¶ÙˆÙŠØ©',
    'subs.member'                => 'Ø§Ù„Ø¹Ø¶Ùˆ',
    'subs.notes'                 => 'Ù…Ù„Ø§Ø­Ø¸Ø§Øª',
    'subs.active'                => 'Ù†Ø´Ø·',
    'subs.pending'               => 'Ù…Ø¹Ù„Ù‚',
    'subs.expired'               => 'Ù…Ù†ØªÙ‡ÙŠ',
    'subs.expiring_30'           => 'ØªÙ†ØªÙ‡ÙŠ Ø®Ù„Ø§Ù„ 30 ÙŠÙˆÙ…',
    'subs.expiring_subscriptions'=> 'Ø§Ø´ØªØ±Ø§ÙƒØ§Øª ØªÙ†ØªÙ‡ÙŠ Ù‚Ø±ÙŠØ¨Ø§Ù‹',
    'subs.bulk_renewal'          => 'ØªØ¬Ø¯ÙŠØ¯ Ø¬Ù…Ø§Ø¹ÙŠ',
    'subs.freezes'               => 'Ø§Ù„ØªØ¬Ù…ÙŠØ¯',
    'subs.create_freeze'         => 'Ø·Ù„Ø¨ ØªØ¬Ù…ÙŠØ¯',
    'subs.freeze_details'        => 'ØªÙØ§ØµÙŠÙ„ Ø§Ù„ØªØ¬Ù…ÙŠØ¯',
    'subs.freeze_policies'       => 'Ø³ÙŠØ§Ø³Ø§Øª Ø§Ù„ØªØ¬Ù…ÙŠØ¯',
];
```

## File: modules\subscriptions\lang\en.php

```php
<?php
return [
    'subs.subscriptions'         => 'Subscriptions',
    'subs.subscription'          => 'Subscription',
    'subs.create_subscription'   => 'Create Subscription',
    'subs.view_subscription'     => 'View Subscription',
    'subs.subscription_periods'  => 'Subscription Periods',
    'subs.add_period'            => 'Add Period',
    'subs.period'                => 'Period',
    'subs.membership_type'       => 'Membership Type',
    'subs.member'                => 'Member',
    'subs.notes'                 => 'Notes',
    'subs.active'                => 'Active',
    'subs.pending'               => 'Pending',
    'subs.expired'               => 'Expired',
    'subs.expiring_30'           => 'Expiring in 30 Days',
    'subs.expiring_subscriptions'=> 'Expiring Subscriptions',
    'subs.bulk_renewal'          => 'Bulk Renewal',
    'subs.freezes'               => 'Freezes',
    'subs.create_freeze'         => 'Request Freeze',
    'subs.freeze_details'        => 'Freeze Details',
    'subs.freeze_policies'       => 'Freeze Policies',
];
```

## File: modules\subscriptions\module.json

```json
{
    "name": "subscriptions",
    "label_en": "Subscriptions & Freezes",
    "label_ar": "Ø§Ù„Ø§Ø´ØªØ±Ø§ÙƒØ§Øª ÙˆØ§Ù„ØªØ¬Ù…ÙŠØ¯",
    "version": "1.0.0",
    "description": "Subscription periods, member subscriptions, renewals, freezes and freeze policies",
    "sort_order": 40,
    "icon": "fas fa-sync-alt",
    "routes_file": "routes.php",
    "css_files": ["css/subscriptions.css"],
    "js_files": ["js/subscriptions.js"],
    "menu": [
        {
            "label_en": "Subscriptions",
            "label_ar": "Ø§Ù„Ø§Ø´ØªØ±Ø§ÙƒØ§Øª",
            "icon": "fas fa-sync-alt",
            "permission": "members.view",
            "sort_order": 40,
            "children": [
                {
                    "label_en": "All Subscriptions",
                    "label_ar": "ÙƒÙ„ Ø§Ù„Ø§Ø´ØªØ±Ø§ÙƒØ§Øª",
                    "route": "/subscriptions",
                    "permission": "members.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Subscription Periods",
                    "label_ar": "ÙØªØ±Ø§Øª Ø§Ù„Ø§Ø´ØªØ±Ø§Ùƒ",
                    "route": "/subscriptions/periods",
                    "permission": "members.view",
                    "sort_order": 2
                },
                {
                    "label_en": "Bulk Renewal",
                    "label_ar": "ØªØ¬Ø¯ÙŠØ¯ Ø¬Ù…Ø§Ø¹ÙŠ",
                    "route": "/subscriptions/bulk-renew",
                    "permission": "members.create",
                    "sort_order": 3
                },
                {
                    "label_en": "Expiring Soon",
                    "label_ar": "ØªÙ†ØªÙ‡ÙŠ Ù‚Ø±ÙŠØ¨Ø§Ù‹",
                    "route": "/subscriptions/expiring",
                    "permission": "members.view",
                    "sort_order": 4
                }
            ]
        },
        {
            "label_en": "Freezes",
            "label_ar": "Ø§Ù„ØªØ¬Ù…ÙŠØ¯",
            "icon": "fas fa-snowflake",
            "permission": "freezes.view",
            "sort_order": 41,
            "children": [
                {
                    "label_en": "All Freezes",
                    "label_ar": "ÙƒÙ„ Ø§Ù„ØªØ¬Ù…ÙŠØ¯",
                    "route": "/freezes",
                    "permission": "freezes.view",
                    "sort_order": 1
                },
                {
                    "label_en": "Request Freeze",
                    "label_ar": "Ø·Ù„Ø¨ ØªØ¬Ù…ÙŠØ¯",
                    "route": "/freezes/create",
                    "permission": "freezes.create",
                    "sort_order": 2
                },
                {
                    "label_en": "Freeze Policies",
                    "label_ar": "Ø³ÙŠØ§Ø³Ø§Øª Ø§Ù„ØªØ¬Ù…ÙŠØ¯",
                    "route": "/freezes/policies",
                    "permission": "freezes.view",
                    "sort_order": 3
                }
            ]
        }
    ],
    "dashboard_widgets": [
        {
            "widget_id": "expiring_subscriptions",
            "label_en": "Expiring Subscriptions",
            "label_ar": "Ø§Ø´ØªØ±Ø§ÙƒØ§Øª ØªÙ†ØªÙ‡ÙŠ Ù‚Ø±ÙŠØ¨Ø§Ù‹",
            "permission": "members.view",
            "partial": "views/widgets/expiring_subscriptions.php",
            "size": "col-md-4",
            "sort_order": 40
        },
        {
            "widget_id": "active_freezes",
            "label_en": "Active Freezes",
            "label_ar": "Ø§Ù„ØªØ¬Ù…ÙŠØ¯ Ø§Ù„Ù†Ø´Ø·",
            "permission": "freezes.view",
            "partial": "views/widgets/active_freezes.php",
            "size": "col-md-4",
            "sort_order": 41
        }
    ]
}
```

## File: modules\subscriptions\routes.php

```php
<?php
return [
    // â”€â”€ Subscription Periods â”€â”€
    ['GET',  '/subscriptions/periods',            'SubscriptionController@periods',       'members.view'],
    ['POST', '/subscriptions/periods',            'SubscriptionApiController@storePeriod', 'members.create'],
    ['POST', '/subscriptions/periods/{id}',       'SubscriptionApiController@updatePeriod','members.create'],
    ['GET',  '/api/subscriptions/periods/datatable','SubscriptionApiController@periodsDatatable','members.view'],
    ['GET',  '/api/subscriptions/periods/{id}',   'SubscriptionApiController@getPeriod',   'members.view'],

    // â”€â”€ Subscriptions â”€â”€
    ['GET',  '/subscriptions',                    'SubscriptionController@index',          'members.view'],
    ['GET',  '/subscriptions/create',             'SubscriptionController@create',         'members.create'],
    ['POST', '/subscriptions',                    'SubscriptionApiController@store',       'members.create'],
    ['GET',  '/subscriptions/expiring',           'SubscriptionController@expiring',       'members.view'],
    ['GET',  '/subscriptions/bulk-renew',         'SubscriptionController@bulkRenew',      'members.create'],
    ['POST', '/subscriptions/bulk-renew',         'SubscriptionApiController@processBulkRenew','members.create'],
    ['GET',  '/subscriptions/{id}',               'SubscriptionController@show',           'members.view'],

    // â”€â”€ Subscription API â”€â”€
    ['GET',  '/api/subscriptions/datatable',      'SubscriptionApiController@datatable',   'members.view'],
    ['GET',  '/api/subscriptions/expiring/datatable','SubscriptionApiController@expiringDatatable','members.view'],
    ['GET',  '/api/subscriptions/eligible',       'SubscriptionApiController@eligible',    'members.create'],
    ['GET',  '/api/subscriptions/calculate-fee',  'SubscriptionApiController@calculateFee','members.create'],

    // â”€â”€ Freezes â”€â”€
    ['GET',  '/freezes',                          'FreezeController@index',                'freezes.view'],
    ['GET',  '/freezes/create',                   'FreezeController@create',               'freezes.create'],
    ['POST', '/freezes',                          'FreezeApiController@store',             'freezes.create'],
    ['GET',  '/freezes/policies',                 'FreezeController@policies',             'freezes.view'],
    ['POST', '/freezes/policies',                 'FreezeApiController@storePolicy',       'freezes.approve'],
    ['POST', '/freezes/policies/{id}',            'FreezeApiController@updatePolicy',      'freezes.approve'],
    ['GET',  '/freezes/{id}',                     'FreezeController@show',                 'freezes.view'],

    // â”€â”€ Freeze API â”€â”€
    ['GET',  '/api/freezes/datatable',            'FreezeApiController@datatable',         'freezes.view'],
    ['POST', '/api/freezes/{id}/approve',         'FreezeApiController@approve',           'freezes.approve'],
    ['POST', '/api/freezes/{id}/reject',          'FreezeApiController@reject',            'freezes.approve'],
    ['POST', '/api/freezes/{id}/end',             'FreezeApiController@endFreeze',         'freezes.approve'],
    ['POST', '/api/freezes/{id}/extend',          'FreezeApiController@extend',            'freezes.approve'],
    ['GET',  '/api/freezes/policies/datatable',   'FreezeApiController@policiesDatatable', 'freezes.view'],
    ['GET',  '/api/freezes/policies/{id}',        'FreezeApiController@getPolicy',         'freezes.view'],
];
```

## File: modules\subscriptions\views\freezes\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-7">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-snowflake me-2"></i><?= lang('subs.create_freeze') ?></h5></div>
            <div class="card-body">
                <form id="freeze-form" method="POST" action="/freezes" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label">Member <span class="text-danger">*</span></label>
                        <select name="member_id" id="freeze-member-select" class="form-select select2" style="width:100%" required>
                            <?php if ($member): ?>
                            <option value="<?= $member['id'] ?>" selected><?= e($member['membership_number'] . ' - ' . $member['full_name_en']) ?></option>
                            <?php endif; ?>
                        </select>
                        <div class="invalid-feedback" data-field="member_id"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Reason <span class="text-danger">*</span></label>
                        <textarea name="freeze_reason" class="form-control" rows="3" required></textarea>
                        <div class="invalid-feedback" data-field="freeze_reason"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Category</label>
                        <select name="reason_category" class="form-select">
                            <option value="general">General</option>
                            <option value="medical">Medical</option>
                            <option value="travel">Travel</option>
                            <option value="work">Work</option>
                            <option value="personal">Personal</option>
                            <option value="other">Other</option>
                        </select>
                    </div>
                    <div class="row g-3 mb-3">
                        <div class="col-md-6"><label class="form-label">Start Date <span class="text-danger">*</span></label><input type="text" name="start_date" class="form-control datepicker" required><div class="invalid-feedback" data-field="start_date"></div></div>
                        <div class="col-md-6"><label class="form-label">End Date <span class="text-danger">*</span></label><input type="text" name="end_date" class="form-control datepicker" required><div class="invalid-feedback" data-field="end_date"></div></div>
                    </div>
                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Submit Freeze Request</button>
                        <a href="/freezes" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    $('#freeze-member-select').select2({
        theme:'bootstrap-5', width:'100%', minimumInputLength:2, allowClear:true,
        ajax: {
            url:'/api/members/search', dataType:'json', delay:300,
            data:function(p){ return {q:p.term}; },
            processResults:function(data){ return {results:(data.data||data).map(function(m){ return {id:m.id, text:m.membership_number+' - '+m.name}; })}; }
        }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\freezes\index.php

```php
<div class="row g-3 mb-4">
    <div class="col-md-3"><div class="card border-warning h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Pending</h6><h3 class="mb-0"><?= $totalPending ?></h3></div><div class="align-self-center"><i class="fas fa-hourglass-half fa-2x text-warning opacity-50"></i></div></div></div></div></div>
    <div class="col-md-3"><div class="card border-info h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Approved</h6><h3 class="mb-0"><?= $totalApproved ?></h3></div><div class="align-self-center"><i class="fas fa-check fa-2x text-info opacity-50"></i></div></div></div></div></div>
    <div class="col-md-3"><div class="card border-primary h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Active</h6><h3 class="mb-0"><?= $totalActive ?></h3></div><div class="align-self-center"><i class="fas fa-snowflake fa-2x text-primary opacity-50"></i></div></div></div></div></div>
    <div class="col-md-3"><div class="card border-secondary h-100"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="text-muted mb-1">Ended</h6><h3 class="mb-0"><?= $totalEnded ?></h3></div><div class="align-self-center"><i class="fas fa-stop-circle fa-2x text-secondary opacity-50"></i></div></div></div></div></div>
</div>

<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-snowflake me-2"></i><?= lang('subs.freezes') ?></h4>
    <?php if (auth_can('freezes.create')): ?>
    <a href="/freezes/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('subs.create_freeze') ?></a>
    <?php endif; ?>
</div>

<div class="card mb-3">
    <div class="card-body py-2">
        <form class="row g-2 align-items-end" id="freeze-filters" data-filter-table="freezes-table">
            <div class="col-md-3">
                <select name="status" class="form-select form-select-sm">
                    <option value="">All Statuses</option>
                    <option value="pending">Pending</option><option value="approved">Approved</option>
                    <option value="active">Active</option><option value="ended">Ended</option>
                    <option value="cancelled">Cancelled</option><option value="rejected">Rejected</option>
                </select>
            </div>
            <div class="col-md-2">
                <button type="button" class="btn btn-sm btn-outline-primary" id="btn-filter-freeze"><i class="fas fa-filter"></i></button>
                <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-clear-freeze"><i class="fas fa-times"></i></button>
            </div>
        </form>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="freezes-table" class="table table-striped table-hover w-100"
               data-url="/api/freezes/datatable"
               data-columns='<?= json_encode([
                   ["data" => "freeze_number", "title" => "Freeze #"],
                   ["data" => "membership_number", "title" => "Member #"],
                   ["data" => "member_name", "title" => "Name"],
                   ["data" => "reason", "title" => "Reason"],
                   ["data" => "start_date", "title" => "Start"],
                   ["data" => "end_date", "title" => "End"],
                   ["data" => "duration", "title" => "Duration"],
                   ["data" => "fee", "title" => "Fee"],
                   ["data" => "status", "title" => "Status"],
                   ["data" => "actions", "title" => "", "orderable" => false, "searchable" => false]
               ]) ?>'>
            <thead><tr><th>Freeze #</th><th>Member #</th><th>Name</th><th>Reason</th><th>Start</th><th>End</th><th>Duration</th><th>Fee</th><th>Status</th><th></th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('freezes-table');
    $('#btn-filter-freeze').on('click', function(){ DataTableHelper.reload('freezes-table'); });
    $('#btn-clear-freeze').on('click', function(){ $('#freeze-filters select').val(''); DataTableHelper.reload('freezes-table'); });

    // Approve
    $(document).on('click', '.btn-approve-freeze', function(){
        var id = $(this).data('id');
        Swal.fire({ title:'Approve Freeze?', icon:'question', showCancelButton:true, confirmButtonText:'Approve', confirmButtonColor:'#28a745' }).then(function(r){
            if(r.isConfirmed){
                $.post('/api/freezes/'+id+'/approve', {_token: $('meta[name="csrf-token"]').attr('content')}, function(res){
                    if(res.success){ FormHelper.showToast('success', res.message); DataTableHelper.reload('freezes-table'); }
                    else { FormHelper.showToast('error', res.message); }
                });
            }
        });
    });

    // Reject
    $(document).on('click', '.btn-reject-freeze', function(){
        var id = $(this).data('id');
        Swal.fire({ title:'Reject Freeze?', input:'textarea', inputPlaceholder:'Rejection reason...', icon:'warning', showCancelButton:true, confirmButtonText:'Reject', confirmButtonColor:'#dc3545' }).then(function(r){
            if(r.isConfirmed){
                $.post('/api/freezes/'+id+'/reject', {_token: $('meta[name="csrf-token"]').attr('content'), rejection_reason: r.value}, function(res){
                    if(res.success){ FormHelper.showToast('success', res.message); DataTableHelper.reload('freezes-table'); }
                    else { FormHelper.showToast('error', res.message); }
                });
            }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\freezes\policies.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-gavel me-2"></i><?= lang('subs.freeze_policies') ?></h4>
    <?php if (auth_can('freezes.approve')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#policy-modal" id="btn-add-policy"><i class="fas fa-plus me-1"></i>Add Policy</button>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="policies-table" class="table table-striped w-100"
               data-url="/api/freezes/policies/datatable"
               data-columns='<?= json_encode([
                   ["data"=>"policy_code","title"=>"Code"],["data"=>"name_en","title"=>"Name"],["data"=>"name_ar","title"=>"Name (AR)"],
                   ["data"=>"type_name","title"=>"Type"],["data"=>"max_days","title"=>"Max Days"],["data"=>"max_times","title"=>"Max/Year"],
                   ["data"=>"fee_type","title"=>"Fee Type"],["data"=>"fee_amount","title"=>"Fee"],["data"=>"is_active","title"=>"Active"],
                   ["data"=>"actions","title"=>"","orderable"=>false]
               ]) ?>'>
            <thead><tr><th>Code</th><th>Name</th><th>Name (AR)</th><th>Type</th><th>Max Days</th><th>Max/Year</th><th>Fee Type</th><th>Fee</th><th>Active</th><th></th></tr></thead>
        </table>
    </div>
</div>

<!-- Policy Modal -->
<div class="modal fade" id="policy-modal" tabindex="-1">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header"><h5 class="modal-title" id="policy-modal-title">Add Policy</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <form id="policy-form" method="POST" action="/freezes/policies" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-body">
                    <div class="row g-3">
                        <div class="col-md-4"><label class="form-label">Code *</label><input type="text" name="policy_code" class="form-control" required></div>
                        <div class="col-md-4"><label class="form-label">Name (EN) *</label><input type="text" name="name_en" class="form-control" required></div>
                        <div class="col-md-4"><label class="form-label">Name (AR) *</label><input type="text" name="name_ar" class="form-control" required dir="rtl"></div>
                        <div class="col-md-6"><label class="form-label">Membership Type</label>
                            <select name="membership_type_id" class="form-select"><option value="">All Types</option>
                                <?php foreach ($types as $t): ?><option value="<?= $t['id'] ?>"><?= e($t['name_en']) ?></option><?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-3"><label class="form-label">Min Days *</label><input type="number" name="min_days" class="form-control" value="1" min="1" required></div>
                        <div class="col-md-3"><label class="form-label">Max Days</label><input type="number" name="max_days" class="form-control"></div>
                        <div class="col-md-3"><label class="form-label">Max Times/Year</label><input type="number" name="max_times_per_year" class="form-control"></div>
                        <div class="col-md-3"><label class="form-label">Max Total Days/Year</label><input type="number" name="max_total_days_per_year" class="form-control"></div>
                        <div class="col-md-3"><label class="form-label">Fee Type *</label>
                            <select name="fee_type" class="form-select" required>
                                <option value="none">None</option><option value="fixed">Fixed</option>
                                <option value="per_day">Per Day</option><option value="percentage_of_subscription">% of Sub</option>
                            </select>
                        </div>
                        <div class="col-md-3"><label class="form-label">Fee Amount</label><input type="number" step="0.01" name="fee_amount" class="form-control" value="0"></div>
                        <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="requires_approval" value="1" class="form-check-input" id="pol-approval"><label class="form-check-label" for="pol-approval">Requires Approval</label></div></div>
                        <div class="col-md-3"><div class="form-check mt-4"><input type="checkbox" name="extend_subscription" value="1" class="form-check-input" id="pol-extend" checked><label class="form-check-label" for="pol-extend">Extend Subscription</label></div></div>
                        <div class="col-md-3"><div class="form-check"><input type="checkbox" name="is_active" value="1" class="form-check-input" id="pol-active" checked><label class="form-check-label" for="pol-active">Active</label></div></div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Save</button>
                </div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('policies-table');
    $('#btn-add-policy').on('click', function(){ $('#policy-modal-title').text('Add Policy'); $('#policy-form')[0].reset(); $('#policy-form').attr('action','/freezes/policies'); });
    $(document).on('click', '.btn-edit-policy', function(){
        var id=$(this).data('id');
        $.get('/api/freezes/policies/'+id, function(res){
            if(res.success){
                var d=res.data; $('#policy-modal-title').text('Edit Policy'); $('#policy-form').attr('action','/freezes/policies/'+id);
                Object.keys(d).forEach(function(k){ var el=$('#policy-form [name='+k+']'); if(el.length){ if(el.is(':checkbox')){el.prop('checked',d[k]==1);}else{el.val(d[k]);} }});
                new bootstrap.Modal('#policy-modal').show();
            }
        });
    });
    $(document).on('ajaxSuccess', function(e,xhr,s){ if(s.url&&s.url.includes('/freezes/policies')){ DataTableHelper.reload('policies-table'); bootstrap.Modal.getInstance(document.getElementById('policy-modal'))?.hide(); }});
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\freezes\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-snowflake me-2"></i><?= e($freeze['freeze_number']) ?> <?= status_badge($freeze['status']) ?></h4>
    <div class="d-flex gap-2">
        <?php if (in_array($freeze['status'], ['active', 'approved']) && auth_can('freezes.approve')): ?>
        <button class="btn btn-warning" id="btn-end-freeze"><i class="fas fa-stop me-1"></i>End Freeze</button>
        <button class="btn btn-info" data-bs-toggle="modal" data-bs-target="#extend-modal"><i class="fas fa-plus me-1"></i>Extend</button>
        <?php endif; ?>
        <?php if ($freeze['status'] === 'pending' && auth_can('freezes.approve')): ?>
        <button class="btn btn-success btn-approve-show" data-id="<?= $freeze['id'] ?>"><i class="fas fa-check me-1"></i>Approve</button>
        <button class="btn btn-danger btn-reject-show" data-id="<?= $freeze['id'] ?>"><i class="fas fa-times me-1"></i>Reject</button>
        <?php endif; ?>
        <a href="/freezes" class="btn btn-outline-secondary"><i class="fas fa-arrow-left me-1"></i>Back</a>
    </div>
</div>

<div class="row g-4">
    <div class="col-md-8">
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Freeze Details</h6></div>
            <div class="card-body">
                <div class="row g-3">
                    <div class="col-md-6"><strong>Member:</strong><br><a href="/members/<?= $freeze['member_id'] ?>"><?= e($freeze['membership_number'] . ' - ' . $freeze['member_name']) ?></a></div>
                    <div class="col-md-6"><strong>Policy:</strong><br><?= e($freeze['policy_name'] ?? 'N/A') ?></div>
                    <div class="col-md-4"><strong>Start Date:</strong><br><?= format_date($freeze['start_date']) ?></div>
                    <div class="col-md-4"><strong>Original End:</strong><br><?= format_date($freeze['original_end_date']) ?></div>
                    <div class="col-md-4"><strong>Actual End:</strong><br><?= $freeze['actual_end_date'] ? format_date($freeze['actual_end_date']) : '<em>â€”</em>' ?></div>
                    <div class="col-md-4"><strong>Duration:</strong><br><?= $freeze['duration_days'] ?> days</div>
                    <div class="col-md-4"><strong>Fee:</strong><br><?= format_money($freeze['fee_amount']) ?> <?= $freeze['fee_paid'] ? '<span class="badge bg-success">Paid</span>' : '<span class="badge bg-warning">Unpaid</span>' ?></div>
                    <div class="col-md-4"><strong>Category:</strong><br><?= e($freeze['reason_category'] ?? '-') ?></div>
                    <div class="col-12"><strong>Reason:</strong><br><p class="mb-0"><?= e($freeze['freeze_reason']) ?></p></div>
                </div>
            </div>
        </div>

        <?php if ($freeze['rejection_reason']): ?>
        <div class="alert alert-danger"><strong>Rejection Reason:</strong> <?= e($freeze['rejection_reason']) ?></div>
        <?php endif; ?>
        <?php if ($freeze['early_end_reason']): ?>
        <div class="alert alert-warning"><strong>Early End Reason:</strong> <?= e($freeze['early_end_reason']) ?></div>
        <?php endif; ?>

        <!-- Extensions -->
        <?php if (!empty($extensions)): ?>
        <div class="card">
            <div class="card-header"><h6 class="mb-0">Extensions</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Days</th><th>New End</th><th>Reason</th><th>Status</th><th>Approved By</th></tr></thead>
                    <tbody>
                    <?php foreach ($extensions as $ext): ?>
                    <tr>
                        <td>+<?= $ext['extension_days'] ?></td>
                        <td><?= format_date($ext['new_end_date']) ?></td>
                        <td><?= e($ext['reason']) ?></td>
                        <td><?= status_badge($ext['status']) ?></td>
                        <td><?= e($ext['approved_by_name'] ?? '-') ?></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>
    </div>

    <div class="col-md-4">
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Timeline</h6></div>
            <div class="card-body">
                <ul class="list-unstyled mb-0">
                    <li class="mb-2"><small class="text-muted"><?= format_datetime($freeze['created_at']) ?></small><br>Created by <?= e($freeze['created_by_name'] ?? 'System') ?></li>
                    <?php if ($freeze['approved_at']): ?><li class="mb-2"><small class="text-muted"><?= format_datetime($freeze['approved_at']) ?></small><br>Approved by <?= e($freeze['approved_by_name'] ?? '-') ?></li><?php endif; ?>
                    <?php if ($freeze['activated_at']): ?><li class="mb-2"><small class="text-muted"><?= format_datetime($freeze['activated_at']) ?></small><br>Activated</li><?php endif; ?>
                    <?php if ($freeze['ended_at']): ?><li class="mb-2"><small class="text-muted"><?= format_datetime($freeze['ended_at']) ?></small><br>Ended by <?= e($freeze['ended_by_name'] ?? '-') ?></li><?php endif; ?>
                </ul>
            </div>
        </div>
    </div>
</div>

<!-- Extend Modal -->
<div class="modal fade" id="extend-modal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header"><h5 class="modal-title">Extend Freeze</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <form id="extend-form">
                <div class="modal-body">
                    <div class="mb-3"><label class="form-label">Extension Days <span class="text-danger">*</span></label><input type="number" name="extension_days" class="form-control" min="1" max="365" required></div>
                    <div class="mb-3"><label class="form-label">Reason <span class="text-danger">*</span></label><textarea name="reason" class="form-control" rows="3" required></textarea></div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    <button type="submit" class="btn btn-primary">Extend</button>
                </div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    var freezeId = <?= $freeze['id'] ?>;
    var token = $('meta[name="csrf-token"]').attr('content');

    $('#btn-end-freeze').on('click', function(){
        Swal.fire({title:'End Freeze Early?', input:'text', inputPlaceholder:'Reason...', showCancelButton:true, confirmButtonColor:'#ffc107'}).then(function(r){
            if(r.isConfirmed){
                $.post('/api/freezes/'+freezeId+'/end', {_token:token, reason:r.value||'Early termination'}, function(res){
                    if(res.success){ FormHelper.showToast('success',res.message); setTimeout(function(){location.reload();},800); }
                    else { FormHelper.showToast('error',res.message); }
                });
            }
        });
    });

    $('#extend-form').on('submit', function(e){
        e.preventDefault();
        $.post('/api/freezes/'+freezeId+'/extend', $(this).serialize()+'&_token='+token, function(res){
            if(res.success){ FormHelper.showToast('success',res.message); setTimeout(function(){location.reload();},800); }
            else { FormHelper.showToast('error',res.message||'Failed'); }
        });
    });

    $('.btn-approve-show').on('click', function(){
        $.post('/api/freezes/'+freezeId+'/approve', {_token:token}, function(res){
            if(res.success){ FormHelper.showToast('success',res.message); setTimeout(function(){location.reload();},800); }
        });
    });

    $('.btn-reject-show').on('click', function(){
        Swal.fire({title:'Reject?', input:'textarea', showCancelButton:true, confirmButtonColor:'#dc3545'}).then(function(r){
            if(r.isConfirmed){ $.post('/api/freezes/'+freezeId+'/reject', {_token:token, rejection_reason:r.value}, function(res){ if(res.success){location.reload();} }); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\periods\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-calendar-alt me-2"></i><?= lang('subs.subscription_periods') ?></h4>
    <?php if (auth_can('members.create')): ?>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#period-modal" id="btn-add-period">
        <i class="fas fa-plus me-1"></i><?= lang('subs.add_period') ?>
    </button>
    <?php endif; ?>
</div>

<div class="card">
    <div class="card-body">
        <table id="periods-table" class="table table-striped table-hover w-100"
               data-url="/api/subscriptions/periods/datatable"
               data-columns='<?= json_encode([
                   ["data" => "period_code", "title" => "Code"],
                   ["data" => "name_en", "title" => "Name (EN)"],
                   ["data" => "name_ar", "title" => "Name (AR)"],
                   ["data" => "start_date", "title" => "Start"],
                   ["data" => "end_date", "title" => "End"],
                   ["data" => "grace_end", "title" => "Grace End"],
                   ["data" => "sub_count", "title" => "Subs"],
                   ["data" => "is_active", "title" => "Active"],
                   ["data" => "actions", "title" => "", "orderable" => false]
               ]) ?>'>
            <thead><tr><th>Code</th><th>Name (EN)</th><th>Name (AR)</th><th>Start</th><th>End</th><th>Grace End</th><th>Subs</th><th>Active</th><th></th></tr></thead>
        </table>
    </div>
</div>

<!-- Period Modal -->
<div class="modal fade" id="period-modal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header"><h5 class="modal-title" id="period-modal-title">Add Period</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <form id="period-form" method="POST" action="/subscriptions/periods" data-ajax="true">
                <?= csrf_field() ?>
                <div class="modal-body">
                    <div class="mb-3"><label class="form-label">Code <span class="text-danger">*</span></label><input type="text" name="period_code" class="form-control" required><div class="invalid-feedback" data-field="period_code"></div></div>
                    <div class="mb-3"><label class="form-label">Name (EN) <span class="text-danger">*</span></label><input type="text" name="name_en" class="form-control" required><div class="invalid-feedback" data-field="name_en"></div></div>
                    <div class="mb-3"><label class="form-label">Name (AR) <span class="text-danger">*</span></label><input type="text" name="name_ar" class="form-control" required dir="rtl"><div class="invalid-feedback" data-field="name_ar"></div></div>
                    <div class="row g-3">
                        <div class="col-md-6"><label class="form-label">Start Date <span class="text-danger">*</span></label><input type="text" name="start_date" class="form-control datepicker" required><div class="invalid-feedback" data-field="start_date"></div></div>
                        <div class="col-md-6"><label class="form-label">End Date <span class="text-danger">*</span></label><input type="text" name="end_date" class="form-control datepicker" required><div class="invalid-feedback" data-field="end_date"></div></div>
                    </div>
                    <div class="mb-3 mt-3"><label class="form-label">Grace End Date</label><input type="text" name="grace_end_date" class="form-control datepicker"></div>
                    <div class="form-check"><input type="checkbox" name="is_active" value="1" class="form-check-input" id="period-active" checked><label class="form-check-label" for="period-active">Active</label></div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= lang('common.cancel') ?></button>
                    <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                </div>
            </form>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    DataTableHelper.init('periods-table');

    $('#btn-add-period').on('click', function(){
        $('#period-modal-title').text('Add Period');
        $('#period-form')[0].reset();
        $('#period-form').attr('action', '/subscriptions/periods');
    });

    $(document).on('click', '.btn-edit-period', function(){
        var id = $(this).data('id');
        $.get('/api/subscriptions/periods/'+id, function(res){
            if(res.success){
                var d = res.data;
                $('#period-modal-title').text('Edit Period');
                $('#period-form').attr('action', '/subscriptions/periods/'+id);
                $('#period-form [name=period_code]').val(d.period_code);
                $('#period-form [name=name_en]').val(d.name_en);
                $('#period-form [name=name_ar]').val(d.name_ar);
                $('#period-form [name=start_date]').val(d.start_date);
                $('#period-form [name=end_date]').val(d.end_date);
                $('#period-form [name=grace_end_date]').val(d.grace_end_date||'');
                $('#period-active').prop('checked', d.is_active == 1);
                new bootstrap.Modal('#period-modal').show();
            }
        });
    });

    // Reload table on form success
    $(document).on('ajaxSuccess', function(e, xhr, settings){
        if(settings.url && settings.url.includes('/subscriptions/periods')){
            DataTableHelper.reload('periods-table');
            bootstrap.Modal.getInstance(document.getElementById('period-modal'))?.hide();
        }
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\subscriptions\bulk_renew.php

```php
<div class="card mb-3">
    <div class="card-header"><h5 class="mb-0"><i class="fas fa-redo me-2"></i><?= lang('subs.bulk_renewal') ?></h5></div>
    <div class="card-body">
        <form id="bulk-renew-form" method="POST" action="/subscriptions/bulk-renew" data-ajax="true">
            <?= csrf_field() ?>
            <div class="row g-3 mb-3">
                <div class="col-md-5">
                    <label class="form-label"><?= lang('subs.period') ?> <span class="text-danger">*</span></label>
                    <select name="period_id" id="bulk-period" class="form-select" required>
                        <option value="">-- Select Period --</option>
                        <?php foreach ($periods as $p): ?>
                        <option value="<?= $p['id'] ?>"><?= e($p['name_en']) ?> (<?= $p['start_date'] ?> â†’ <?= $p['end_date'] ?>)</option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-4">
                    <label class="form-label"><?= lang('subs.membership_type') ?></label>
                    <select id="bulk-type" class="form-select">
                        <option value="">All Types</option>
                        <?php foreach ($types as $t): ?>
                        <option value="<?= $t['id'] ?>"><?= e($t['name_en']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="col-md-3 d-flex align-items-end">
                    <button type="button" class="btn btn-outline-primary w-100" id="btn-load-eligible">
                        <i class="fas fa-search me-1"></i>Load Eligible Members
                    </button>
                </div>
            </div>

            <div id="eligible-container" class="d-none">
                <div class="d-flex justify-content-between align-items-center mb-2">
                    <div><strong id="eligible-count">0</strong> eligible members</div>
                    <div>
                        <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-select-all">Select All</button>
                        <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-deselect-all">Deselect All</button>
                    </div>
                </div>
                <div class="table-responsive" style="max-height:400px;overflow-y:auto">
                    <table class="table table-sm table-hover" id="eligible-table">
                        <thead class="table-light sticky-top"><tr>
                            <th style="width:40px"><input type="checkbox" id="check-all"></th>
                            <th>Member #</th><th>Name</th><th>Phone</th><th>Type</th><th>Last Sub End</th>
                        </tr></thead>
                        <tbody></tbody>
                    </table>
                </div>
                <hr>
                <button type="submit" class="btn btn-success" id="btn-process-bulk" disabled>
                    <i class="fas fa-check-double me-1"></i>Process Bulk Renewal (<span id="selected-count">0</span> selected)
                </button>
            </div>
        </form>
    </div>
</div>

<!-- Results -->
<div id="bulk-results" class="d-none">
    <div class="alert" id="bulk-results-alert"></div>
    <div id="bulk-errors-list"></div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    function updateSelected(){
        var c = $('#eligible-table input[name="member_ids[]"]:checked').length;
        $('#selected-count').text(c);
        $('#btn-process-bulk').prop('disabled', c === 0);
    }

    $('#btn-load-eligible').on('click', function(){
        var periodId = $('#bulk-period').val();
        if(!periodId){ Swal.fire('Error','Select a period','warning'); return; }
        var btn = $(this); btn.prop('disabled',true).html('<i class="fas fa-spinner fa-spin me-1"></i>Loading...');

        $.get('/api/subscriptions/eligible', { period_id: periodId, type_id: $('#bulk-type').val() }, function(res){
            var tbody = $('#eligible-table tbody').empty();
            if(res.data && res.data.length){
                res.data.forEach(function(m){
                    tbody.append('<tr><td><input type="checkbox" name="member_ids[]" value="'+m.id+'"></td>'+
                        '<td>'+m.membership_number+'</td><td>'+m.full_name_en+'</td><td>'+(m.primary_phone||'-')+'</td>'+
                        '<td>'+m.type_name+'</td><td>'+(m.last_sub_end||'None')+'</td></tr>');
                });
                $('#eligible-count').text(res.data.length);
                $('#eligible-container').removeClass('d-none');
            } else {
                $('#eligible-container').addClass('d-none');
                Swal.fire('Info','No eligible members found','info');
            }
            btn.prop('disabled',false).html('<i class="fas fa-search me-1"></i>Load Eligible Members');
        });
    });

    $(document).on('change', '#eligible-table input[type=checkbox]', updateSelected);
    $('#check-all').on('change', function(){ $('#eligible-table input[name="member_ids[]"]').prop('checked', this.checked); updateSelected(); });
    $('#btn-select-all').on('click', function(){ $('#eligible-table input[name="member_ids[]"]').prop('checked',true); $('#check-all').prop('checked',true); updateSelected(); });
    $('#btn-deselect-all').on('click', function(){ $('#eligible-table input[name="member_ids[]"]').prop('checked',false); $('#check-all').prop('checked',false); updateSelected(); });

    // Override form submission to handle bulk result
    $('#bulk-renew-form').attr('data-ajax','false').on('submit', function(e){
        e.preventDefault();
        var btn = $('#btn-process-bulk');
        btn.prop('disabled',true).html('<i class="fas fa-spinner fa-spin me-1"></i>Processing...');

        $.ajax({
            url: $(this).attr('action'), type: 'POST', data: $(this).serialize(),
            headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'), 'X-Requested-With':'XMLHttpRequest'},
            success: function(res){
                var cls = res.failed_count > 0 ? 'alert-warning' : 'alert-success';
                $('#bulk-results-alert').attr('class','alert '+cls).html('<strong>'+res.message+'</strong>');
                if(res.errors && res.errors.length){
                    var html = '<ul class="list-group">';
                    res.errors.forEach(function(e){ html += '<li class="list-group-item list-group-item-danger small">'+e+'</li>'; });
                    html += '</ul>';
                    $('#bulk-errors-list').html(html);
                } else { $('#bulk-errors-list').html(''); }
                $('#bulk-results').removeClass('d-none');
            },
            error: function(){ Swal.fire('Error','Bulk renewal failed','error'); },
            complete: function(){ btn.prop('disabled',false).html('<i class="fas fa-check-double me-1"></i>Process Bulk Renewal (<span id="selected-count">0</span> selected)'); }
        });
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\subscriptions\create.php

```php
<div class="row justify-content-center">
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header"><h5 class="mb-0"><i class="fas fa-plus-circle me-2"></i><?= lang('subs.create_subscription') ?></h5></div>
            <div class="card-body">
                <form id="sub-create-form" method="POST" action="/subscriptions" data-ajax="true">
                    <?= csrf_field() ?>
                    <!-- Member Lookup -->
                    <div class="mb-3">
                        <label class="form-label"><?= lang('subs.member') ?> <span class="text-danger">*</span></label>
                        <select name="member_id" id="member-select" class="form-select select2" style="width:100%"
                                data-placeholder="Search member..." required>
                            <?php if ($member): ?>
                            <option value="<?= $member['id'] ?>" selected><?= e($member['membership_number'] . ' - ' . $member['full_name_en']) ?></option>
                            <?php endif; ?>
                        </select>
                        <div class="invalid-feedback" data-field="member_id"></div>
                    </div>
                    <!-- Period -->
                    <div class="mb-3">
                        <label class="form-label"><?= lang('subs.period') ?> <span class="text-danger">*</span></label>
                        <select name="subscription_period_id" id="period-select" class="form-select" required>
                            <option value="">-- Select Period --</option>
                            <?php foreach ($periods as $p): ?>
                            <option value="<?= $p['id'] ?>" data-start="<?= $p['start_date'] ?>" data-end="<?= $p['end_date'] ?>">
                                <?= e($p['name_en'] . ' (' . $p['start_date'] . ' â†’ ' . $p['end_date'] . ')') ?>
                            </option>
                            <?php endforeach; ?>
                        </select>
                        <div class="invalid-feedback" data-field="subscription_period_id"></div>
                    </div>
                    <!-- Fee Calculation -->
                    <div id="fee-section" class="d-none">
                        <div class="card bg-light mb-3">
                            <div class="card-body">
                                <div class="row g-2">
                                    <div class="col-md-4"><label class="form-label">Base Amount</label><input type="text" class="form-control-plaintext fw-bold" id="disp-base" readonly></div>
                                    <div class="col-md-4"><label class="form-label">Dependents (<span id="disp-dep-count">0</span>)</label><input type="text" class="form-control-plaintext" id="disp-dep" readonly></div>
                                    <div class="col-md-4"><label class="form-label">Late Fee</label><input type="text" class="form-control-plaintext text-danger" id="disp-late" readonly></div>
                                </div>
                                <hr>
                                <div class="row g-2">
                                    <div class="col-md-6"><label class="form-label">Discount</label>
                                        <input type="number" step="0.01" min="0" name="discount_amount" id="discount-input" class="form-control" value="0">
                                    </div>
                                    <div class="col-md-6"><label class="form-label fw-bold">Total</label>
                                        <input type="text" class="form-control-plaintext fs-4 fw-bold text-primary" id="disp-total" readonly>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <input type="hidden" name="amount" id="amount-input">
                        <input type="hidden" name="dependents_amount" id="dep-amount-input">
                        <input type="hidden" name="late_fee" id="late-fee-input">
                    </div>
                    <!-- Notes -->
                    <div class="mb-3">
                        <label class="form-label"><?= lang('subs.notes') ?></label>
                        <textarea name="notes" class="form-control" rows="2"></textarea>
                    </div>
                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary" id="btn-submit-sub" disabled>
                            <i class="fas fa-save me-1"></i><?= lang('common.save') ?>
                        </button>
                        <a href="/subscriptions" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    // Member Select2 AJAX
    $('#member-select').select2({
        theme: 'bootstrap-5', width: '100%', minimumInputLength: 2, allowClear: true,
        ajax: {
            url: '/api/members/search',
            dataType: 'json', delay: 300,
            data: function(params){ return { q: params.term }; },
            processResults: function(data){
                return { results: (data.data || data).map(function(m){
                    return { id: m.id, text: m.membership_number + ' - ' + m.name };
                })};
            }
        }
    });

    function calculateFee(){
        var memberId = $('#member-select').val();
        var periodId = $('#period-select').val();
        if(!memberId || !periodId){ $('#fee-section').addClass('d-none'); $('#btn-submit-sub').prop('disabled',true); return; }

        $.get('/api/subscriptions/calculate-fee', { member_id: memberId, period_id: periodId }, function(res){
            if(res.success){
                var d = res;
                $('#disp-base').val(d.base_amount.toFixed(2) + ' EGP');
                $('#disp-dep-count').text(d.dep_count);
                $('#disp-dep').val(d.dep_amount.toFixed(2) + ' EGP');
                $('#disp-late').val(d.late_fee.toFixed(2) + ' EGP');
                $('#amount-input').val(d.base_amount);
                $('#dep-amount-input').val(d.dep_amount);
                $('#late-fee-input').val(d.late_fee);
                updateTotal();
                $('#fee-section').removeClass('d-none');
                $('#btn-submit-sub').prop('disabled', false);
            }
        });
    }

    function updateTotal(){
        var base = parseFloat($('#amount-input').val()) || 0;
        var dep  = parseFloat($('#dep-amount-input').val()) || 0;
        var late = parseFloat($('#late-fee-input').val()) || 0;
        var disc = parseFloat($('#discount-input').val()) || 0;
        var total = base + dep + late - disc;
        $('#disp-total').val(total.toFixed(2) + ' EGP');
    }

    $('#member-select, #period-select').on('change', calculateFee);
    $('#discount-input').on('input', updateTotal);

    <?php if ($member): ?>
    setTimeout(calculateFee, 300);
    <?php endif; ?>
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\subscriptions\expiring.php

```php
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><i class="fas fa-exclamation-triangle text-warning me-2"></i><?= lang('subs.expiring_subscriptions') ?></h4>
    <div class="d-flex gap-2 align-items-center">
        <label class="form-label mb-0 me-2">Days ahead:</label>
        <select id="expiry-days" class="form-select form-select-sm" style="width:120px">
            <option value="7">7 days</option>
            <option value="14">14 days</option>
            <option value="30" selected>30 days</option>
            <option value="60">60 days</option>
            <option value="90">90 days</option>
        </select>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <table id="expiring-table" class="table table-striped table-hover w-100"
               data-url="/api/subscriptions/expiring/datatable"
               data-columns='<?= json_encode([
                   ["data" => "membership_number", "title" => "Member #"],
                   ["data" => "member_name", "title" => "Name"],
                   ["data" => "phone", "title" => "Phone"],
                   ["data" => "type_name", "title" => "Type"],
                   ["data" => "end_date", "title" => "Expires"],
                   ["data" => "days_remaining", "title" => "Remaining"],
                   ["data" => "actions", "title" => "", "orderable" => false, "searchable" => false]
               ]) ?>'>
            <thead><tr><th>Member #</th><th>Name</th><th>Phone</th><th>Type</th><th>Expires</th><th>Remaining</th><th></th></tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    var dt = DataTableHelper.init('expiring-table', {
        ajax: {
            url: '/api/subscriptions/expiring/datatable',
            data: function(d){ d.days = $('#expiry-days').val(); }
        }
    });
    $('#expiry-days').on('change', function(){ DataTableHelper.reload('expiring-table'); });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\subscriptions\index.php

```php
<!-- Stats Cards -->
<div class="row g-3 mb-4">
    <div class="col-md-3">
        <div class="card border-success h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div>
                <h6 class="text-muted mb-1"><?= lang('subs.active') ?></h6>
                <h3 class="mb-0"><?= number_format($totalActive) ?></h3>
            </div><div class="align-self-center"><i class="fas fa-check-circle fa-2x text-success opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-warning h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div>
                <h6 class="text-muted mb-1"><?= lang('subs.pending') ?></h6>
                <h3 class="mb-0"><?= number_format($totalPending) ?></h3>
            </div><div class="align-self-center"><i class="fas fa-clock fa-2x text-warning opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-danger h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div>
                <h6 class="text-muted mb-1"><?= lang('subs.expiring_30') ?></h6>
                <h3 class="mb-0"><?= number_format($expiringIn30) ?></h3>
            </div><div class="align-self-center"><i class="fas fa-exclamation-triangle fa-2x text-danger opacity-50"></i></div></div>
        </div></div>
    </div>
    <div class="col-md-3">
        <div class="card border-secondary h-100"><div class="card-body">
            <div class="d-flex justify-content-between"><div>
                <h6 class="text-muted mb-1"><?= lang('subs.expired') ?></h6>
                <h3 class="mb-0"><?= number_format($totalExpired) ?></h3>
            </div><div class="align-self-center"><i class="fas fa-times-circle fa-2x text-secondary opacity-50"></i></div></div>
        </div></div>
    </div>
</div>

<!-- Toolbar -->
<div class="d-flex justify-content-between align-items-center mb-3">
    <h4 class="mb-0"><?= lang('subs.subscriptions') ?></h4>
    <?php if (auth_can('members.create')): ?>
    <a href="/subscriptions/create" class="btn btn-primary"><i class="fas fa-plus me-1"></i><?= lang('subs.create_subscription') ?></a>
    <?php endif; ?>
</div>

<!-- Filters -->
<div class="card mb-3">
    <div class="card-body py-2">
        <form class="row g-2 align-items-end" id="sub-filters" data-filter-table="subscriptions-table">
            <div class="col-md-3">
                <label class="form-label form-label-sm"><?= lang('common.status') ?></label>
                <select name="status" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <option value="pending">Pending</option>
                    <option value="active">Active</option>
                    <option value="expired">Expired</option>
                    <option value="cancelled">Cancelled</option>
                    <option value="frozen">Frozen</option>
                </select>
            </div>
            <div class="col-md-3">
                <label class="form-label form-label-sm"><?= lang('subs.period') ?></label>
                <select name="period_id" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <?php foreach ($periods as $p): ?>
                    <option value="<?= $p['id'] ?>"><?= e($p['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-3">
                <label class="form-label form-label-sm"><?= lang('subs.membership_type') ?></label>
                <select name="type_id" class="form-select form-select-sm">
                    <option value=""><?= lang('common.all') ?></option>
                    <?php foreach ($types as $t): ?>
                    <option value="<?= $t['id'] ?>"><?= e($t['name_en']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="col-md-3">
                <button type="button" class="btn btn-sm btn-outline-primary" id="btn-filter-subs"><i class="fas fa-filter me-1"></i><?= lang('common.filter') ?></button>
                <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-clear-subs"><i class="fas fa-times me-1"></i><?= lang('common.clear') ?></button>
            </div>
        </form>
    </div>
</div>

<!-- DataTable -->
<div class="card">
    <div class="card-body">
        <table id="subscriptions-table" class="table table-striped table-hover w-100"
               data-url="/api/subscriptions/datatable"
               data-columns='<?= json_encode([
                   ["data" => "id", "title" => "#", "width" => "50px"],
                   ["data" => "membership_number", "title" => "Member #"],
                   ["data" => "member_name", "title" => "Member Name"],
                   ["data" => "type_name", "title" => "Type"],
                   ["data" => "period_name", "title" => "Period"],
                   ["data" => "start_date", "title" => "Start"],
                   ["data" => "end_date", "title" => "End"],
                   ["data" => "net_amount", "title" => "Amount"],
                   ["data" => "paid_amount", "title" => "Paid"],
                   ["data" => "status", "title" => "Status"],
                   ["data" => "actions", "title" => "Actions", "orderable" => false, "searchable" => false]
               ]) ?>'>
            <thead><tr>
                <th>#</th><th>Member #</th><th>Name</th><th>Type</th><th>Period</th>
                <th>Start</th><th>End</th><th>Amount</th><th>Paid</th><th>Status</th><th>Actions</th>
            </tr></thead>
        </table>
    </div>
</div>

<?php $__view->push('scripts') ?>
<script>
$(function(){
    const dt = DataTableHelper.init('subscriptions-table');
    $('#btn-filter-subs').on('click', function(){ DataTableHelper.reload('subscriptions-table'); });
    $('#btn-clear-subs').on('click', function(){
        $('#sub-filters select').val('');
        DataTableHelper.reload('subscriptions-table');
    });
});
</script>
<?php $__view->endPush() ?>
```

## File: modules\subscriptions\views\subscriptions\show.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <h4 class="mb-0"><i class="fas fa-file-invoice me-2"></i><?= lang('subs.subscription') ?> #<?= $sub['id'] ?></h4>
    <a href="/subscriptions" class="btn btn-outline-secondary"><i class="fas fa-arrow-left me-1"></i><?= lang('common.back') ?></a>
</div>

<div class="row g-4">
    <!-- Main Info -->
    <div class="col-md-8">
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Subscription Details</h6></div>
            <div class="card-body">
                <div class="row g-3">
                    <div class="col-md-6"><strong>Member:</strong><br><a href="/members/<?= $sub['member_id'] ?>"><?= e($sub['membership_number'] . ' - ' . $sub['member_name_en']) ?></a></div>
                    <div class="col-md-6"><strong>Type:</strong><br><?= e($sub['type_name_en']) ?></div>
                    <div class="col-md-6"><strong>Period:</strong><br><?= e($sub['period_name_en']) ?> (<?= e($sub['period_code']) ?>)</div>
                    <div class="col-md-6"><strong>Status:</strong><br><?= status_badge($sub['status']) ?></div>
                    <div class="col-md-3"><strong>Start:</strong><br><?= format_date($sub['start_date']) ?></div>
                    <div class="col-md-3"><strong>End:</strong><br><?= format_date($sub['end_date']) ?></div>
                    <div class="col-md-3"><strong>Renewal?</strong><br><?= $sub['is_renewal'] ? '<span class="badge bg-info">Yes</span>' : 'No' ?></div>
                    <div class="col-md-3"><strong>Dependents:</strong><br><?= $sub['dependents_count'] ?></div>
                </div>
                <?php if ($sub['notes']): ?><hr><p class="text-muted mb-0"><i class="fas fa-sticky-note me-1"></i><?= e($sub['notes']) ?></p><?php endif; ?>
            </div>
        </div>

        <!-- Freezes -->
        <?php if (!empty($freezes)): ?>
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0"><i class="fas fa-snowflake me-2"></i>Related Freezes</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Freeze #</th><th>Start</th><th>End</th><th>Days</th><th>Status</th></tr></thead>
                    <tbody>
                    <?php foreach ($freezes as $f): ?>
                    <tr>
                        <td><a href="/freezes/<?= $f['id'] ?>"><?= e($f['freeze_number']) ?></a></td>
                        <td><?= format_date($f['start_date']) ?></td>
                        <td><?= format_date($f['actual_end_date'] ?? $f['original_end_date']) ?></td>
                        <td><?= $f['duration_days'] ?></td>
                        <td><?= status_badge($f['status']) ?></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>

        <!-- Renewal Log -->
        <?php if (!empty($renewalLog)): ?>
        <div class="card">
            <div class="card-header"><h6 class="mb-0"><i class="fas fa-history me-2"></i>Renewal History</h6></div>
            <div class="card-body p-0">
                <table class="table table-sm mb-0">
                    <thead><tr><th>Date</th><th>Type</th><th>Late Days</th><th>Late Fee</th><th>By</th></tr></thead>
                    <tbody>
                    <?php foreach ($renewalLog as $rl): ?>
                    <tr>
                        <td><?= format_date($rl['renewal_date']) ?></td>
                        <td><?= status_badge($rl['renewal_type']) ?></td>
                        <td><?= $rl['late_days'] ?></td>
                        <td><?= format_money($rl['late_fee']) ?></td>
                        <td><?= e($rl['created_by_name'] ?? '-') ?></td>
                    </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
        <?php endif; ?>
    </div>

    <!-- Sidebar -->
    <div class="col-md-4">
        <!-- Financial -->
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Financial</h6></div>
            <div class="card-body">
                <table class="table table-sm table-borderless mb-0">
                    <tr><td>Base Amount</td><td class="text-end"><?= format_money($sub['amount']) ?></td></tr>
                    <tr><td>Discount</td><td class="text-end text-success">-<?= format_money($sub['discount_amount']) ?></td></tr>
                    <tr><td>Dependents</td><td class="text-end"><?= format_money($sub['dependents_amount']) ?></td></tr>
                    <tr><td>Late Fee</td><td class="text-end text-danger"><?= format_money($sub['late_fee']) ?></td></tr>
                    <tr class="fw-bold border-top"><td>Net Amount</td><td class="text-end"><?= format_money($sub['net_amount']) ?></td></tr>
                    <tr><td>Paid</td><td class="text-end text-success"><?= format_money($sub['paid_amount']) ?></td></tr>
                    <tr class="fw-bold border-top"><td>Balance Due</td><td class="text-end <?= $sub['balance_due'] > 0 ? 'text-danger' : 'text-success' ?>"><?= format_money($sub['balance_due']) ?></td></tr>
                </table>
            </div>
        </div>
        <?php if ($invoice): ?>
        <div class="card mb-3">
            <div class="card-header"><h6 class="mb-0">Invoice</h6></div>
            <div class="card-body">
                <a href="/finance/invoices/<?= $invoice['id'] ?>" class="fw-bold"><?= e($invoice['invoice_number']) ?></a>
                <br><?= status_badge($invoice['status']) ?>
                <br>Total: <?= format_money($invoice['total_amount']) ?>
            </div>
        </div>
        <?php endif; ?>
    </div>
</div>
```

## File: modules\subscriptions\views\widgets\active_freezes.php

```php
<?php
$db = Database::getInstance();
$active = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status = 'active'");
$pending = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_freezes WHERE status = 'pending'");
$recentFreezes = $db->fetchAll(
    "SELECT mf.freeze_number, mf.status, m.full_name_en, DATEDIFF(COALESCE(mf.actual_end_date, mf.original_end_date), CURDATE()) AS days_left
     FROM member_freezes mf JOIN members m ON mf.member_id = m.id
     WHERE mf.status IN ('active','pending')
     ORDER BY mf.created_at DESC LIMIT 5"
);
?>
<div class="card h-100">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h6 class="mb-0"><i class="fas fa-snowflake text-info me-2"></i>Freezes</h6>
        <div>
            <span class="badge bg-primary"><?= $active ?> active</span>
            <?php if ($pending): ?><span class="badge bg-warning text-dark"><?= $pending ?> pending</span><?php endif; ?>
        </div>
    </div>
    <div class="card-body p-0">
        <?php if (empty($recentFreezes)): ?>
        <div class="text-center text-muted py-4"><i class="fas fa-check-circle fa-2x mb-2"></i><br>No active freezes</div>
        <?php else: ?>
        <ul class="list-group list-group-flush">
            <?php foreach ($recentFreezes as $f): ?>
            <li class="list-group-item d-flex justify-content-between align-items-center py-2">
                <div><small class="fw-bold"><?= e($f['freeze_number']) ?></small><br><small><?= e($f['full_name_en']) ?></small></div>
                <?= status_badge($f['status']) ?>
            </li>
            <?php endforeach; ?>
        </ul>
        <?php endif; ?>
    </div>
    <div class="card-footer text-center"><a href="/freezes" class="small">View All â†’</a></div>
</div>
```

## File: modules\subscriptions\views\widgets\expiring_subscriptions.php

```php
<?php
$db = Database::getInstance();
$count = (int) $db->fetchColumn("SELECT COUNT(*) FROM member_subscriptions WHERE status = 'active' AND end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)");
$recent = $db->fetchAll(
    "SELECT ms.end_date, m.full_name_en, m.membership_number, DATEDIFF(ms.end_date, CURDATE()) AS days_left
     FROM member_subscriptions ms JOIN members m ON ms.member_id = m.id
     WHERE ms.status = 'active' AND ms.end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)
     ORDER BY ms.end_date ASC LIMIT 5"
);
?>
<div class="card h-100">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h6 class="mb-0"><i class="fas fa-exclamation-triangle text-warning me-2"></i>Expiring Subscriptions (30d)</h6>
        <span class="badge bg-warning text-dark"><?= $count ?></span>
    </div>
    <div class="card-body p-0">
        <?php if (empty($recent)): ?>
        <div class="text-center text-muted py-4"><i class="fas fa-check-circle fa-2x mb-2"></i><br>No expiring subscriptions</div>
        <?php else: ?>
        <ul class="list-group list-group-flush">
            <?php foreach ($recent as $r): ?>
            <li class="list-group-item d-flex justify-content-between align-items-center py-2">
                <div><small class="fw-bold"><?= e($r['membership_number']) ?></small><br><small><?= e($r['full_name_en']) ?></small></div>
                <span class="badge <?= $r['days_left'] <= 7 ? 'bg-danger' : 'bg-warning text-dark' ?>"><?= $r['days_left'] ?>d</span>
            </li>
            <?php endforeach; ?>
        </ul>
        <?php endif; ?>
    </div>
    <div class="card-footer text-center"><a href="/subscriptions/expiring" class="small">View All â†’</a></div>
</div>
```

## File: prompts\MASTER_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# MASTER ARCHITECTURAL PROMPT â€” CLUB MANAGEMENT SYSTEM
# Given to ALL AIs. Read ENTIRELY before writing a single line.
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

## PROJECT OVERVIEW
You are building ONE module of a large Club Management System (CMS).
The system is a PHP 8+ / MySQL 8 / HTML / CSS / JS web application.
NO frameworks (no Laravel, no Symfony, no React). Pure PHP MVC, self-contained.
The full database schema is attached. DO NOT create or alter any tables â€” the DB is pre-built.

## CRITICAL RULES â€” VIOLATE THESE AND THE BUILD FAILS

1. **NEVER modify, overwrite, or create files outside your assigned module directory** (except Phase 0 which builds the core).
2. **NEVER create database tables, alter columns, or modify stored procedures.** The DB is immutable.
3. **You MUST use the core framework classes from Phase 0.** They exist at `/core/`. You CANNOT see their source, but this document specifies their EXACT public APIs.
4. **Every file you create MUST go inside `/modules/<your_module_name>/`** following the structure below.
5. **Your module is auto-discovered.** Phase 0 scans `/modules/*/module.json` at runtime.
6. **You MUST provide a `module.json` manifest** â€” this is how the system knows your module exists.
7. **All database access goes through the core `Database` class.** Direct `mysqli_*` or raw `new PDO` is FORBIDDEN.
8. **All output goes through the core `View` class** and your module's view files.
9. **No inline CSS or JS in PHP files.** Use your module's dedicated CSS/JS files.
10. **All form submissions MUST include CSRF token.** Use `<?= csrf_field() ?>` in forms.

## DIRECTORY STRUCTURE (FULL SYSTEM)
```

## File: prompts\PHASE_0_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 0: CORE FOUNDATION â€” THE GENESIS
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST. This is the immutable foundation.
# Your code will NEVER be modified by any other AI.
# Every other phase depends on YOUR exact APIs.

## YOUR MISSION
Build the COMPLETE core framework, authentication system, main layout, dashboard shell,
and module auto-discovery system. This is the skeleton upon which all modules attach.

## WHAT YOU BUILD (Every file. No exceptions.)

### 1. `/public/index.php` â€” Front Controller
- Sets error reporting, starts output buffering
- Requires `/core/Bootstrap.php`
- Creates App instance and runs it

### 2. `/public/.htaccess` â€” Apache Rewrite
```

## File: prompts\PHASE_1_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 1: MEMBERS & DEPENDENTS MODULE
# Module directory: /modules/members/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST. Follow ALL conventions exactly.

## YOUR TABLES (read/write)
members, member_dependents, member_families, member_addresses, member_contacts,
member_documents, member_photos, member_education, member_employment,
member_notes, member_tags, member_status_history, dependent_status_history,
dependent_documents, dependent_relationship_types, dependent_age_policies,
membership_types, membership_categories, membership_type_fees,
membership_type_benefits, membership_type_documents_required,
membership_type_age_rules, membership_type_capacity,
member_endorsements, endorsement_types, member_custom_fields,
member_custom_field_values, member_balances, member_ledger (READ ONLY),
member_loyalty (READ ONLY)

## REFERENCE TABLES (read only)
sys_nationalities, sys_countries, sys_cities, sys_districts, sys_currencies, auth_users

## PAGES TO BUILD

### 1. Member Listing (`GET /members`)
- Server-side DataTable with columns: Membership #, Photo (thumbnail), Full Name (EN), National ID, Phone, Type, Status, Actions
- Filters bar above table: Status dropdown, Membership Type dropdown, Gender, Date range (join date), Search text
- Action buttons: View, Edit, Print Card
- Toolbar: Add Member, Export (CSV/Excel/PDF), Print
- Stats cards above table: Total Active, New This Month, Frozen, Suspended
- Color-coded status badges

### 2. Create Member (`GET /members/create` and `POST /members`)
- Tabbed form: Personal Info | Contact Details | Address | Employment & Education | Additional Info | Documents
- Tab 1 - Personal: Title, First/Second/Third/Last Name (EN + AR), National ID, Passport #, Nationality (Select2), DOB (Flatpickr), Gender, Marital Status, Blood Type, Photo Upload (with preview)
- Tab 2 - Contact: Primary Phone, Secondary Phone, WhatsApp, Primary Email, Secondary Email, Communication Preference, Language Preference
- Tab 3 - Address: Address Line 1 & 2, Country (Select2), City (Select2 dependent on country), District, Postal Code
- Tab 4 - Employment: Occupation, Employer, Employer Address, Job Title, Education Level
- Tab 5 - Additional: Membership Type (Select2), Sponsors (member lookup Select2), Emergency Contact (name, phone, relation), Medical Conditions, Allergies, VIP checkbox, Notes
- Tab 6 - Documents: Dynamic document upload based on membership_type_documents_required
- On save: Generate membership_number via SequenceGenerator, create member_balances record, create member_families record if family membership, audit log
- AJAX submit

### 3. View Member (`GET /members/{id}`)
- Left column: Photo (large), basic info card (name, number, type, status, QR code placeholder)
- Right column with tabs:
  - **Overview**: All personal details in read-only format, status timeline from member_status_history
  - **Family & Dependents**: List of dependents with add/edit/remove buttons (inline or modal). Show family_code. Add dependent form: name, relationship type, DOB, gender, national ID, photo, phone, documents
  - **Financial**: member_balances summary card, recent invoices table (from invoices table), recent payments table, outstanding balance, credit balance. Link to full financial view.
  - **Documents**: List with upload capability. Each doc shows: type, name, expiry, verified status. Verify button (for authorized users).
  - **Subscriptions**: List from member_subscriptions. Current active subscription highlighted.
  - **Cards**: List from member_cards. Issue new card button.
  - **Freezes**: History from member_freezes.
  - **Violations**: History from violations table.
  - **Notes**: member_notes with add/pin/delete. Confidential notes only visible to authorized users.
  - **Tags**: member_tags with add/remove (autocomplete from existing tags).
  - **Activity**: member_access_log (recent entries), visit_count, last_visit.
  - **Endorsements**: member_endorsements list.
  - **Audit**: Recent audit_log entries for this member.

### 4. Edit Member (`GET /members/{id}/edit` and `POST /members/{id}`)
- Same tabbed form as create, pre-filled with existing data
- Track changes for audit log (old vs new values)

### 5. Change Member Status (`POST /api/members/{id}/status`)
- Modal with: new status, reason, effective date
- Call appropriate stored procedure: sp_activate_member, sp_suspend_member, sp_terminate_member
- Audit log the change

### 6. Member Quick Search (`GET /api/members/search?q=term`)
- Returns JSON array for Select2 AJAX. Search by: membership_number, full_name_en, full_name_ar, national_id, primary_phone.
- Used by OTHER modules (finance, sports, etc.) to look up members.
- Format: `[{"id": 1, "text": "MEM-000001 - John Doe", "membership_number": "MEM-000001", "name": "John Doe", "phone": "..."}]`

### 7. Dependent Management (sub-routes under members)
- `GET /members/{id}/dependents/create` â€” Add dependent modal/page
- `POST /members/{id}/dependents` â€” Store dependent
- `GET /members/{id}/dependents/{depId}/edit` â€” Edit dependent
- `POST /members/{id}/dependents/{depId}` â€” Update dependent
- `POST /members/{id}/dependents/{depId}/delete` â€” Remove dependent (soft)
- Validate: relationship type max count, age limits, gender restrictions per dependent_relationship_types

### 8. Membership Types Management
- `GET /membership-types` â€” List all types with DataTable
- `GET /membership-types/create` â€” Create form
- `POST /membership-types` â€” Store
- `GET /membership-types/{id}/edit` â€” Edit form
- `POST /membership-types/{id}` â€” Update
- Form includes: All fields from membership_types table, fee configuration (membership_type_fees), benefits (membership_type_benefits), required documents (membership_type_documents_required), age rules

### 9. Membership Categories Management
- Simple CRUD for membership_categories

### 10. Print Member Profile
- `GET /members/{id}/print` â€” Print-friendly view using print layout

### 11. Endorsement Management
- `GET /members/{id}/endorsements` â€” List endorsements
- `POST /members/{id}/endorsements` â€” Create endorsement

## BUSINESS LOGIC
- Member number auto-generated via sp_generate_next_sequence('member_number')
- Family auto-created for family membership types
- Dependent count validated against membership_type.max_dependents
- full_name_en/ar auto-concatenated (the DB trigger handles this, but also set it in PHP)
- Soft delete only (set is_deleted = 1)
- VIP flag visual indicator throughout
- Blacklist flag blocks certain operations

## DASHBOARD WIDGETS
1. **Active Members Count** â€” Total active members with trend
2. **Membership Distribution** â€” Pie/donut chart by membership type
3. **New Members This Month** â€” Count with list of recent additions

## PERMISSIONS USED
members.view, members.create, members.edit, members.delete, members.status, members.financial,
dependents.view, dependents.manage
```

## File: prompts\PHASE_10_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 10: ADVANCED FINANCE MODULE
# Module directory: /modules/advanced_finance/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
installment_plan_templates, installment_plans, installment_plan_schedules,
installment_payments, credit_notes, credit_note_items, refunds, write_offs,
checks, check_status_history, bank_accounts, bank_deposits, bank_deposit_items,
gl_accounts, gl_journal_entries, gl_journal_entry_lines, gl_account_balances,
budget_heads

## PAGES TO BUILD

### 1. Installment Plans (`GET /advanced-finance/installments`)
- DataTable with vw_installment_overview data
- Create plan: member, invoice, template or custom, down payment, schedule generation
- Call `sp_create_installment_plan`
- View plan with payment schedule
- Record installment payment
- Mark defaulted

### 2. Installment Templates (`GET /advanced-finance/installment-templates`)
- CRUD for installment_plan_templates

### 3. Checks (`GET /advanced-finance/checks`)
- DataTable: Check#, Bank, Drawer, Amount, Date, Status
- Receive check registration
- Status workflow: received â†’ deposited â†’ cleared/bounced
- Bounce handling with fee
- Replacement check linking
- Scan upload (front/back)

### 4. Credit Notes (`GET /advanced-finance/credit-notes`)
- Create credit note for member
- Link to original invoice
- Application to outstanding invoices

### 5. Refunds (`GET /advanced-finance/refunds`)
- Create refund request
- Approval workflow
- Processing with bank reference

### 6. Write-offs (`GET /advanced-finance/write-offs`)
- Create write-off (requires board resolution)
- Approval and posting

### 7. Bank Accounts (`GET /advanced-finance/bank-accounts`)
- CRUD for bank_accounts

### 8. Bank Deposits (`GET /advanced-finance/deposits`)
- Create deposit slip (group cash + checks)
- Mark as deposited/confirmed
- Bank reference recording

### 9. General Ledger (`GET /advanced-finance/gl`)
- Chart of Accounts (gl_accounts tree view)
- Journal Entries CRUD
- Post/reverse entries
- Account balance view per period

### 10. Budgets (`GET /advanced-finance/budgets`)
- Budget heads per fiscal year per account
- Quarterly breakdown
- Actual vs budget comparison
- Variance reporting

## BUSINESS LOGIC
- Installment payment updates schedule and plan (DB trigger handles this)
- Check status changes tracked in history
- Credit note application reduces invoice balance
- Write-off posts journal entry
- GL entries must balance (total debit = total credit)
- Budget variance calculated on-the-fly

## DASHBOARD WIDGETS
1. **Overdue Installments** â€” Count
2. **Pending Checks** â€” Deposited but not cleared
3. **GL Balance** â€” Cash account balance

## PERMISSIONS
installments.view, installments.create,
refunds.create, refunds.approve,
gl.view, gl.manage, budget.view, budget.manage
```

## File: prompts\PHASE_11_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 11: COMMUNICATIONS & DOCUMENTS MODULE
# Module directory: /modules/communications/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
notification_channels, notification_templates, notification_triggers,
notification_queue, notifications_sent, notification_preferences,
scheduled_notifications, bulk_communications, bulk_communication_recipients,
email_log, sms_log, announcements, announcement_reads,
document_templates, generated_documents

## PAGES TO BUILD

### 1. Notification Templates (`GET /communications/templates`)
- CRUD for notification_templates
- Channel selection, trigger mapping
- Template body with variable placeholders (shown with helper)
- Preview functionality
- Bilingual (EN + AR)

### 2. Notification Channels (`GET /communications/channels`)
- CRUD for channels with configuration

### 3. Notification Triggers (`GET /communications/triggers`)
- Map events to templates
- Condition configuration

### 4. Notification Queue (`GET /communications/queue`)
- DataTable of queued/sent/failed notifications
- Retry failed
- Cancel queued

### 5. Bulk Communications (`GET /communications/bulk`)
- Campaign creation: name, channel, template, target criteria
- Target criteria builder (by membership type, status, age, etc.)
- Preview recipients
- Schedule or send immediately
- Track delivery stats

### 6. Sent Notifications (`GET /communications/sent`)
- DataTable of all sent notifications
- Delivery status tracking

### 7. SMS Log & Email Log
- Dedicated views for SMS and email logs

### 8. Notification Preferences (member-facing, but admin can view)
- Per-member channel/trigger opt-in/opt-out

### 9. Announcements (`GET /communications/announcements`)
- CRUD: Title, body (EN+AR), category, priority, target audience
- Publish/unpublish toggle
- Pin toggle
- Expiry date
- Views/reads tracking

### 10. Document Templates (`GET /communications/document-templates`)
- CRUD for document_templates
- Template body editor (HTML with variable placeholders)
- Variables list per template
- Paper size, orientation settings

### 11. Generate Document (`POST /communications/documents/generate`)
- Select template, select member/reference
- Preview generated document
- Download PDF
- Track in generated_documents

## DASHBOARD WIDGETS
1. **Notifications Sent Today** â€” Count by channel
2. **Failed Notifications** â€” Recent failures

## PERMISSIONS
notifications.view, notifications.send, notifications.bulk,
documents.view, documents.manage
```

## File: prompts\PHASE_12_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 12: REPORTS & DASHBOARDS MODULE
# Module directory: /modules/reports/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
report_definitions, report_parameters, report_templates, report_favorites,
report_generated, report_schedules, report_schedule_runs,
dashboard_widgets, dashboard_user_layouts

## DATABASE VIEWS (use these heavily)
vw_active_members, vw_pending_applications, vw_overdue_invoices,
vw_member_balance_summary, vw_member_family_overview, vw_subscription_renewal_status,
vw_active_freezes, vw_violation_summary, vw_complaint_dashboard,
vw_installment_overview, vw_daily_revenue, vw_expiring_cards,
vw_expiring_documents, vw_sport_class_summary, vw_today_access,
vw_waiting_list_status, vw_kpi_summary

## PAGES TO BUILD

### 1. Report Browser (`GET /reports`)
- Card grid of available reports grouped by category
- Search and filter by category
- Quick access to favorites

### 2. Report Viewer (`GET /reports/{id}`)
- Dynamic parameter form from report_parameters
- Execute report and display results in DataTable
- Chart visualization (if chart_type defined)
- Export buttons: PDF, Excel, CSV, Print
- Save as favorite with current parameters

### 3. Built-in Reports (create report_definitions for each):
  - **Members Report**: Member listing with flexible filters (type, status, join date, city, nationality, age range)
  - **Financial Summary**: Revenue by period, by fee type, by payment method
  - **Outstanding Balances**: Members with outstanding > 0, aging analysis
  - **Subscription Status**: Active/expired/expiring breakdown
  - **Violation Report**: Violations by type, severity, period
  - **Complaint Analysis**: Complaints by category, SLA compliance rate
  - **Attendance Report**: Facility usage, sport class attendance
  - **Guest Pass Report**: Guest passes issued by period
  - **Collection Report**: Payments collected by cashier, by method, by period
  - **Membership Type Distribution**: Members per type with trends
  - **Dependent Report**: Dependents by type, age analysis
  - **Expiring Documents/Cards**: Items expiring in configurable window
  - **Freeze Report**: Freezes by reason, by period
  - **Sport Enrollment**: Class enrollments, fill rates
  - **Board Resolutions**: Resolutions by status, implementation rate

### 4. Report Scheduler (`GET /reports/schedules`)
- CRUD for report_schedules
- Frequency configuration
- Recipient management
- Run history

### 5. Custom Report Builder (`GET /reports/custom`)
- Table/view selector
- Column selector
- Filter builder (AND/OR conditions)
- Grouping selector
- Sorting configuration
- Preview and save as report definition

### 6. Dashboard Configuration (`GET /reports/dashboard-config`)
- Manage personal dashboard layout
- Widget selection and arrangement (drag-drop or grid config)
- Widget catalog from all modules

### 7. KPI Dashboard (`GET /reports/kpis`)
- Use vw_kpi_summary
- Large number cards with sparkline trends
- Comparison to previous period

## BUSINESS LOGIC
- Report queries generated dynamically from report_definitions and parameters
- Results cached temporarily for export after initial query
- Report generation logged in report_generated
- Scheduled reports run at configured times
- Custom reports validated for SQL injection prevention (whitelist approach)

## DASHBOARD WIDGETS
1. **KPI Summary** â€” Key numbers from vw_kpi_summary
2. **Revenue Chart** â€” 30-day trend line
3. **Member Growth** â€” Monthly new members chart

## PERMISSIONS
reports.view, reports.schedule, reports.custom
```

## File: prompts\PHASE_13_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 13: SYSTEM ADMINISTRATION MODULE
# Module directory: /modules/admin/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
auth_users (CRUD), auth_roles (CRUD), auth_permissions (read),
auth_role_permissions (manage), auth_user_roles (manage),
departments, staff, staff_positions, staff_shifts, staff_tasks,
staff_performance_reviews, system_jobs, system_job_runs,
data_quality_rules, data_quality_issues, data_quality_scan_runs,
import_batches, import_batch_rows, export_log,
file_attachments, recycle_bin, data_change_log, api_request_log,
sys_form_number_pools, sys_form_number_issued, sys_translations,
sys_lookup_categories, sys_lookup_values, sys_config (CRUD)

## PAGES TO BUILD

### 1. User Management (`GET /admin/users`)
- DataTable: Username, Email, Full Name, Roles, Active, Last Login
- Create/Edit user (full_name, email, username, password, roles, active toggle)
- Lock/unlock user
- Force password change
- View user sessions

### 2. Role Management (`GET /admin/roles`)
- DataTable of roles
- Create/Edit role with permission assignment
- Permission matrix: modules as rows, CRUD+approve as columns
- Checkbox grid interface

### 3. System Settings (`GET /admin/settings`)
- Grouped settings from sys_config
- Tabs by config_group: Club, Financial, Security, Email, SMS, Membership, System, Booking, Guest, etc.
- Inline edit per setting
- Change log (config_change_log)

### 4. Departments (`GET /admin/departments`)
- CRUD with tree structure (parent_id)
- Manager assignment

### 5. Staff Management (`GET /admin/staff`)
- DataTable and CRUD for staff
- Link to user account
- Position assignment
- Shift management
- Task assignment and tracking
- Performance reviews

### 6. System Jobs (`GET /admin/jobs`)
- DataTable of system_jobs with status
- Enable/disable toggle
- Manual run trigger
- Run history (system_job_runs)
- Last run status, next run time

### 7. Audit Log Viewer (`GET /admin/audit-log`)
- DataTable of audit_log
- Filters: User, Action, Module, Entity, Date Range, Severity
- Detail view showing old/new values

### 8. Login Log (`GET /admin/login-log`)
- DataTable of login_log
- Filter by user, action, IP

### 9. Data Quality (`GET /admin/data-quality`)
- Rules management (data_quality_rules CRUD)
- Scan runs history
- Issues viewer with filters
- Fix/Acknowledge/Ignore actions
- Trigger scan button

### 10. Import Tool (`GET /admin/import`)
- Upload CSV/Excel file
- Select import type (members, subscriptions, payments)
- Column mapping interface
- Validation preview (show errors before import)
- Execute import
- Import history with rollback option

### 11. Export Log (`GET /admin/export-log`)
- History of all exports by all users

### 12. Recycle Bin (`GET /admin/recycle-bin`)
- DataTable of soft-deleted entities
- Restore functionality
- Permanent delete (with confirmation)
- Auto-purge policy

### 13. Lookup Values (`GET /admin/lookups`)
- Manage sys_lookup_categories and sys_lookup_values
- Dynamic CRUD per category

### 14. Translations (`GET /admin/translations`)
- Manage sys_translations
- Filter by language and group
- Inline edit

### 15. Form Number Pools (`GET /admin/form-numbers`)
- Manage form number pools
- Issue form numbers
- Track usage

### 16. Sequence Management (`GET /admin/sequences`)
- View/edit sys_sequences
- Reset sequences (with confirmation)

### 17. System Health
- PHP info, DB version, storage usage
- Active sessions count
- Queue status
- Error log viewer (recent from system_error_log)

## BUSINESS LOGIC
- User password hashed with password_hash(PASSWORD_BCRYPT)
- System role (is_system=1) cannot be deleted
- Super admin role always has all permissions
- Import validates each row before insert, creates batch record
- Recycle bin auto-purges after restore_deadline
- Data quality scan checks all active rules

## DASHBOARD WIDGETS
1. **System Health** â€” Active users, sessions, errors today
2. **Data Quality** â€” Open issues count by severity

## PERMISSIONS
users.view, users.manage, roles.manage,
settings.view, settings.manage,
audit.view, staff.view, staff.manage,
import.execute, export.execute
```

## File: prompts\PHASE_2_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 2: APPLICATIONS & WAITING LISTS MODULE
# Module directory: /modules/applications/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
applications, application_periods, application_documents, application_fees,
application_interviews, application_reviews, application_sponsors,
application_status_history, application_board_votes,
waiting_lists, waiting_list_entries, waiting_list_entry_status_history

## REFERENCE TABLES (read only)
members, membership_types, membership_categories, sys_nationalities,
sys_countries, sys_cities, auth_users, fee_types

## PAGES TO BUILD

### 1. Application Listing (`GET /applications`)
- DataTable: App#, Applicant Name, Phone, Type, Status, Priority, Submission Date, Assigned To, Actions
- Filters: Status, Membership Type, Priority, Date Range, Source
- Status summary cards above: Submitted, Under Review, Board Pending, Approved, Rejected

### 2. Create Application (`GET /applications/create`, `POST /applications`)
- Multi-step form or tabbed form:
  - Step 1: Personal Info (mirrors member fields)
  - Step 2: Contact & Address
  - Step 3: Employment & Education
  - Step 4: Membership Type Selection (shows fees, benefits, requirements)
  - Step 5: Sponsors (Select2 member lookup, minimum count per type)
  - Step 6: Document Upload (dynamic based on selected membership type)
  - Step 7: Declaration checkbox & submit
- Auto-generate application_number via SequenceGenerator
- Calculate total_fees_calculated based on membership_type_fees
- Create application_fees records
- Source: walk_in, online, referral, etc.

### 3. View Application (`GET /applications/{id}`)
- Full application details with status timeline
- Tabs: Details, Documents, Sponsors, Reviews, Interview, Board Vote, Fees
- Action buttons based on current status:
  - Submit (from draft)
  - Assign To (staff dropdown)
  - Start Review
  - Request Documents
  - Schedule Interview
  - Send to Board
  - Approve / Reject
  - Create Member (from approved)
- Each action shows a modal with reason/notes field

### 4. Application Review (`POST /api/applications/{id}/review`)
- Record in application_reviews
- Move status forward/back

### 5. Application Interview (`POST /api/applications/{id}/interview`)
- Schedule, record results, recommendation

### 6. Board Voting (`GET /applications/{id}/board-vote`)
- Show all board members
- Record votes (approve/reject/abstain)
- Calculate result

### 7. Create Member from Application (`POST /applications/{id}/create-member`)
- Call `sp_create_member_from_application`
- Redirect to new member profile

### 8. Application Periods Management
- CRUD for application_periods
- Open/close periods
- Track max_applications

### 9. Waiting List (`GET /waiting-list`)
- DataTable per membership type
- Columns: Queue#, Name, Phone, Registration Date, Priority, Status
- Actions: Offer, Promote, Remove, Move Up/Down
- Create entry form
- Auto-expire offers past deadline

### 10. Waiting List Configuration
- Per membership type: max entries, offer validity days, auto-promote toggle

## BUSINESS LOGIC
- Status machine: draft â†’ submitted â†’ under_review â†’ documents_pending â†’ interview_scheduled â†’ interview_completed â†’ board_pending â†’ approved/rejected/waitlisted
- Cannot skip statuses
- Approved applications can create members (one-time operation)
- Application periods limit when applications can be submitted
- Waiting list queue management with priority ordering
- Sponsor endorsement validation

## DASHBOARD WIDGETS
1. **Pending Applications** â€” Count by status
2. **Waiting List Overview** â€” Entries per membership type

## PERMISSIONS
applications.view, applications.create, applications.review, applications.approve
waitlist.view, waitlist.manage
```

## File: prompts\PHASE_3_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 3: FINANCE CORE MODULE
# Module directory: /modules/finance/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
invoices, invoice_items, invoice_discounts, payments, payment_allocations,
payment_methods, receipts, receipt_items, fee_types, discount_types,
tax_rates, cash_registers, cash_register_sessions, cash_register_transactions,
daily_cash_reconciliation, fiscal_years, fiscal_periods,
member_balances (write via sp_recalculate_member_balance), member_ledger

## PAGES TO BUILD

### 1. Invoice Listing (`GET /finance/invoices`)
- DataTable: Invoice#, Member, Type, Date, Due Date, Total, Paid, Balance, Status, Actions
- Filters: Status, Type, Date Range, Overdue toggle
- Summary cards: Total Outstanding, Total Overdue, Issued Today, Collected Today

### 2. Create Invoice (`GET /finance/invoices/create`, `POST /finance/invoices`)
- Member lookup (Select2 AJAX)
- Invoice type selection
- Dynamic line items: Fee Type (Select2), Description, Qty, Unit Price, Discount, Tax Rate â†’ auto-calc totals
- Add/remove line items dynamically
- Apply discount (discount_types selection)
- Due date (Flatpickr)
- Notes
- Save as draft or issue immediately
- Call `sp_create_invoice` then `sp_add_invoice_item` for each item
- Then optionally `sp_issue_invoice`

### 3. View Invoice (`GET /finance/invoices/{id}`)
- Professional invoice layout (printable)
- Club header, member details, line items table, totals
- Payment history for this invoice
- Action buttons: Issue (if draft), Record Payment, Print, Void, Cancel
- PDF download

### 4. Record Payment (`GET /finance/payments/create`, `POST /finance/payments`)
- Member lookup
- Amount, Payment Method, Date, Reference #
- Allocate to specific invoice(s) or auto-allocate
- Call `sp_record_payment`
- Auto-generate receipt via `sp_generate_receipt`
- If cash register session active, record transaction

### 5. Payment Listing (`GET /finance/payments`)
- DataTable: Payment#, Member, Amount, Method, Date, Status, Actions
- Filters: Method, Status, Date Range

### 6. Receipts (`GET /finance/receipts`)
- DataTable: Receipt#, Member, Amount, Date, Status
- View receipt (printable format)
- Reprint option

### 7. Cash Register Management
- Open Session (`POST /finance/cash-register/open`): Select register, enter opening balance
- Active Session Dashboard: Show current session stats (cash in, cash out, checks, cards, transaction count)
- Close Session (`POST /finance/cash-register/close`): Enter closing balance, system calculates expected, shows variance
- Session History: List past sessions with reconciliation status

### 8. Fee Types Management (`GET /finance/fee-types`)
- CRUD for fee_types table

### 9. Payment Methods Management
- CRUD for payment_methods

### 10. Fiscal Years & Periods
- CRUD for fiscal_years and fiscal_periods
- Open/Close periods

### 11. Member Financial Statement (`GET /finance/members/{id}/statement`)
- Full member_ledger for a member
- Date range filter
- Running balance display
- Print/Export options

### 12. Daily Revenue Report (`GET /finance/daily-revenue`)
- Use vw_daily_revenue view
- Date picker, grouped by payment method
- Chart (bar chart by method)

### 13. Overdue Invoices Report
- Use vw_overdue_invoices view
- DataTable with aging buckets (30/60/90/120+ days)

## BUSINESS LOGIC
- Invoice total auto-calculated from items
- balance_due = total_amount - paid_amount (DB trigger handles this)
- Payment allocation updates invoice paid_amount (DB trigger)
- Issuing invoice updates member outstanding_balance
- Cash register session must be open to accept cash/check payments
- Receipts auto-generated on payment
- Fiscal period must be open to record transactions

## DASHBOARD WIDGETS
1. **Revenue Today** â€” Total collected today by method
2. **Outstanding Balance** â€” Total system-wide outstanding
3. **Overdue Invoices** â€” Count and total amount

## PERMISSIONS
invoices.view, invoices.create, invoices.void,
payments.view, payments.create, payments.reverse,
receipts.view, receipts.print,
cash_register.open, cash_register.close
```

## File: prompts\PHASE_4_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 4: SUBSCRIPTIONS & FREEZES MODULE
# Module directory: /modules/subscriptions/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
subscription_periods, member_subscriptions, subscription_renewal_log,
freeze_policies, member_freezes, freeze_extensions

## REFERENCE TABLES (read only)
members, membership_types, invoices

## PAGES TO BUILD

### 1. Subscription Periods (`GET /subscriptions/periods`)
- CRUD for subscription periods
- Open/close periods

### 2. Member Subscriptions Listing (`GET /subscriptions`)
- DataTable: Member#, Name, Type, Period, Start, End, Amount, Paid, Status
- Filters: Status, Membership Type, Period, Expiring Soon toggle
- Use vw_subscription_renewal_status view

### 3. Create/Renew Subscription (`POST /subscriptions`)
- Member lookup, period selection, auto-calculate amount from membership_type_fees
- Handle renewal (link renewed_from_id)
- Late fee calculation if past grace period
- Dependent count and fees

### 4. Bulk Renewal (`GET /subscriptions/bulk-renew`)
- List eligible members for renewal
- Select all / individual selection
- Bulk create subscriptions and invoices

### 5. Expiring Subscriptions Report
- Members whose subscription ends within N days
- Notify button (queue notifications)

### 6. Freeze Management (`GET /freezes`)
- DataTable: Freeze#, Member, Reason, Start, End, Duration, Status, Fee
- Filters: Status, Date Range
- Use vw_active_freezes view

### 7. Create Freeze Request (`POST /freezes/create`)
- Member lookup, reason, category, dates
- Validate against freeze_policies (max days, max times, etc.)
- Call `sp_create_freeze`

### 8. Approve/Reject Freeze (`POST /api/freezes/{id}/approve`)
- Approval workflow
- On approve: activate freeze, change member status to 'frozen'

### 9. End Freeze Early (`POST /api/freezes/{id}/end`)
- End before scheduled date with reason

### 10. Freeze Extensions (`POST /freezes/{id}/extend`)
- Request extension with additional days
- Approval workflow

### 11. Freeze Policies (`GET /freezes/policies`)
- CRUD for freeze_policies
- Per membership type configuration

## BUSINESS LOGIC
- Subscription creates invoice for payment
- Freeze validated against policy limits
- Active freeze changes member status to 'frozen'
- Ended freeze restores member to 'active'
- Subscription extended by freeze duration if policy allows
- Late renewals trigger late_fee based on membership_type settings

## DASHBOARD WIDGETS
1. **Expiring Subscriptions** â€” Count expiring in 30 days
2. **Active Freezes** â€” Count and list

## PERMISSIONS
freezes.view, freezes.create, freezes.approve
```

## File: prompts\PHASE_5_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 5: CARDS, ACCESS CONTROL & GUESTS MODULE
# Module directory: /modules/cards/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
card_types, card_designs, member_cards, card_requests, card_print_batches,
card_print_batch_items, access_points, access_control_rules, member_access_log,
guest_pass_types, guest_passes, guest_visits, guest_visit_facilities,
guest_policy_rules, guest_blacklist, carnet_types, carnets, carnet_entries

## PAGES TO BUILD

### 1. Cards Listing (`GET /cards`)
- DataTable: Card#, Member, Type, Issue Date, Expiry, Status, Actions
- Expiring cards highlighted

### 2. Card Requests (`GET /cards/requests`)
- DataTable of card_requests
- Create new request (new/replacement/renewal)
- Approve/reject workflow
- Link to member

### 3. Card Production & Printing
- Print batch management
- Batch creation from approved requests
- Print batch items tracking

### 4. Card Types Management
- CRUD for card_types

### 5. Access Points (`GET /access/points`)
- CRUD for access_points
- Configure hardware type, direction

### 6. Access Control Rules (`GET /access/rules`)
- Per access point rules
- Membership type restrictions, time restrictions, day restrictions

### 7. Access Log (`GET /access/log`)
- DataTable: Time, Direction, Member, Access Point, Method, Granted, Reason
- Use vw_today_access view
- Date range filter
- Real-time view (auto-refresh)

### 8. Guest Pass Management (`GET /guests`)
- DataTable: Pass#, Host Member, Guest Name, Date, Type, Status
- Issue guest pass form: Host member lookup, guest details, pass type, date, fee
- Check-in / check-out tracking
- Validate against guest_policy_rules (max visits, same guest limits, etc.)

### 9. Guest Blacklist (`GET /guests/blacklist`)
- CRUD for guest_blacklist
- Check during pass issuance

### 10. Carnet Management (`GET /carnets`)
- Carnet types CRUD
- Issue carnet to member
- Track entries (carnet_entries)
- Entry recording interface

## BUSINESS LOGIC
- Card request fee creates invoice
- Card number generated via SequenceGenerator
- Guest pass number generated via SequenceGenerator
- Guest pass issuance checks blacklist
- Guest policy rules enforce limits
- Access log records every entry/exit
- Carnet entry updates remaining count (DB trigger handles decrement)

## DASHBOARD WIDGETS
1. **Expiring Cards** â€” Cards expiring in 30 days
2. **Today's Access** â€” Entry count today
3. **Active Guest Passes** â€” Today's active guest passes

## PERMISSIONS
cards.view, cards.manage, cards.print,
guests.view, guests.create,
access.view, access.manage
```

## File: prompts\PHASE_6_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 6: VIOLATIONS, PENALTIES & COMPLAINTS MODULE
# Module directory: /modules/violations/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
violation_categories, violation_types, violations, violation_evidence,
violation_witnesses, violation_investigations, violation_investigation_notes,
violation_escalation_rules, penalties, penalty_appeals, penalty_appeal_decisions,
penalty_payments, complaint_categories, complaint_priorities, complaints,
complaint_followups, complaint_attachments, complaint_resolutions

## PAGES TO BUILD

### 1. Violations Listing (`GET /violations`)
- DataTable with vw_violation_summary data
- Filters: Status, Category, Severity, Date Range

### 2. Report Violation (`GET /violations/create`, `POST /violations`)
- Member lookup, violation type (cascading: category â†’ type), incident date/time/location
- Description, evidence upload (multi-file), witness information
- Auto-calculate occurrence_number (DB trigger does this)

### 3. View Violation (`GET /violations/{id}`)
- Full details with status timeline
- Evidence gallery, witness list
- Investigation section: assign investigator, record findings
- Decision section: penalty assignment
- Appeal section
- Related violations (if repeat offense)

### 4. Investigation Management
- Open investigation, record notes
- Member notification & response tracking
- Final recommendation

### 5. Penalties (`GET /penalties`)
- DataTable: Penalty#, Member, Type, Fine, Suspension Days, Status
- Create penalty from violation decision
- Fine tracking (paid/unpaid)
- Suspension management

### 6. Penalty Appeals
- Submit appeal form
- Hearing scheduling
- Decision recording
- Override/modify original penalty

### 7. Violation Types Management
- CRUD for violation_categories, violation_types
- Escalation rules per type

### 8. Complaints Listing (`GET /complaints`)
- DataTable with vw_complaint_dashboard data
- SLA status indicators (on track / at risk / breached)
- Filters: Status, Category, Priority, SLA Status

### 9. Submit Complaint (`POST /complaints`)
- Member lookup (or walk-in), category, priority, subject, description
- Attachments, channel selection
- SLA deadline auto-calculated (DB trigger does this)

### 10. Manage Complaint (`GET /complaints/{id}`)
- Full details with follow-up timeline
- Assign to staff/department
- Add follow-ups (notes, actions, status changes)
- Escalation management
- Resolution recording
- Satisfaction rating (after resolution)

### 11. Complaint Categories & Priorities
- CRUD management

## BUSINESS LOGIC
- Violation occurrence tracking for repeat offenders
- Escalation rules trigger harsher penalties for repeat offenses
- Penalty of type suspension auto-changes member status (DB trigger)
- Penalty of type termination auto-terminates member (DB trigger)
- Complaint SLA monitoring
- SLA breach auto-flagging (DB trigger on update)

## DASHBOARD WIDGETS
1. **Open Violations** â€” Count by severity
2. **SLA Status** â€” Complaints at risk/breached
3. **Pending Appeals** â€” Count

## PERMISSIONS
violations.view, violations.create, violations.decide,
penalties.manage, complaints.view, complaints.manage
```

## File: prompts\PHASE_7_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 7: SPORTS & ACTIVITIES MODULE
# Module directory: /modules/sports/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
sport_categories, sports, sport_seasons, sport_coaches, sport_coach_certifications,
sport_classes, sport_class_schedules, sport_class_enrollments, sport_class_attendance,
sport_class_waitlist, sport_teams, sport_team_members, sport_leagues, sport_league_teams,
sport_matches, sport_match_results, sport_tournaments, sport_tournament_registrations,
sport_equipment, sport_equipment_rentals, sport_memberships, sport_member_achievements,
sport_rules, sport_locker_rooms, sport_lockers, sport_locker_assignments

## PAGES TO BUILD

### 1. Sports Catalog (`GET /sports`)
- Grid/card view of all sports with icons and categories
- CRUD for sports and sport_categories

### 2. Seasons (`GET /sports/seasons`)
- CRUD for sport_seasons per sport
- Status management (upcoming â†’ registration â†’ active â†’ completed)

### 3. Coaches (`GET /sports/coaches`)
- DataTable and CRUD for sport_coaches
- Certification management (sport_coach_certifications)
- Coach profile page with schedule, classes, certifications

### 4. Classes (`GET /sports/classes`)
- DataTable with vw_sport_class_summary
- Create/Edit class with schedules (sport_class_schedules)
- Enrollment management:
  - Enroll member/dependent
  - Waitlist when full
  - Withdraw
- Attendance tracking:
  - Date-based attendance sheet
  - Mark present/absent/late/excused
  - Attendance reports per class

### 5. Teams (`GET /sports/teams`)
- CRUD for sport_teams
- Team member management (add/remove players)
- Captain/vice-captain assignment
- Team roster view

### 6. Leagues (`GET /sports/leagues`)
- CRUD for sport_leagues
- Team registration
- Standings table (sport_league_teams)
- Match scheduling

### 7. Matches (`GET /sports/matches`)
- DataTable of all matches
- Create match (select teams, facility, date/time)
- Record results (scores)
- Match events (goals, cards, etc.) via sport_match_results

### 8. Tournaments (`GET /sports/tournaments`)
- CRUD for sport_tournaments
- Registration management
- Bracket generation (store in brackets_data JSON)
- Winner recording

### 9. Equipment (`GET /sports/equipment`)
- Inventory DataTable
- CRUD for sport_equipment
- Rental management:
  - Issue to member
  - Return processing
  - Damage/loss handling
  - Deposit tracking

### 10. Lockers (`GET /sports/lockers`)
- Locker rooms and lockers CRUD
- Assignment to members
- Availability view (visual grid)

### 11. Sport Memberships (`GET /sports/memberships`)
- Enroll members in specific sports
- Track per-sport membership levels

### 12. Achievements (`GET /sports/achievements`)
- Record and display member achievements per sport

## BUSINESS LOGIC
- Class enrollment validates age/gender/membership restrictions
- Full classes auto-set to 'full' status (DB trigger)
- Team member count tracked (DB trigger)
- Match results auto-update league standings (DB trigger)
- Equipment rental updates available quantity (DB trigger)

## DASHBOARD WIDGETS
1. **Active Classes** â€” Count with enrollment stats
2. **Upcoming Matches** â€” Next 5 matches
3. **Equipment Alerts** â€” Low stock or overdue rentals

## PERMISSIONS
sports.view, sports.manage, classes.view, classes.manage, classes.attendance,
teams.view, teams.manage, tournaments.view, tournaments.manage,
equipment.view, equipment.manage
```

## File: prompts\PHASE_8_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 8: FACILITIES, BOOKINGS & EVENTS MODULE
# Module directory: /modules/facilities/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
facility_types, facilities, facility_time_slots, facility_slot_overrides,
facility_access_schedules, facility_amenities, facility_bookings,
facility_booking_participants, facility_maintenance, club_events,
club_event_registrations, swimming_pools, swimming_pool_readings,
parking_zones, parking_permits, parking_log

## PAGES TO BUILD

### 1. Facilities Listing (`GET /facilities`)
- Card grid or DataTable of all facilities
- Status indicators (active, maintenance, closed)
- Quick stats: total, active, under maintenance

### 2. Facility CRUD (`GET /facilities/create`, etc.)
- Full form with all facility fields
- Amenities management
- Time slots configuration (weekly schedule)
- Slot overrides (holidays, special events)
- Access schedule per membership type
- Photo upload

### 3. Facility Types Management
- CRUD for facility_types

### 4. Booking Calendar (`GET /facilities/{id}/bookings`)
- Weekly calendar view showing time slots and bookings
- Color-coded by booking type/status
- Click-to-book on empty slots
- Booking detail modal on click

### 5. Create Booking (`POST /facilities/bookings`)
- Member lookup, facility, date, time slot selection
- Participant management
- Check availability (call `sp_check_booking_availability`)
- Fee calculation
- Validation: advance booking days, membership type access

### 6. Booking Management (`GET /bookings`)
- DataTable of all bookings
- Filters: Facility, Status, Date Range
- Actions: Confirm, Check-in, Complete, Cancel
- Cancellation with fee calculation

### 7. Facility Maintenance (`GET /facilities/{id}/maintenance`)
- CRUD for facility_maintenance records
- Schedule upcoming maintenance
- Track costs

### 8. Events (`GET /events`)
- DataTable: Code, Title, Type, Date, Venue, Attendees, Status
- Create/Edit event with full details
- Registration management:
  - Member registration
  - Guest allowance
  - Fee collection
  - Attendee check-in
- Event types: social, sport, cultural, etc.

### 9. Swimming Pools (`GET /facilities/pools`)
- Pool configuration (swimming_pools)
- Water quality readings entry (swimming_pool_readings)
- Compliance tracking
- Reading history chart (pH, chlorine, temperature over time)

### 10. Parking (`GET /facilities/parking`)
- Zones management (parking_zones)
- Permit issuance (parking_permits)
- Parking log view
- Permit status management

## BUSINESS LOGIC
- Booking conflict detection via sp_check_booking_availability
- Slot overrides block bookings on closed days
- Maintenance records set facility status
- Event registration respects max_attendees
- Pool readings flag non-compliance

## DASHBOARD WIDGETS
1. **Today's Bookings** â€” Count by facility
2. **Upcoming Events** â€” Next 3 events
3. **Facility Status** â€” Active/Maintenance/Closed counts

## PERMISSIONS
facilities.view, facilities.manage,
bookings.view, bookings.create, bookings.manage,
events.view, events.manage
```

## File: prompts\PHASE_9_PROMPT.md

```md
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# PHASE 9: BOARD, GOVERNANCE & TRANSFERS MODULE
# Module directory: /modules/board/
# â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
# READ THE MASTER PROMPT FIRST.

## YOUR TABLES
board_terms, board_positions, board_members, board_committees,
board_committee_members, board_meetings, board_meeting_agenda,
board_meeting_attendees, board_resolutions, board_resolution_votes,
transfer_types, membership_transfers, transfer_documents,
transfer_heirs, transfer_status_history,
special_offers, special_offer_conditions, special_offer_membership_types,
member_applied_offers

## PAGES TO BUILD

### 1. Board Terms (`GET /board/terms`)
- CRUD, status management

### 2. Board Members (`GET /board/members`)
- DataTable: Name, Position, Term, Status
- Add/remove board members
- Position assignment
- Resignation recording

### 3. Committees (`GET /board/committees`)
- CRUD with member assignment

### 4. Meetings (`GET /board/meetings`)
- DataTable: Meeting#, Type, Date, Status, Quorum
- Create meeting with agenda items
- Attendance recording (present/absent/excused/proxy)
- Quorum calculation
- Minutes recording (text + file upload)
- Minutes approval workflow

### 5. Resolutions (`GET /board/resolutions`)
- DataTable: Resolution#, Date, Title, Status, Votes
- Create from meeting or standalone
- Voting interface (for/against/abstain per board member)
- Implementation tracking
- Supersession chain

### 6. Membership Transfers (`GET /transfers`)
- DataTable: Transfer#, From, To, Type, Status
- Create transfer request (full form with all transfer_types fields)
- Document upload (death certificate, court order, divorce docs, etc.)
- Heir management (for inheritance transfers)
- Review/approve workflow
- Board approval when required
- Complete transfer (creates new member, terminates old)

### 7. Transfer Types Management
- CRUD for transfer_types

### 8. Special Offers (`GET /offers`)
- DataTable: Code, Name, Type, Dates, Uses, Status
- Create/edit offers with conditions
- Membership type targeting
- Apply offer to member (with invoice discount)
- Usage tracking

## BUSINESS LOGIC
- Board resolution voting tallies
- Quorum validation for meetings
- Transfer status machine: requested â†’ documents_pending â†’ under_review â†’ board_pending â†’ approved â†’ completed
- Transfer completion calls appropriate stored procedures
- Special offer conditions evaluated on application

## DASHBOARD WIDGETS
1. **Upcoming Meetings** â€” Next scheduled meeting
2. **Pending Transfers** â€” Count
3. **Active Offers** â€” Count with usage stats

## PERMISSIONS
board.view, board.manage, board.resolutions,
transfers.view, transfers.manage, transfers.approve,
offers.view, offers.manage
```

## File: public\.htaccess

```htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?_url=$1 [QSA,L]
```

## File: public\assets\css\app.css

```css
/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   Club Management System â€” Core Stylesheet
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

:root {
    --sidebar-width: 260px;
    --sidebar-collapsed-width: 0px;
    --sidebar-bg: #1a2332;
    --sidebar-hover: #243347;
    --sidebar-active: #2c5282;
    --sidebar-text: #a0aec0;
    --sidebar-text-active: #ffffff;
    --header-height: 56px;
    --footer-height: 40px;
    --primary: #2c5282;
    --primary-light: #3182ce;
}

* { box-sizing: border-box; }

body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; background: #f4f6f9; min-height: 100vh; }

/* Auth Layout */
.auth-body { background: linear-gradient(135deg, #1a2332 0%, #2c5282 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; }
.auth-wrapper { width: 100%; max-width: 440px; padding: 20px; }
.auth-card { border: none; border-radius: 12px; }

/* Sidebar */
.cms-sidebar { position: fixed; top: 0; left: 0; width: var(--sidebar-width); height: 100vh; background: var(--sidebar-bg); color: var(--sidebar-text); z-index: 1050; display: flex; flex-direction: column; transition: transform 0.3s ease; overflow: hidden; }
.cms-sidebar.collapsed { transform: translateX(calc(-1 * var(--sidebar-width))); }

.sidebar-header { padding: 16px 20px; border-bottom: 1px solid rgba(255,255,255,0.08); display: flex; align-items: center; justify-content: space-between; min-height: 60px; }
.sidebar-brand { color: #fff; text-decoration: none; font-size: 1.1rem; font-weight: 600; display: flex; align-items: center; }
.sidebar-brand:hover { color: #fff; }
.sidebar-close { color: var(--sidebar-text); display: none; }

.sidebar-body { flex: 1; overflow-y: auto; padding: 10px 0; }
.sidebar-body::-webkit-scrollbar { width: 4px; }
.sidebar-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 4px; }

.sidebar-link { color: var(--sidebar-text) !important; padding: 10px 20px; font-size: 0.875rem; display: flex; align-items: center; transition: all 0.2s; border-radius: 0; text-decoration: none; }
.sidebar-link:hover { background: var(--sidebar-hover); color: var(--sidebar-text-active) !important; }
.sidebar-link.active { background: var(--sidebar-active); color: var(--sidebar-text-active) !important; }
.sidebar-link i:first-child { width: 22px; text-align: center; }

.sidebar-chevron { font-size: 0.7rem; transition: transform 0.2s; }
.sidebar-link.collapsed .sidebar-chevron { transform: rotate(-90deg); }

.sidebar-submenu { padding: 2px 0; }
.sidebar-sublink { color: var(--sidebar-text) !important; padding: 7px 20px; font-size: 0.8rem; }
.sidebar-sublink:hover { color: var(--sidebar-text-active) !important; background: rgba(255,255,255,0.05); }
.sidebar-sublink.active { color: var(--sidebar-text-active) !important; background: rgba(44,82,130,0.3); }
.sidebar-sublink i { font-size: 0.4rem; opacity: 0.5; }

.sidebar-footer { padding: 12px 20px; border-top: 1px solid rgba(255,255,255,0.08); font-size: 0.75rem; text-align: center; }

.sidebar-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1040; }

/* Main Content */
.cms-main { margin-left: var(--sidebar-width); min-height: 100vh; display: flex; flex-direction: column; transition: margin-left 0.3s ease; }
.cms-main.expanded { margin-left: 0; }

.cms-header { background: #fff; border-bottom: 1px solid #e2e8f0; padding: 0 20px; min-height: var(--header-height); display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 1020; box-shadow: 0 1px 3px rgba(0,0,0,0.05); }
.sidebar-toggle { border: none; background: none; padding: 8px; color: #4a5568; }
.sidebar-toggle:hover { color: var(--primary); }

.cms-content { flex: 1; padding: 20px; }
.cms-footer { padding: 10px 20px; text-align: center; font-size: 0.8rem; color: #718096; border-top: 1px solid #e2e8f0; background: #fff; }

/* Cards */
.card { border: 1px solid #e2e8f0; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); margin-bottom: 20px; }
.card-header { background: #f8fafc; border-bottom: 1px solid #e2e8f0; padding: 12px 16px; font-weight: 600; }
.card-body { padding: 16px; }

/* DataTable Adjustments */
.dataTables_wrapper .dataTables_filter input { border-radius: 6px; border: 1px solid #cbd5e0; padding: 6px 12px; }
.dataTables_wrapper .dataTables_length select { border-radius: 6px; border: 1px solid #cbd5e0; padding: 4px 8px; }
table.dataTable thead th { background: #f8fafc; font-weight: 600; font-size: 0.85rem; color: #4a5568; border-bottom: 2px solid #e2e8f0; }
table.dataTable tbody td { font-size: 0.875rem; vertical-align: middle; }

/* Badges */
.badge { font-weight: 500; font-size: 0.75rem; padding: 4px 8px; border-radius: 4px; }

/* Buttons */
.btn-sm { font-size: 0.8rem; padding: 4px 10px; }
.btn-group-actions .btn { border-radius: 4px; margin: 0 1px; }

/* Forms */
.form-label { font-weight: 500; color: #4a5568; font-size: 0.875rem; margin-bottom: 4px; }
.form-control:focus, .form-select:focus { border-color: var(--primary-light); box-shadow: 0 0 0 0.2rem rgba(49,130,206,0.15); }
.invalid-feedback { font-size: 0.8rem; }

/* Select2 */
.select2-container--bootstrap-5 .select2-selection { min-height: 38px; border-color: #dee2e6; }

/* Loading Overlay */
.loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.7); z-index: 9999; display: flex; align-items: center; justify-content: center; }
.loading-overlay .spinner-border { width: 3rem; height: 3rem; }

/* Toast positioning */
.swal2-container.swal2-top-end { top: 70px !important; }

/* Status badges enhancement */
.badge.bg-success { background-color: #38a169 !important; }
.badge.bg-danger { background-color: #e53e3e !important; }
.badge.bg-warning { background-color: #dd6b20 !important; color: #fff !important; }
.badge.bg-info { background-color: #3182ce !important; }

/* Tab styling */
.nav-tabs .nav-link { color: #4a5568; font-weight: 500; }
.nav-tabs .nav-link.active { color: var(--primary); border-color: #dee2e6 #dee2e6 #fff; }

/* Breadcrumb */
.breadcrumb { font-size: 0.85rem; }
.breadcrumb-item a { color: var(--primary); text-decoration: none; }

/* Stat cards */
.card.border-primary { border-left: 4px solid var(--bs-primary) !important; }
.card.border-success { border-left: 4px solid var(--bs-success) !important; }
.card.border-warning { border-left: 4px solid var(--bs-warning) !important; }
.card.border-danger { border-left: 4px solid var(--bs-danger) !important; }
.card.border-info { border-left: 4px solid var(--bs-info) !important; }

/* Print */
@media print {
    .cms-sidebar, .cms-header, .cms-footer, .sidebar-overlay { display: none !important; }
    .cms-main { margin-left: 0 !important; }
    .no-print { display: none !important; }
    .card { box-shadow: none; border: 1px solid #ddd; }
}

/* Responsive */
@media (max-width: 991.98px) {
    .cms-sidebar { transform: translateX(calc(-1 * var(--sidebar-width))); }
    .cms-sidebar.show { transform: translateX(0); }
    .sidebar-close { display: block; }
    .sidebar-overlay.show { display: block; }
    .cms-main { margin-left: 0; }
}

/* Animation */
.fade-in { animation: fadeIn 0.3s ease-in; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

/* Alert auto-dismiss animation */
.alert { animation: fadeIn 0.3s ease-in; }

/* Empty state */
.empty-state { text-align: center; padding: 60px 20px; color: #a0aec0; }
.empty-state i { font-size: 4rem; margin-bottom: 20px; }

/* Thumbnail in tables */
.table-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; }

/* Sticky table header */
.table-sticky thead th { position: sticky; top: 0; z-index: 10; }

/* Custom scrollbar */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #f1f1f1; }
::-webkit-scrollbar-thumb { background: #cbd5e0; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #a0aec0; }
```

## File: public\assets\css\rtl.css

```css
/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   RTL Overrides
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

[dir="rtl"] .cms-sidebar { left: auto; right: 0; }
[dir="rtl"] .cms-sidebar.collapsed { transform: translateX(var(--sidebar-width)); }
[dir="rtl"] .cms-main { margin-left: 0; margin-right: var(--sidebar-width); }
[dir="rtl"] .cms-main.expanded { margin-right: 0; }

[dir="rtl"] .sidebar-link i:first-child { margin-left: 10px; margin-right: 0; }
[dir="rtl"] .sidebar-chevron { margin-left: 0 !important; margin-right: auto; }
[dir="rtl"] .sidebar-submenu { padding-right: 1rem; padding-left: 0; }

[dir="rtl"] .me-1 { margin-right: 0 !important; margin-left: 0.25rem !important; }
[dir="rtl"] .me-2 { margin-right: 0 !important; margin-left: 0.5rem !important; }
[dir="rtl"] .me-3 { margin-right: 0 !important; margin-left: 1rem !important; }
[dir="rtl"] .ms-auto { margin-right: auto !important; margin-left: 0 !important; }
[dir="rtl"] .ms-3 { margin-right: 1rem !important; margin-left: 0 !important; }

[dir="rtl"] .card.border-primary,
[dir="rtl"] .card.border-success,
[dir="rtl"] .card.border-warning,
[dir="rtl"] .card.border-danger,
[dir="rtl"] .card.border-info {
    border-left: 1px solid #e2e8f0 !important;
    border-right-width: 4px !important;
}

[dir="rtl"] .dropdown-menu-end { --bs-position: end; }

[dir="rtl"] .breadcrumb-item + .breadcrumb-item::before { float: right; padding-left: 0.5rem; padding-right: 0; }

@media (max-width: 991.98px) {
    [dir="rtl"] .cms-sidebar { transform: translateX(var(--sidebar-width)); }
    [dir="rtl"] .cms-sidebar.show { transform: translateX(0); }
    [dir="rtl"] .cms-main { margin-right: 0; }
}
```

## File: public\assets\js\app.js

```js
/**
 * Club Management System â€” Core JavaScript
 */
(function() {
    'use strict';

    // CSRF Token for all AJAX requests
    const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
    if (csrfToken) {
        $.ajaxSetup({
            headers: { 'X-CSRF-TOKEN': csrfToken, 'X-Requested-With': 'XMLHttpRequest' }
        });
    }

    // Global AJAX error handler
    $(document).ajaxError(function(event, jqXHR, settings) {
        if (jqXHR.status === 401) {
            Swal.fire({ icon: 'warning', title: 'Session Expired', text: 'Please log in again.', confirmButtonText: 'Login' })
                .then(() => { window.location.href = '/login'; });
        } else if (jqXHR.status === 403) {
            Swal.fire({ icon: 'error', title: 'Access Denied', text: 'You do not have permission for this action.' });
        } else if (jqXHR.status >= 500) {
            Swal.fire({ icon: 'error', title: 'Server Error', text: 'Something went wrong. Please try again later.' });
        }
    });

    // Sidebar Toggle
    const sidebar = document.getElementById('sidebar');
    const mainContent = document.getElementById('mainContent');
    const sidebarToggle = document.getElementById('sidebarToggle');
    const sidebarClose = document.getElementById('sidebarClose');
    const sidebarOverlay = document.getElementById('sidebarOverlay');

    function toggleSidebar() {
        if (window.innerWidth >= 992) {
            sidebar?.classList.toggle('collapsed');
            mainContent?.classList.toggle('expanded');
            localStorage.setItem('sidebar_collapsed', sidebar?.classList.contains('collapsed') ? '1' : '0');
        } else {
            sidebar?.classList.toggle('show');
            sidebarOverlay?.classList.toggle('show');
        }
    }

    sidebarToggle?.addEventListener('click', toggleSidebar);
    sidebarClose?.addEventListener('click', toggleSidebar);
    sidebarOverlay?.addEventListener('click', toggleSidebar);

    // Restore sidebar state
    if (window.innerWidth >= 992 && localStorage.getItem('sidebar_collapsed') === '1') {
        sidebar?.classList.add('collapsed');
        mainContent?.classList.add('expanded');
    }

    // Confirm Delete
    document.addEventListener('click', function(e) {
        const btn = e.target.closest('[data-confirm]');
        if (btn) {
            e.preventDefault();
            const message = btn.getAttribute('data-confirm-message') || 'Are you sure?';
            const form = btn.closest('form');
            Swal.fire({
                title: 'Confirm',
                text: message,
                icon: 'warning',
                showCancelButton: true,
                confirmButtonColor: '#e53e3e',
                confirmButtonText: 'Yes, proceed',
                cancelButtonText: 'Cancel'
            }).then((result) => {
                if (result.isConfirmed && form) {
                    form.submit();
                }
            });
        }
    });

    // Auto-dismiss alerts after 5 seconds
    document.querySelectorAll('.alert-dismissible').forEach(function(alert) {
        setTimeout(function() {
            const bsAlert = bootstrap.Alert.getOrCreateInstance(alert);
            bsAlert?.close();
        }, 5000);
    });

    // Initialize tooltips
    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function(el) {
        new bootstrap.Tooltip(el);
    });

    // Initialize popovers
    document.querySelectorAll('[data-bs-toggle="popover"]').forEach(function(el) {
        new bootstrap.Popover(el);
    });

    // Initialize Select2
    if ($.fn.select2) {
        $('.select2').select2({ theme: 'bootstrap-5', width: '100%' });
    }

    // Initialize Flatpickr
    if (typeof flatpickr !== 'undefined') {
        document.querySelectorAll('.datepicker').forEach(function(el) {
            flatpickr(el, { dateFormat: 'Y-m-d', allowInput: true });
        });
        document.querySelectorAll('.datetimepicker').forEach(function(el) {
            flatpickr(el, { dateFormat: 'Y-m-d H:i', enableTime: true, allowInput: true });
        });
    }

})();
```

## File: public\assets\js\datatable-helper.js

```js
/**
 * DataTable Helper â€” Server-side DataTable initialization
 */
const DataTableHelper = {
    instances: {},

    init(tableId, options = {}) {
        const table = document.getElementById(tableId);
        if (!table) return null;

        const url = table.getAttribute('data-url');
        let columns = [];
        try {
            columns = JSON.parse(table.getAttribute('data-columns') || '[]');
        } catch (e) {
            console.error('Invalid data-columns JSON for', tableId);
            return null;
        }

        const defaultOptions = {
            processing: true,
            serverSide: true,
            ajax: {
                url: url,
                type: 'GET',
                data: function(d) {
                    // Append any filter inputs
                    const filterForm = document.querySelector('[data-filter-table="' + tableId + '"]');
                    if (filterForm) {
                        const formData = new FormData(filterForm);
                        for (const [key, value] of formData.entries()) {
                            if (value) d[key] = value;
                        }
                    }
                    return d;
                },
                error: function(xhr) {
                    console.error('DataTable AJAX error:', xhr);
                }
            },
            columns: columns,
            order: [[0, 'desc']],
            pageLength: 25,
            lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
            language: {
                processing: '<div class="spinner-border spinner-border-sm text-primary" role="status"><span class="visually-hidden">Loading...</span></div>',
                emptyTable: 'No data available',
                zeroRecords: 'No matching records found',
            },
            responsive: true,
            stateSave: true,
            dom: '<"row"<"col-sm-6"l><"col-sm-6"f>>rtip',
            drawCallback: function() {
                // Re-init tooltips on redraw
                document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function(el) {
                    new bootstrap.Tooltip(el);
                });
            }
        };

        const finalOptions = Object.assign({}, defaultOptions, options);
        this.instances[tableId] = $('#' + tableId).DataTable(finalOptions);
        return this.instances[tableId];
    },

    reload(tableId) {
        if (this.instances[tableId]) {
            this.instances[tableId].ajax.reload(null, false);
        }
    },

    getInstance(tableId) {
        return this.instances[tableId] || null;
    }
};
```

## File: public\assets\js\form-helper.js

```js
/**
 * Form Helper â€” AJAX form submission with validation display
 */
const FormHelper = {
    init() {
        document.addEventListener('submit', (e) => {
            const form = e.target;
            if (form.getAttribute('data-ajax') !== 'true') return;
            e.preventDefault();
            this.submit(form);
        });
    },

    submit(form) {
        this.clearErrors(form);
        const submitBtn = form.querySelector('[type="submit"]');
        const originalText = submitBtn ? submitBtn.innerHTML : '';

        if (submitBtn) {
            submitBtn.disabled = true;
            submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Processing...';
        }

        const formData = new FormData(form);
        const method = form.method.toUpperCase() || 'POST';
        const url = form.action;

        $.ajax({
            url: url,
            type: method,
            data: formData,
            processData: false,
            contentType: false,
            success: (response) => {
                if (response.success) {
                    this.showToast('success', response.message || 'Operation successful');
                    if (response.redirect) {
                        setTimeout(() => { window.location.href = response.redirect; }, 800);
                    }
                } else if (response.errors) {
                    this.showErrors(form, response.errors);
                } else {
                    this.showToast('error', response.message || 'Operation failed');
                }
            },
            error: (xhr) => {
                if (xhr.status === 422) {
                    const response = xhr.responseJSON;
                    if (response && response.errors) {
                        this.showErrors(form, response.errors);
                    }
                }
                // 401, 403, 500 handled by global handler
            },
            complete: () => {
                if (submitBtn) {
                    submitBtn.disabled = false;
                    submitBtn.innerHTML = originalText;
                }
            }
        });
    },

    clearErrors(form) {
        form.querySelectorAll('.is-invalid').forEach(el => el.classList.remove('is-invalid'));
        form.querySelectorAll('.invalid-feedback').forEach(el => el.textContent = '');
    },

    showErrors(form, errors) {
        for (const [field, messages] of Object.entries(errors)) {
            const input = form.querySelector(`[name="${field}"]`);
            const feedback = form.querySelector(`[data-field="${field}"]`) || (input ? input.parentElement.querySelector('.invalid-feedback') : null);

            if (input) {
                input.classList.add('is-invalid');
            }
            if (feedback) {
                feedback.textContent = Array.isArray(messages) ? messages[0] : messages;
                feedback.style.display = 'block';
            }
        }

        // Scroll to first error
        const firstError = form.querySelector('.is-invalid');
        if (firstError) {
            firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
            firstError.focus();
        }
    },

    showToast(type, message) {
        const Toast = Swal.mixin({
            toast: true,
            position: 'top-end',
            showConfirmButton: false,
            timer: 3000,
            timerProgressBar: true,
        });
        Toast.fire({
            icon: type,
            title: message
        });
    }
};

document.addEventListener('DOMContentLoaded', () => FormHelper.init());
```

## File: public\assets\js\modal-helper.js

```js
/**
 * Modal Helper â€” Auto-load content from AJAX URL into modals
 */
const ModalHelper = {
    init() {
        document.addEventListener('click', (e) => {
            const trigger = e.target.closest('[data-ajax-url]');
            if (!trigger) return;

            const modalTarget = trigger.getAttribute('data-bs-target');
            if (!modalTarget) return;

            const modal = document.querySelector(modalTarget);
            if (!modal) return;

            const url = trigger.getAttribute('data-ajax-url');
            const bodyEl = modal.querySelector('.modal-body');

            if (bodyEl && url) {
                bodyEl.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>';

                $.get(url)
                    .done(function(response) {
                        if (typeof response === 'object' && response.html) {
                            bodyEl.innerHTML = response.html;
                        } else if (typeof response === 'string') {
                            bodyEl.innerHTML = response;
                        } else {
                            bodyEl.innerHTML = '<pre>' + JSON.stringify(response, null, 2) + '</pre>';
                        }
                    })
                    .fail(function(xhr) {
                        bodyEl.innerHTML = '<div class="alert alert-danger">Failed to load content. Please try again.</div>';
                    });
            }
        });
    }
};

document.addEventListener('DOMContentLoaded', () => ModalHelper.init());
```

## File: public\index.php

```php
<?php
/**
 * Club Management System â€” Front Controller
 * Phase 0: The Genesis
 */

error_reporting(E_ALL);
ini_set('display_errors', 0);
ob_start();

define('ROOT_PATH', dirname(__DIR__));
define('PUBLIC_PATH', __DIR__);
define('DS', DIRECTORY_SEPARATOR);

require_once ROOT_PATH . '/core/Bootstrap.php';

$app = new App();
$app->run();

ob_end_flush();
```

## File: views\auth\change_password.php

```php
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header">
                <h5 class="mb-0"><i class="fas fa-key me-2"></i><?= lang('auth.change_password') ?></h5>
            </div>
            <div class="card-body">
                <form method="POST" action="/change-password" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('auth.current_password') ?> <span class="text-danger">*</span></label>
                        <input type="password" name="current_password" class="form-control" required>
                        <div class="invalid-feedback" data-field="current_password"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('auth.new_password') ?> <span class="text-danger">*</span></label>
                        <input type="password" name="new_password" class="form-control" required minlength="8">
                        <div class="invalid-feedback" data-field="new_password"></div>
                        <small class="text-muted">Minimum <?= Config::get('password_min_length', 8) ?> characters</small>
                    </div>
                    <div class="mb-3">
                        <label class="form-label"><?= lang('auth.confirm_password') ?> <span class="text-danger">*</span></label>
                        <input type="password" name="new_password_confirmation" class="form-control" required>
                        <div class="invalid-feedback" data-field="new_password_confirmation"></div>
                    </div>
                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                        <a href="/dashboard" class="btn btn-secondary"><?= lang('common.cancel') ?></a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: views\auth\login.php

```php
<?php $oldUsername = Session::getFlash('old')['username'] ?? ''; ?>
<form method="POST" action="/login" id="login-form">
    <?= csrf_field() ?>
    <div class="mb-3">
        <label class="form-label"><?= lang('auth.username') ?></label>
        <div class="input-group">
            <span class="input-group-text"><i class="fas fa-user"></i></span>
            <input type="text" name="username" class="form-control" value="<?= e($oldUsername) ?>" autofocus required placeholder="<?= lang('auth.username') ?>">
        </div>
    </div>
    <div class="mb-3">
        <label class="form-label"><?= lang('auth.password') ?></label>
        <div class="input-group">
            <span class="input-group-text"><i class="fas fa-lock"></i></span>
            <input type="password" name="password" class="form-control" required placeholder="<?= lang('auth.password') ?>">
        </div>
    </div>
    <div class="d-grid">
        <button type="submit" class="btn btn-primary btn-lg"><?= lang('auth.login') ?></button>
    </div>
</form>
```

## File: views\auth\profile.php

```php
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header">
                <h5 class="mb-0"><i class="fas fa-user me-2"></i><?= lang('auth.profile') ?></h5>
            </div>
            <div class="card-body">
                <form method="POST" action="/profile" data-ajax="true">
                    <?= csrf_field() ?>
                    <div class="mb-3">
                        <label class="form-label">Username</label>
                        <input type="text" class="form-control" value="<?= e($user['username'] ?? '') ?>" disabled>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Full Name <span class="text-danger">*</span></label>
                        <input type="text" name="full_name_en" class="form-control" value="<?= e($user['full_name_en'] ?? '') ?>" required>
                        <div class="invalid-feedback" data-field="full_name_en"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Email <span class="text-danger">*</span></label>
                        <input type="email" name="email" class="form-control" value="<?= e($user['email'] ?? '') ?>" required>
                        <div class="invalid-feedback" data-field="email"></div>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Phone</label>
                        <input type="text" name="phone" class="form-control" value="<?= e($user['phone'] ?? '') ?>">
                    </div>
                    <button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i><?= lang('common.save') ?></button>
                </form>
            </div>
        </div>
    </div>
</div>
```

## File: views\dashboard\index.php

```php
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h3 class="mb-1"><?= lang('dashboard.welcome') ?>, <?= e(auth_user('full_name_en') ?? 'User') ?>!</h3>
        <p class="text-muted mb-0"><?= date('l, F j, Y') ?></p>
    </div>
</div>

<!-- Widgets Grid -->
<div class="row g-3">
    <?php if (empty($widgets)): ?>
    <div class="col-12">
        <div class="card">
            <div class="card-body text-center py-5">
                <i class="fas fa-cube fa-3x text-muted mb-3"></i>
                <h5 class="text-muted">No modules installed yet</h5>
                <p class="text-muted">Dashboard widgets will appear here as modules are added to the system.</p>
            </div>
        </div>
    </div>
    <?php else: ?>
    <?php foreach ($widgets as $widget): ?>
    <div class="<?= $widget['size'] ?? 'col-md-4' ?>">
        <?php
        $partialPath = ROOT_PATH . '/modules/' . $widget['_module'] . '/' . ($widget['partial'] ?? '');
        if (file_exists($partialPath)) {
            include $partialPath;
        } else {
            echo '<div class="card"><div class="card-body text-muted">Widget: ' . e($widget['label_en'] ?? 'Unknown') . '</div></div>';
        }
        ?>
    </div>
    <?php endforeach; ?>
    <?php endif; ?>
</div>

<!-- Quick System Stats (Phase 0 default widget) -->
<div class="row g-3 mt-3">
    <?php
    try {
        $db = Database::getInstance();
        $totalMembers = $db->fetchColumn("SELECT COUNT(*) FROM members WHERE status = 'active' AND is_deleted = 0");
        $totalUsers = $db->fetchColumn("SELECT COUNT(*) FROM auth_users WHERE is_active = 1 AND is_deleted = 0");
        $modulesCount = count(ModuleRegistry::getModules());
    } catch (\Exception $e) {
        $totalMembers = 0;
        $totalUsers = 0;
        $modulesCount = 0;
    }
    ?>
    <div class="col-md-3">
        <div class="card border-primary h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1">Active Members</h6>
                        <h3 class="mb-0"><?= number_format($totalMembers) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-users fa-2x text-primary opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-success h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1">System Users</h6>
                        <h3 class="mb-0"><?= number_format($totalUsers) ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-user-shield fa-2x text-success opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-info h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1">Active Modules</h6>
                        <h3 class="mb-0"><?= $modulesCount ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-puzzle-piece fa-2x text-info opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="card border-warning h-100">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted mb-1">Server Time</h6>
                        <h3 class="mb-0" style="font-size:1.2rem"><?= date('H:i:s') ?></h3>
                    </div>
                    <div class="align-self-center"><i class="fas fa-clock fa-2x text-warning opacity-50"></i></div>
                </div>
            </div>
        </div>
    </div>
</div>
```

## File: views\errors\403.php

```php
<div class="text-center py-5">
    <i class="fas fa-ban fa-5x text-danger mb-4"></i>
    <h1 class="display-4">403</h1>
    <h4 class="text-muted mb-3">Access Denied</h4>
    <p class="text-muted">You do not have permission to access this resource.</p>
    <a href="/dashboard" class="btn btn-primary"><i class="fas fa-home me-1"></i>Back to Dashboard</a>
</div>
```

## File: views\errors\404.php

```php
<div class="text-center py-5">
    <i class="fas fa-search fa-5x text-warning mb-4"></i>
    <h1 class="display-4">404</h1>
    <h4 class="text-muted mb-3">Page Not Found</h4>
    <p class="text-muted">The page you're looking for doesn't exist or has been moved.</p>
    <a href="/dashboard" class="btn btn-primary"><i class="fas fa-home me-1"></i>Back to Dashboard</a>
</div>
```

## File: views\errors\500.php

```php
<div class="text-center py-5">
    <i class="fas fa-exclamation-triangle fa-5x text-danger mb-4"></i>
    <h1 class="display-4">500</h1>
    <h4 class="text-muted mb-3">Internal Server Error</h4>
    <p class="text-muted">Something went wrong. Please try again later.</p>
    <a href="/dashboard" class="btn btn-primary"><i class="fas fa-home me-1"></i>Back to Dashboard</a>
</div>
```

## File: views\lang\ar.php

```php
<?php
return [
    'common.save' => 'Ø­ÙØ¸',
    'common.cancel' => 'Ø¥Ù„ØºØ§Ø¡',
    'common.delete' => 'Ø­Ø°Ù',
    'common.edit' => 'ØªØ¹Ø¯ÙŠÙ„',
    'common.view' => 'Ø¹Ø±Ø¶',
    'common.create' => 'Ø¥Ù†Ø´Ø§Ø¡',
    'common.search' => 'Ø¨Ø­Ø«',
    'common.actions' => 'Ø¥Ø¬Ø±Ø§Ø¡Ø§Øª',
    'common.status' => 'Ø§Ù„Ø­Ø§Ù„Ø©',
    'common.yes' => 'Ù†Ø¹Ù…',
    'common.no' => 'Ù„Ø§',
    'common.confirm' => 'ØªØ£ÙƒÙŠØ¯',
    'common.back' => 'Ø±Ø¬ÙˆØ¹',
    'common.export_csv' => 'ØªØµØ¯ÙŠØ± CSV',
    'common.export_excel' => 'ØªØµØ¯ÙŠØ± Excel',
    'common.export_pdf' => 'ØªØµØ¯ÙŠØ± PDF',
    'common.print' => 'Ø·Ø¨Ø§Ø¹Ø©',
    'common.loading' => 'Ø¬Ø§Ø±ÙŠ Ø§Ù„ØªØ­Ù…ÙŠÙ„...',
    'common.no_data' => 'Ù„Ø§ ØªÙˆØ¬Ø¯ Ø¨ÙŠØ§Ù†Ø§Øª',
    'common.showing' => 'Ø¹Ø±Ø¶',
    'common.of' => 'Ù…Ù†',
    'common.entries' => 'Ø³Ø¬Ù„',
    'common.are_you_sure' => 'Ù‡Ù„ Ø£Ù†Øª Ù…ØªØ£ÙƒØ¯ØŸ',
    'common.cannot_undo' => 'Ù„Ø§ ÙŠÙ…ÙƒÙ† Ø§Ù„ØªØ±Ø§Ø¬Ø¹ Ø¹Ù† Ù‡Ø°Ø§ Ø§Ù„Ø¥Ø¬Ø±Ø§Ø¡.',
    'common.success' => 'Ù†Ø¬Ø§Ø­',
    'common.error' => 'Ø®Ø·Ø£',
    'common.close' => 'Ø¥ØºÙ„Ø§Ù‚',
    'common.submit' => 'Ø¥Ø±Ø³Ø§Ù„',
    'common.reset' => 'Ø¥Ø¹Ø§Ø¯Ø© ØªØ¹ÙŠÙŠÙ†',
    'common.filter' => 'ØªØµÙÙŠØ©',
    'common.clear' => 'Ù…Ø³Ø­',
    'common.all' => 'Ø§Ù„ÙƒÙ„',
    'common.home' => 'Ø§Ù„Ø±Ø¦ÙŠØ³ÙŠØ©',
    'common.details' => 'Ø§Ù„ØªÙØ§ØµÙŠÙ„',
    'auth.login' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
    'auth.logout' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø®Ø±ÙˆØ¬',
    'auth.username' => 'Ø§Ø³Ù… Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù…',
    'auth.password' => 'ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±',
    'auth.remember_me' => 'ØªØ°ÙƒØ±Ù†ÙŠ',
    'auth.change_password' => 'ØªØºÙŠÙŠØ± ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±',
    'auth.current_password' => 'ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± Ø§Ù„Ø­Ø§Ù„ÙŠØ©',
    'auth.new_password' => 'ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± Ø§Ù„Ø¬Ø¯ÙŠØ¯Ø©',
    'auth.confirm_password' => 'ØªØ£ÙƒÙŠØ¯ ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± Ø§Ù„Ø¬Ø¯ÙŠØ¯Ø©',
    'auth.profile' => 'Ù…Ù„ÙÙŠ Ø§Ù„Ø´Ø®ØµÙŠ',
    'dashboard.title' => 'Ù„ÙˆØ­Ø© Ø§Ù„ØªØ­ÙƒÙ…',
    'dashboard.welcome' => 'Ù…Ø±Ø­Ø¨Ø§Ù‹',
];
```

## File: views\lang\en.php

```php
<?php
return [
    'common.save' => 'Save',
    'common.cancel' => 'Cancel',
    'common.delete' => 'Delete',
    'common.edit' => 'Edit',
    'common.view' => 'View',
    'common.create' => 'Create',
    'common.search' => 'Search',
    'common.actions' => 'Actions',
    'common.status' => 'Status',
    'common.yes' => 'Yes',
    'common.no' => 'No',
    'common.confirm' => 'Confirm',
    'common.back' => 'Back',
    'common.export_csv' => 'Export CSV',
    'common.export_excel' => 'Export Excel',
    'common.export_pdf' => 'Export PDF',
    'common.print' => 'Print',
    'common.loading' => 'Loading...',
    'common.no_data' => 'No data available',
    'common.showing' => 'Showing',
    'common.of' => 'of',
    'common.entries' => 'entries',
    'common.are_you_sure' => 'Are you sure?',
    'common.cannot_undo' => 'This action cannot be undone.',
    'common.success' => 'Success',
    'common.error' => 'Error',
    'common.close' => 'Close',
    'common.submit' => 'Submit',
    'common.reset' => 'Reset',
    'common.filter' => 'Filter',
    'common.clear' => 'Clear',
    'common.all' => 'All',
    'common.home' => 'Home',
    'common.details' => 'Details',
    'auth.login' => 'Login',
    'auth.logout' => 'Logout',
    'auth.username' => 'Username',
    'auth.password' => 'Password',
    'auth.remember_me' => 'Remember Me',
    'auth.change_password' => 'Change Password',
    'auth.current_password' => 'Current Password',
    'auth.new_password' => 'New Password',
    'auth.confirm_password' => 'Confirm New Password',
    'auth.profile' => 'My Profile',
    'dashboard.title' => 'Dashboard',
    'dashboard.welcome' => 'Welcome',
];
```

## File: views\layouts\app.php

```php
<?php
$direction = is_rtl() ? 'rtl' : 'ltr';
$langCode = current_lang();
$clubName = Config::get('club_name_en', 'Club Management');
$pageTitle = ($title ?? 'Dashboard') . ' | ' . $clubName;
?>
<!DOCTYPE html>
<html lang="<?= $langCode ?>" dir="<?= $direction ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= csrf_token() ?>">
    <title><?= e($pageTitle) ?></title>

    <!-- Bootstrap 5.3 -->
    <?php if (is_rtl()): ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
    <?php else: ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <?php endif; ?>

    <!-- Font Awesome 6 -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">

    <!-- DataTables -->
    <link href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap5.min.css" rel="stylesheet">

    <!-- Select2 -->
    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet">

    <!-- Flatpickr -->
    <link href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css" rel="stylesheet">

    <!-- SweetAlert2 -->
    <link href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" rel="stylesheet">

    <!-- App CSS -->
    <link href="/assets/css/app.css" rel="stylesheet">
    <?php if (is_rtl()): ?>
    <link href="/assets/css/rtl.css" rel="stylesheet">
    <?php endif; ?>

    <!-- Module CSS -->
    <?php foreach (ModuleRegistry::getCssFiles() as $css): ?>
    <link href="<?= $css ?>" rel="stylesheet">
    <?php endforeach; ?>

    <?= $__view->getStack('styles') ?? '' ?>
</head>
<body class="cms-body">
    <!-- Sidebar -->
    <nav id="sidebar" class="cms-sidebar">
        <div class="sidebar-header">
            <a href="/dashboard" class="sidebar-brand">
                <i class="fas fa-shield-alt me-2"></i>
                <span class="sidebar-brand-text"><?= e(Config::get('club_name_' . $langCode, 'CMS')) ?></span>
            </a>
            <button class="btn btn-sm sidebar-close d-lg-none" id="sidebarClose">
                <i class="fas fa-times"></i>
            </button>
        </div>
        <div class="sidebar-body">
            <ul class="nav flex-column">
                <li class="nav-item">
                    <a class="nav-link sidebar-link <?= active_class('/dashboard') ?>" href="/dashboard">
                        <i class="fas fa-tachometer-alt me-2"></i>
                        <span><?= lang('dashboard.title') ?></span>
                    </a>
                </li>
                <?= MenuBuilder::render() ?>
            </ul>
        </div>
        <div class="sidebar-footer">
            <small class="text-muted">&copy; <?= date('Y') ?> <?= e(Config::get('club_name_en', 'CMS')) ?></small>
        </div>
    </nav>

    <!-- Overlay -->
    <div class="sidebar-overlay" id="sidebarOverlay"></div>

    <!-- Main Content -->
    <div id="mainContent" class="cms-main">
        <!-- Header -->
        <header class="cms-header">
            <div class="d-flex align-items-center">
                <button class="btn btn-sm me-3 sidebar-toggle" id="sidebarToggle">
                    <i class="fas fa-bars fa-lg"></i>
                </button>
                <?php if (!empty($breadcrumbs)): ?>
                <nav aria-label="breadcrumb" class="d-none d-md-block">
                    <ol class="breadcrumb mb-0">
                        <li class="breadcrumb-item"><a href="/dashboard"><i class="fas fa-home"></i></a></li>
                        <?php foreach ($breadcrumbs as $i => $crumb): ?>
                        <?php if (isset($crumb['url']) && $i < count($breadcrumbs) - 1): ?>
                        <li class="breadcrumb-item"><a href="<?= e($crumb['url']) ?>"><?= e($crumb['label']) ?></a></li>
                        <?php else: ?>
                        <li class="breadcrumb-item active"><?= e($crumb['label']) ?></li>
                        <?php endif; ?>
                        <?php endforeach; ?>
                    </ol>
                </nav>
                <?php endif; ?>
            </div>
            <div class="d-flex align-items-center gap-2">
                <!-- Language Switcher -->
                <div class="dropdown">
                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
                        <i class="fas fa-globe me-1"></i><?= strtoupper(current_lang()) ?>
                    </button>
                    <ul class="dropdown-menu dropdown-menu-end">
                        <li><a class="dropdown-item" href="/lang/en">ðŸ‡¬ðŸ‡§ English</a></li>
                        <li><a class="dropdown-item" href="/lang/ar">ðŸ‡ªðŸ‡¬ Ø§Ù„Ø¹Ø±Ø¨ÙŠØ©</a></li>
                    </ul>
                </div>
                <!-- User Dropdown -->
                <div class="dropdown">
                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
                        <i class="fas fa-user-circle me-1"></i>
                        <span class="d-none d-md-inline"><?= e(auth_user('full_name_en') ?? 'User') ?></span>
                    </button>
                    <ul class="dropdown-menu dropdown-menu-end">
                        <li><a class="dropdown-item" href="/profile"><i class="fas fa-user me-2"></i><?= lang('auth.profile') ?></a></li>
                        <li><a class="dropdown-item" href="/change-password"><i class="fas fa-key me-2"></i><?= lang('auth.change_password') ?></a></li>
                        <li><hr class="dropdown-divider"></li>
                        <li>
                            <form method="POST" action="/logout" class="d-inline">
                                <?= csrf_field() ?>
                                <button type="submit" class="dropdown-item text-danger"><i class="fas fa-sign-out-alt me-2"></i><?= lang('auth.logout') ?></button>
                            </form>
                        </li>
                    </ul>
                </div>
            </div>
        </header>

        <!-- Alerts -->
        <div class="cms-content">
            <?php
            $alerts = Session::getFlash('alerts') ?? [];
            $error = Session::getFlash('error');
            $success = Session::getFlash('success');
            if ($error) $alerts[] = ['type' => 'danger', 'message' => $error];
            if ($success) $alerts[] = ['type' => 'success', 'message' => $success];
            ?>
            <?php foreach ($alerts as $alert): ?>
            <div class="alert alert-<?= e($alert['type']) ?> alert-dismissible fade show" role="alert">
                <?= e($alert['message']) ?>
                <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
            </div>
            <?php endforeach; ?>

            <!-- Page Content -->
            <?= $content ?>
        </div>

        <!-- Footer -->
        <footer class="cms-footer">
            <span>&copy; <?= date('Y') ?> <?= e(Config::get('club_name_en', 'Club Management System')) ?>. All rights reserved.</span>
        </footer>
    </div>

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

    <!-- Bootstrap 5.3 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

    <!-- DataTables -->
    <script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>

    <!-- Select2 -->
    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

    <!-- Flatpickr -->
    <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>

    <!-- SweetAlert2 -->
    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

    <!-- Chart.js -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>

    <!-- Core JS -->
    <script src="/assets/js/app.js"></script>
    <script src="/assets/js/datatable-helper.js"></script>
    <script src="/assets/js/form-helper.js"></script>
    <script src="/assets/js/modal-helper.js"></script>

    <!-- Module JS -->
    <?php foreach (ModuleRegistry::getJsFiles() as $js): ?>
    <script src="<?= $js ?>"></script>
    <?php endforeach; ?>

    <?= $__view->getStack('scripts') ?? '' ?>
</body>
</html>
```

## File: views\layouts\app_114.php

```php
<?php
/**
 * Main Application Layout â€” Authenticated pages
 *
 * Available variables injected by View::render():
 *   $content   â€” Rendered view content
 *   $_styles   â€” Pushed CSS from view (BUG 1 FIX: use variable, not getStack)
 *   $_scripts  â€” Pushed JS from view (BUG 1 FIX: use variable, not getStack)
 *   $title     â€” Page title (from controller)
 *   $breadcrumbs â€” Breadcrumb array (from controller)
 *   $__view    â€” View instance (available but not needed for stacks)
 */
$direction = is_rtl() ? 'rtl' : 'ltr';
$langCode = current_lang();
$clubName = Config::get('club_name_en', 'Club Management');
$pageTitle = ($title ?? 'Dashboard') . ' | ' . $clubName;
?>
<!DOCTYPE html>
<html lang="<?= $langCode ?>" dir="<?= $direction ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= csrf_token() ?>">
    <title><?= e($pageTitle) ?></title>

    <!-- Bootstrap 5.3 -->
    <?php if (is_rtl()): ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
    <?php else: ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <?php endif; ?>

    <!-- Font Awesome 6 -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">

    <!-- DataTables -->
    <link href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap5.min.css" rel="stylesheet">

    <!-- Select2 -->
    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet">

    <!-- Flatpickr -->
    <link href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css" rel="stylesheet">

    <!-- SweetAlert2 -->
    <link href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" rel="stylesheet">

    <!-- App CSS -->
    <link href="/assets/css/app.css" rel="stylesheet">
    <?php if (is_rtl()): ?>
    <link href="/assets/css/rtl.css" rel="stylesheet">
    <?php endif; ?>

    <!-- Module CSS -->
    <?php foreach (ModuleRegistry::getCssFiles() as $cssFile): ?>
    <link href="<?= $cssFile ?>" rel="stylesheet">
    <?php endforeach; ?>

    <!-- BUG 1 FIX: Use $_styles variable, not $__view->getStack() -->
    <?= $_styles ?? '' ?>
</head>
<body class="cms-body">
    <!-- Sidebar -->
    <nav id="sidebar" class="cms-sidebar">
        <div class="sidebar-header">
            <a href="/dashboard" class="sidebar-brand">
                <i class="fas fa-shield-alt me-2"></i>
                <span class="sidebar-brand-text"><?= e(Config::get('club_name_' . $langCode, 'CMS')) ?></span>
            </a>
            <button class="btn btn-sm sidebar-close d-lg-none" id="sidebarClose">
                <i class="fas fa-times text-white"></i>
            </button>
        </div>
        <div class="sidebar-body">
            <ul class="nav flex-column">
                <li class="nav-item">
                    <a class="nav-link sidebar-link <?= active_class('/dashboard') ?>" href="/dashboard">
                        <i class="fas fa-tachometer-alt me-2"></i>
                        <span><?= lang('dashboard.title') ?></span>
                    </a>
                </li>
                <?= MenuBuilder::render() ?>
            </ul>
        </div>
        <div class="sidebar-footer">
            <small class="text-muted">&copy; <?= date('Y') ?> <?= e(Config::get('club_name_en', 'CMS')) ?></small>
        </div>
    </nav>

    <!-- Overlay for mobile sidebar -->
    <div class="sidebar-overlay" id="sidebarOverlay"></div>

    <!-- Main Content -->
    <div id="mainContent" class="cms-main">
        <!-- Header -->
        <header class="cms-header">
            <div class="d-flex align-items-center">
                <button class="btn btn-sm me-3 sidebar-toggle" id="sidebarToggle" type="button">
                    <i class="fas fa-bars fa-lg"></i>
                </button>
                <?php if (!empty($breadcrumbs)): ?>
                <nav aria-label="breadcrumb" class="d-none d-md-block">
                    <ol class="breadcrumb mb-0">
                        <li class="breadcrumb-item"><a href="/dashboard"><i class="fas fa-home"></i></a></li>
                        <?php foreach ($breadcrumbs as $i => $crumb): ?>
                            <?php if (isset($crumb['url']) && $i < count($breadcrumbs) - 1): ?>
                            <li class="breadcrumb-item"><a href="<?= e($crumb['url']) ?>"><?= e($crumb['label']) ?></a></li>
                            <?php else: ?>
                            <li class="breadcrumb-item active"><?= e($crumb['label']) ?></li>
                            <?php endif; ?>
                        <?php endforeach; ?>
                    </ol>
                </nav>
                <?php endif; ?>
            </div>
            <div class="d-flex align-items-center gap-2">
                <!-- Language Switcher -->
                <div class="dropdown">
                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
                        <i class="fas fa-globe me-1"></i><?= strtoupper(current_lang()) ?>
                    </button>
                    <ul class="dropdown-menu dropdown-menu-end">
                        <li><a class="dropdown-item <?= current_lang() === 'en' ? 'active' : '' ?>" href="/lang/en">ðŸ‡¬ðŸ‡§ English</a></li>
                        <li><a class="dropdown-item <?= current_lang() === 'ar' ? 'active' : '' ?>" href="/lang/ar">ðŸ‡ªðŸ‡¬ Ø§Ù„Ø¹Ø±Ø¨ÙŠØ©</a></li>
                    </ul>
                </div>
                <!-- User Dropdown -->
                <div class="dropdown">
                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
                        <i class="fas fa-user-circle me-1"></i>
                        <span class="d-none d-md-inline"><?= e(auth_user('full_name_en') ?? 'User') ?></span>
                    </button>
                    <ul class="dropdown-menu dropdown-menu-end">
                        <li><a class="dropdown-item" href="/profile"><i class="fas fa-user me-2"></i><?= lang('auth.profile') ?></a></li>
                        <li><a class="dropdown-item" href="/change-password"><i class="fas fa-key me-2"></i><?= lang('auth.change_password') ?></a></li>
                        <li><hr class="dropdown-divider"></li>
                        <li>
                            <form method="POST" action="/logout" class="d-inline w-100">
                                <?= csrf_field() ?>
                                <button type="submit" class="dropdown-item text-danger"><i class="fas fa-sign-out-alt me-2"></i><?= lang('auth.logout') ?></button>
                            </form>
                        </li>
                    </ul>
                </div>
            </div>
        </header>

        <!-- Main Content Area -->
        <div class="cms-content">
            <!-- Flash Alerts -->
            <?php $alerts = get_flash_alerts(); ?>
            <?php foreach ($alerts as $alert): ?>
            <div class="alert alert-<?= e($alert['type']) ?> alert-dismissible fade show" role="alert">
                <?php if ($alert['type'] === 'danger'): ?><i class="fas fa-exclamation-circle me-1"></i><?php endif; ?>
                <?php if ($alert['type'] === 'success'): ?><i class="fas fa-check-circle me-1"></i><?php endif; ?>
                <?php if ($alert['type'] === 'warning'): ?><i class="fas fa-exclamation-triangle me-1"></i><?php endif; ?>
                <?php if ($alert['type'] === 'info'): ?><i class="fas fa-info-circle me-1"></i><?php endif; ?>
                <?= e($alert['message']) ?>
                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            </div>
            <?php endforeach; ?>

            <!-- Page Content -->
            <?= $content ?>
        </div>

        <!-- Footer -->
        <footer class="cms-footer">
            <span>&copy; <?= date('Y') ?> <?= e(Config::get('club_name_en', 'Club Management System')) ?>. All rights reserved.</span>
        </footer>
    </div>

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

    <!-- Bootstrap 5.3 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

    <!-- DataTables -->
    <script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>

    <!-- Select2 -->
    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

    <!-- Flatpickr -->
    <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>

    <!-- SweetAlert2 -->
    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

    <!-- Chart.js -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>

    <!-- Core JS -->
    <script src="/assets/js/app.js"></script>
    <script src="/assets/js/datatable-helper.js"></script>
    <script src="/assets/js/form-helper.js"></script>
    <script src="/assets/js/modal-helper.js"></script>

    <!-- Module JS -->
    <?php foreach (ModuleRegistry::getJsFiles() as $jsFile): ?>
    <script src="<?= $jsFile ?>"></script>
    <?php endforeach; ?>

    <!-- BUG 1 FIX: Use $_scripts variable, not $__view->getStack() -->
    <?= $_scripts ?? '' ?>
</body>
</html>
```

## File: views\layouts\auth.php

```php
<?php
$direction = is_rtl() ? 'rtl' : 'ltr';
$langCode = current_lang();
$clubName = Config::get('club_name_en', 'Club Management');
$pageTitle = ($title ?? 'Login') . ' | ' . $clubName;
?>
<!DOCTYPE html>
<html lang="<?= $langCode ?>" dir="<?= $direction ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= csrf_token() ?>">
    <title><?= e($pageTitle) ?></title>
    <?php if (is_rtl()): ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
    <?php else: ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <?php endif; ?>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
    <link href="/assets/css/app.css" rel="stylesheet">
    <?php if (is_rtl()): ?>
    <link href="/assets/css/rtl.css" rel="stylesheet">
    <?php endif; ?>
</head>
<body class="auth-body">
    <div class="auth-wrapper">
        <div class="auth-card card shadow">
            <div class="card-body p-4 p-md-5">
                <div class="text-center mb-4">
                    <i class="fas fa-shield-alt fa-3x text-primary mb-3"></i>
                    <h4><?= e($clubName) ?></h4>
                </div>
                <?php
                $error = Session::getFlash('error');
                if ($error): ?>
                <div class="alert alert-danger alert-dismissible fade show" role="alert">
                    <?= e($error) ?>
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
                <?php endif; ?>
                <?= $content ?>
            </div>
        </div>
        <div class="text-center mt-3">
            <a href="/lang/en" class="text-muted small me-2">English</a>
            <a href="/lang/ar" class="text-muted small">Ø§Ù„Ø¹Ø±Ø¨ÙŠØ©</a>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
```

## File: views\layouts\auth_115.php

```php
<?php
$direction = is_rtl() ? 'rtl' : 'ltr';
$langCode = current_lang();
$clubName = Config::get('club_name_en', 'Club Management');
$pageTitle = ($title ?? 'Login') . ' | ' . $clubName;
?>
<!DOCTYPE html>
<html lang="<?= $langCode ?>" dir="<?= $direction ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= csrf_token() ?>">
    <title><?= e($pageTitle) ?></title>
    <?php if (is_rtl()): ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
    <?php else: ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <?php endif; ?>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
    <link href="/assets/css/app.css" rel="stylesheet">
    <?php if (is_rtl()): ?>
    <link href="/assets/css/rtl.css" rel="stylesheet">
    <?php endif; ?>
</head>
<body class="auth-body">
    <div class="auth-wrapper">
        <div class="auth-card card shadow">
            <div class="card-body p-4 p-md-5">
                <div class="text-center mb-4">
                    <i class="fas fa-shield-alt fa-3x text-primary mb-3"></i>
                    <h4><?= e($clubName) ?></h4>
                </div>
                <!-- Flash Alerts - uses same helper as app layout -->
                <?php $alerts = get_flash_alerts(); ?>
                <?php foreach ($alerts as $alert): ?>
                <div class="alert alert-<?= e($alert['type']) ?> alert-dismissible fade show" role="alert">
                    <?= e($alert['message']) ?>
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                </div>
                <?php endforeach; ?>
                <?= $content ?>
            </div>
        </div>
        <div class="text-center mt-3">
            <a href="/lang/en" class="text-muted small me-2">English</a>
            <a href="/lang/ar" class="text-muted small">Ø§Ù„Ø¹Ø±Ø¨ÙŠØ©</a>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
```

## File: views\layouts\print.php

```php
<?php
$direction = is_rtl() ? 'rtl' : 'ltr';
$langCode = current_lang();
?>
<!DOCTYPE html>
<html lang="<?= $langCode ?>" dir="<?= $direction ?>">
<head>
    <meta charset="UTF-8">
    <title><?= e($title ?? 'Print') ?></title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body { padding: 20px; font-size: 12pt; }
        @media print { .no-print { display: none !important; } }
        table { width: 100%; border-collapse: collapse; }
        th, td { border: 1px solid #ddd; padding: 6px 8px; }
    </style>
</head>
<body>
    <div class="no-print mb-3">
        <button onclick="window.print()" class="btn btn-primary btn-sm"><i class="fas fa-print"></i> Print</button>
        <button onclick="window.close()" class="btn btn-secondary btn-sm"><i class="fas fa-times"></i> Close</button>
    </div>
    <?= $content ?>
</body>
</html>
```
