# Libra Law Firm - Architecture Document

**Version:** 1.1
**Date:** December 21, 2025
**Status:** Approved
**Domain:** libra.ps

---

## Change Log

| Date | Version | Description | Author |
|------|---------|-------------|--------|
| Dec 21, 2025 | 1.0 | Initial architecture document | Winston (Architect) |
| Dec 21, 2025 | 1.1 | Added CI/CD, rollback procedures, monitoring, timezone handling, alternatives analysis | Winston (Architect) |

---

## Table of Contents

1. [Introduction](#1-introduction)
2. [High Level Architecture](#2-high-level-architecture)
3. [Technology Stack](#3-technology-stack)
4. [Data Models](#4-data-models)
5. [Database Schema](#5-database-schema)
6. [Application Structure](#6-application-structure)
7. [Authentication & Authorization](#7-authentication--authorization)
8. [Core Workflows](#8-core-workflows)
9. [Routing Structure](#9-routing-structure)
10. [Email System](#10-email-system)
11. [Background Jobs & Scheduling](#11-background-jobs--scheduling)
12. [Localization & Timezone](#12-localization--timezone)
13. [Security](#13-security)
14. [Testing Strategy](#14-testing-strategy)
15. [Performance & Optimization](#15-performance--optimization)
16. [Deployment & CI/CD](#16-deployment--cicd)
17. [Monitoring & Alerting](#17-monitoring--alerting)
18. [Error Handling & Graceful Degradation](#18-error-handling--graceful-degradation)
19. [Rollback & Recovery Procedures](#19-rollback--recovery-procedures)
20. [Coding Standards](#20-coding-standards)
21. [Technology Alternatives Considered](#21-technology-alternatives-considered)
22. [Appendices](#22-appendices)

---

## 1. Introduction

### 1.1 Purpose

This document outlines the complete architecture for Libra Law Firm's web platform. It serves as the single source of truth for development, ensuring consistency and guiding AI-driven implementation.

### 1.2 Project Overview

Libra Law Firm is a bilingual (Arabic/English) Laravel application serving as both a public-facing website and internal management tool for:
- Client consultation booking and management
- Case timeline tracking
- Legal content publishing
- Administrative operations

### 1.3 Key Constraints

| Constraint | Details |
|------------|---------|
| Single Admin | One lawyer manages all operations |
| No Online Payments | All payments handled offline |
| Client-Managed Infrastructure | Rocky Linux 9 server |
| No Self-Registration | Admin creates all client accounts |
| Bilingual Requirement | Arabic (RTL) primary, English (LTR) secondary |

### 1.4 Project Context

**Existing Foundation:**
- Laravel 12.43.1 with Fortify authentication
- Livewire 3.7.3 + Volt 1.10.1 for reactive components
- Flux UI Free 2.10.2 component library
- Tailwind CSS 4.1.11
- Pest 4.2.0 testing framework

---

## 2. High Level Architecture

### 2.1 Technical Summary

Libra is a **server-rendered Laravel monolith** with Livewire for reactive UI. The architecture follows Laravel's MVC pattern enhanced with:

- **Livewire 3 + Volt** for interactive components without JavaScript complexity
- **Fortify** for headless authentication with custom Volt views
- **Eloquent ORM** for database operations
- **Blade + Flux UI** for consistent, accessible component design
- **Queue workers** for background email processing and scheduled reminders

This architecture prioritizes **simplicity and maintainability** over distributed complexity.

### 2.2 System Context Diagram

```mermaid
graph TB
    subgraph "Users"
        Admin[Admin/Lawyer]
        Client[Clients]
        Public[Public Visitors]
    end

    subgraph "Libra Platform"
        App[Laravel Application<br/>libra.ps]
    end

    subgraph "External Services"
        SMTP[SMTP Server<br/>no-reply@libra.ps]
    end

    subgraph "Infrastructure"
        Server[Rocky Linux 9<br/>Client-Managed]
    end

    Admin -->|Manage users, bookings,<br/>timelines, posts| App
    Client -->|Book consultations,<br/>view timelines| App
    Public -->|View posts,<br/>firm info| App

    App -->|Send emails| SMTP
    App -->|Deployed on| Server
```

### 2.3 Container Diagram

```mermaid
graph TB
    subgraph "Client Devices"
        Browser[Web Browser]
        Mobile[Mobile Browser]
    end

    subgraph "Web Server - Nginx"
        Static[Static Assets<br/>CSS/JS/Images]
        PHP[PHP-FPM 8.4]
    end

    subgraph "Laravel Application"
        Router[Route Handler]
        MW[Middleware Stack<br/>Auth, Locale, CSRF]

        subgraph "Presentation Layer"
            Volt[Volt Components]
            Flux[Flux UI Components]
            Blade[Blade Templates]
        end

        subgraph "Business Logic Layer"
            Actions[Action Classes]
            Fortify[Fortify Auth]
            Policies[Authorization Policies]
        end

        subgraph "Data Access Layer"
            Models[Eloquent Models]
            Observers[Model Observers]
        end
    end

    subgraph "Background Processing"
        Queue[Queue Worker<br/>Supervisor]
        Scheduler[Task Scheduler<br/>Cron]
    end

    subgraph "Data Storage"
        MariaDB[(MariaDB<br/>Production)]
        SQLite[(SQLite<br/>Development)]
        FileStorage[Local Storage<br/>Exports/Logs]
    end

    subgraph "External"
        SMTP[SMTP Server]
    end

    Browser --> Static
    Browser --> PHP
    Mobile --> Static
    Mobile --> PHP

    PHP --> Router
    Router --> MW
    MW --> Volt
    MW --> Blade
    Volt --> Flux
    Volt --> Actions
    Actions --> Fortify
    Actions --> Policies
    Actions --> Models
    Models --> Observers
    Models --> MariaDB
    Models --> SQLite

    Queue --> Actions
    Queue --> SMTP
    Scheduler --> Queue
    Actions --> FileStorage
```

### 2.4 Architectural Patterns

| Pattern | Description | Rationale |
|---------|-------------|-----------|
| **MVC + Livewire** | Laravel MVC with Livewire reactive components | Standard Laravel pattern; eliminates need for separate SPA |
| **Action Classes** | Single-purpose classes for business logic | Keeps controllers thin; reusable; testable |
| **Form Objects** | Livewire form objects for validation | Encapsulates validation; reusable forms |
| **Observer Pattern** | Eloquent observers for model events | Automatic audit logging, notification triggers |
| **Queue-based Processing** | Background jobs for emails and heavy operations | Non-blocking UX; reliable delivery |
| **Policy-based Authorization** | Laravel Policies for access control | Clean separation of authorization logic |
| **Repository Pattern** | Optional for complex queries | Only if query complexity warrants |

---

## 3. Technology Stack

### 3.1 Core Technologies

| Category | Technology | Version | Purpose | Rationale |
|----------|------------|---------|---------|-----------|
| **Runtime** | PHP | 8.4.x | Server-side language | Latest stable; required by Laravel 12 |
| **Framework** | Laravel | 12.x | Application framework | Industry standard; excellent ecosystem |
| **Reactive UI** | Livewire | 3.7.x | Interactive components | No JS build complexity; server-state |
| **Components** | Volt | 1.10.x | Single-file components | Cleaner organization; class-based |
| **UI Library** | Flux UI Free | 2.10.x | Pre-built components | Consistent design; accessibility |
| **CSS** | Tailwind CSS | 4.x | Utility-first styling | Rapid development; RTL support |
| **Auth** | Laravel Fortify | 1.33.x | Headless auth | Flexible; 2FA support |
| **Database (Prod)** | MariaDB | 10.11+ | Production database | MySQL-compatible; performant |
| **Database (Dev)** | SQLite | Latest | Development database | Zero config; fast tests |
| **Testing** | Pest | 4.x | Testing framework | Elegant syntax; Laravel integration |
| **Code Style** | Laravel Pint | 1.x | Code formatting | Consistent style |
| **Queue** | Database Driver | - | Job processing | Simple; no Redis dependency |

### 3.2 Frontend Dependencies

| Package | Version | Purpose |
|---------|---------|---------|
| Alpine.js | (via Livewire) | Minimal JS interactivity |
| Vite | 6.x | Asset bundling |
| Google Fonts | - | Cairo (Arabic), Montserrat (English) |
| Heroicons | (via Flux) | Icon library |

### 3.3 Backend Dependencies (To Add)

| Package | Version | Purpose |
|---------|---------|---------|
| `barryvdh/laravel-dompdf` | ^3.0 | PDF export generation |
| `league/csv` | ^9.0 | CSV export generation |
| `spatie/icalendar-generator` | ^2.0 | .ics calendar file generation |

### 3.4 Development Dependencies

| Package | Purpose |
|---------|---------|
| Laravel Sail | Docker development environment (optional) |
| Laravel Telescope | Debug assistant (dev only) |
| Laravel Pint | Code formatting |

---

## 4. Data Models

### 4.1 Entity Relationship Diagram

```mermaid
erDiagram
    users ||--o{ consultations : "has many"
    users ||--o{ timelines : "has many"
    users ||--o{ notifications : "has many"
    timelines ||--o{ timeline_updates : "has many"
    users ||--o{ admin_logs : "performed by"

    users {
        bigint id PK
        enum user_type "individual|company|admin"
        string full_name
        string national_id "nullable, encrypted"
        string company_name "nullable"
        string company_cert_number "nullable"
        string contact_person_name "nullable"
        string contact_person_id "nullable"
        string email UK
        string phone
        string password "hashed"
        enum status "active|deactivated"
        string preferred_language "ar|en"
        timestamp email_verified_at
        text two_factor_secret "nullable"
        text two_factor_recovery_codes "nullable"
        timestamp two_factor_confirmed_at
        timestamps created_updated
    }

    consultations {
        bigint id PK
        bigint user_id FK
        date booking_date
        time booking_time
        text problem_summary
        enum consultation_type "free|paid"
        decimal payment_amount "nullable"
        enum payment_status "pending|received|na"
        enum status "pending|approved|rejected|completed|no_show|cancelled"
        text admin_notes "nullable"
        timestamps created_updated
    }

    timelines {
        bigint id PK
        bigint user_id FK
        string case_name
        string case_reference "nullable"
        enum status "active|archived"
        timestamps created_updated
    }

    timeline_updates {
        bigint id PK
        bigint timeline_id FK
        bigint admin_id FK
        text update_text
        timestamps created_updated
    }

    posts {
        bigint id PK
        json title "ar and en"
        json body "ar and en"
        enum status "draft|published"
        timestamp published_at "nullable"
        timestamps created_updated
    }

    working_hours {
        bigint id PK
        tinyint day_of_week "0-6 Sunday-Saturday"
        time start_time
        time end_time
        boolean is_active
    }

    blocked_times {
        bigint id PK
        date block_date
        time start_time "nullable for full day"
        time end_time "nullable for full day"
        string reason "nullable"
        timestamps created_updated
    }

    notifications {
        bigint id PK
        bigint user_id FK
        string type
        json data
        timestamp read_at "nullable"
        timestamp sent_at "nullable"
        timestamps created_updated
    }

    admin_logs {
        bigint id PK
        bigint admin_id FK
        string action
        string target_type
        bigint target_id "nullable"
        json old_values "nullable"
        json new_values "nullable"
        string ip_address
        timestamp created_at
    }

    settings {
        bigint id PK
        string key UK
        json value
        timestamps created_updated
    }
```

### 4.2 Model Definitions

#### User Model

```php
// app/Models/User.php
class User extends Authenticatable
{
    use HasFactory, Notifiable, TwoFactorAuthenticatable;

    protected $fillable = [
        'user_type',
        'full_name',
        'national_id',
        'company_name',
        'company_cert_number',
        'contact_person_name',
        'contact_person_id',
        'email',
        'phone',
        'password',
        'status',
        'preferred_language',
    ];

    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];

    protected function casts(): array
    {
        return [
            'user_type' => UserType::class,
            'status' => UserStatus::class,
            'email_verified_at' => 'datetime',
            'two_factor_confirmed_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    // Relationships
    public function consultations(): HasMany;
    public function timelines(): HasMany;
    public function notifications(): HasMany;
    public function adminLogs(): HasMany;

    // Scopes
    public function scopeActive(Builder $query): Builder;
    public function scopeClients(Builder $query): Builder;
    public function scopeIndividuals(Builder $query): Builder;
    public function scopeCompanies(Builder $query): Builder;

    // Helpers
    public function isAdmin(): bool;
    public function isClient(): bool;
    public function isIndividual(): bool;
    public function isCompany(): bool;
    public function canBookOnDate(Carbon $date): bool;
    public function hasBookingOnDate(Carbon $date): bool;
}
```

#### Consultation Model

```php
// app/Models/Consultation.php
class Consultation extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'booking_date',
        'booking_time',
        'problem_summary',
        'consultation_type',
        'payment_amount',
        'payment_status',
        'status',
        'admin_notes',
    ];

    protected function casts(): array
    {
        return [
            'booking_date' => 'date',
            'booking_time' => 'datetime:H:i',
            'consultation_type' => ConsultationType::class,
            'payment_status' => PaymentStatus::class,
            'status' => ConsultationStatus::class,
            'payment_amount' => 'decimal:2',
        ];
    }

    // Relationships
    public function user(): BelongsTo;

    // Scopes
    public function scopePending(Builder $query): Builder;
    public function scopeApproved(Builder $query): Builder;
    public function scopeUpcoming(Builder $query): Builder;
    public function scopeForDate(Builder $query, Carbon $date): Builder;
    public function scopeForDateRange(Builder $query, Carbon $start, Carbon $end): Builder;

    // Accessors
    public function getEndTimeAttribute(): Carbon;
    public function getStartDateTimeAttribute(): Carbon;
    public function getEndDateTimeAttribute(): Carbon;

    // Helpers
    public function isPaid(): bool;
    public function isPending(): bool;
    public function canBeApproved(): bool;
    public function canBeCancelled(): bool;
}
```

#### Timeline Model

```php
// app/Models/Timeline.php
class Timeline extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'case_name',
        'case_reference',
        'status',
    ];

    protected function casts(): array
    {
        return [
            'status' => TimelineStatus::class,
        ];
    }

    // Relationships
    public function user(): BelongsTo;
    public function updates(): HasMany;
    public function latestUpdate(): HasOne;

    // Scopes
    public function scopeActive(Builder $query): Builder;
    public function scopeArchived(Builder $query): Builder;
    public function scopeForUser(Builder $query, User $user): Builder;
}
```

#### Post Model

```php
// app/Models/Post.php
class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'body',
        'status',
        'published_at',
    ];

    protected function casts(): array
    {
        return [
            'title' => 'array',
            'body' => 'array',
            'status' => PostStatus::class,
            'published_at' => 'datetime',
        ];
    }

    // Accessors for localized content
    public function getTitleLocalizedAttribute(): string;
    public function getBodyLocalizedAttribute(): string;

    // Scopes
    public function scopePublished(Builder $query): Builder;
    public function scopeDraft(Builder $query): Builder;

    // Helpers
    public function isPublished(): bool;
}
```

### 4.3 Enums

```php
// app/Enums/UserType.php
enum UserType: string
{
    case Individual = 'individual';
    case Company = 'company';
    case Admin = 'admin';

    public function label(): string
    {
        return match($this) {
            self::Individual => __('enums.user_type.individual'),
            self::Company => __('enums.user_type.company'),
            self::Admin => __('enums.user_type.admin'),
        };
    }
}

// app/Enums/UserStatus.php
enum UserStatus: string
{
    case Active = 'active';
    case Deactivated = 'deactivated';
}

// app/Enums/ConsultationType.php
enum ConsultationType: string
{
    case Free = 'free';
    case Paid = 'paid';
}

// app/Enums/ConsultationStatus.php
enum ConsultationStatus: string
{
    case Pending = 'pending';
    case Approved = 'approved';
    case Rejected = 'rejected';
    case Completed = 'completed';
    case NoShow = 'no_show';
    case Cancelled = 'cancelled';

    public function color(): string
    {
        return match($this) {
            self::Pending => 'yellow',
            self::Approved => 'blue',
            self::Rejected => 'red',
            self::Completed => 'green',
            self::NoShow => 'gray',
            self::Cancelled => 'red',
        };
    }
}

// app/Enums/PaymentStatus.php
enum PaymentStatus: string
{
    case Pending = 'pending';
    case Received = 'received';
    case NotApplicable = 'na';
}

// app/Enums/TimelineStatus.php
enum TimelineStatus: string
{
    case Active = 'active';
    case Archived = 'archived';
}

// app/Enums/PostStatus.php
enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
}
```

---

## 5. Database Schema

### 5.1 Migration Order

1. `0001_01_01_000000_create_users_table.php` (modify existing)
2. `0001_01_01_000001_create_cache_table.php` (existing)
3. `0001_01_01_000002_create_jobs_table.php` (existing)
4. `2025_01_01_000001_add_profile_fields_to_users_table.php`
5. `2025_01_01_000002_create_consultations_table.php`
6. `2025_01_01_000003_create_timelines_table.php`
7. `2025_01_01_000004_create_timeline_updates_table.php`
8. `2025_01_01_000005_create_posts_table.php`
9. `2025_01_01_000006_create_working_hours_table.php`
10. `2025_01_01_000007_create_blocked_times_table.php`
11. `2025_01_01_000008_create_notifications_table.php`
12. `2025_01_01_000009_create_admin_logs_table.php`
13. `2025_01_01_000010_create_settings_table.php`

### 5.2 Key Migration Examples

#### Users Table Extension

```php
// database/migrations/2025_01_01_000001_add_profile_fields_to_users_table.php
public function up(): void
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('user_type')->default('individual')->after('id');
        $table->string('full_name')->nullable()->after('name');
        $table->string('national_id')->nullable()->after('full_name');
        $table->string('company_name')->nullable()->after('national_id');
        $table->string('company_cert_number')->nullable()->after('company_name');
        $table->string('contact_person_name')->nullable()->after('company_cert_number');
        $table->string('contact_person_id')->nullable()->after('contact_person_name');
        $table->string('phone', 20)->nullable()->after('email');
        $table->string('status')->default('active')->after('password');
        $table->string('preferred_language', 2)->default('ar')->after('status');

        $table->index('user_type');
        $table->index('status');
        $table->index(['user_type', 'status']);
    });
}
```

#### Consultations Table

```php
// database/migrations/2025_01_01_000002_create_consultations_table.php
public function up(): void
{
    Schema::create('consultations', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->cascadeOnDelete();
        $table->date('booking_date');
        $table->time('booking_time');
        $table->text('problem_summary');
        $table->string('consultation_type')->default('free');
        $table->decimal('payment_amount', 10, 2)->nullable();
        $table->string('payment_status')->default('na');
        $table->string('status')->default('pending');
        $table->text('admin_notes')->nullable();
        $table->timestamps();

        $table->index('booking_date');
        $table->index('status');
        $table->index(['booking_date', 'booking_time']);
        $table->unique(['user_id', 'booking_date']); // One booking per user per day
    });
}
```

#### Working Hours Table

```php
// database/migrations/2025_01_01_000006_create_working_hours_table.php
public function up(): void
{
    Schema::create('working_hours', function (Blueprint $table) {
        $table->id();
        $table->tinyInteger('day_of_week')->unsigned(); // 0=Sunday, 6=Saturday
        $table->time('start_time');
        $table->time('end_time');
        $table->boolean('is_active')->default(true);

        $table->unique('day_of_week');
    });
}
```

### 5.3 Indexes Strategy

| Table | Index | Columns | Purpose |
|-------|-------|---------|---------|
| users | user_type_status | user_type, status | Filter clients |
| consultations | booking_date | booking_date | Calendar queries |
| consultations | status | status | Filter by status |
| consultations | user_date_unique | user_id, booking_date | Enforce 1/day limit |
| timelines | user_status | user_id, status | Client timeline list |
| posts | status_published | status, published_at | Published posts list |
| admin_logs | target | target_type, target_id | Audit trail lookup |

---

## 6. Application Structure

### 6.1 Directory Structure

```
libra/
├── app/
│   ├── Actions/                      # Business logic (single-purpose)
│   │   ├── Fortify/                 # Auth actions (existing)
│   │   │   ├── CreateNewUser.php
│   │   │   ├── ResetUserPassword.php
│   │   │   └── UpdateUserPassword.php
│   │   ├── Consultation/
│   │   │   ├── CreateConsultationAction.php
│   │   │   ├── ApproveConsultationAction.php
│   │   │   ├── RejectConsultationAction.php
│   │   │   ├── CompleteConsultationAction.php
│   │   │   └── CancelConsultationAction.php
│   │   ├── Timeline/
│   │   │   ├── CreateTimelineAction.php
│   │   │   ├── AddTimelineUpdateAction.php
│   │   │   └── ArchiveTimelineAction.php
│   │   ├── User/
│   │   │   ├── CreateClientAction.php
│   │   │   ├── UpdateClientAction.php
│   │   │   ├── DeactivateClientAction.php
│   │   │   ├── ReactivateClientAction.php
│   │   │   ├── DeleteClientAction.php
│   │   │   └── ConvertClientTypeAction.php
│   │   ├── Post/
│   │   │   ├── CreatePostAction.php
│   │   │   ├── UpdatePostAction.php
│   │   │   ├── PublishPostAction.php
│   │   │   └── DeletePostAction.php
│   │   └── Export/
│   │       ├── ExportUsersAction.php
│   │       ├── ExportConsultationsAction.php
│   │       └── GenerateMonthlyReportAction.php
│   ├── Enums/                        # PHP 8.1+ enums
│   │   ├── UserType.php
│   │   ├── UserStatus.php
│   │   ├── ConsultationType.php
│   │   ├── ConsultationStatus.php
│   │   ├── PaymentStatus.php
│   │   ├── TimelineStatus.php
│   │   └── PostStatus.php
│   ├── Http/
│   │   ├── Controllers/              # Minimal controllers
│   │   │   ├── LanguageController.php
│   │   │   └── CalendarDownloadController.php
│   │   └── Middleware/
│   │       ├── SetLocale.php
│   │       ├── EnsureUserIsAdmin.php
│   │       └── EnsureUserIsActive.php
│   ├── Jobs/                         # Queue jobs
│   │   ├── SendWelcomeEmail.php
│   │   ├── SendBookingNotification.php
│   │   ├── SendConsultationReminder.php
│   │   ├── SendTimelineUpdateNotification.php
│   │   └── GenerateExportFile.php
│   ├── Livewire/                     # Full-page Livewire components (if any)
│   │   └── Forms/                    # Livewire form objects
│   │       ├── ConsultationForm.php
│   │       ├── ClientForm.php
│   │       ├── TimelineForm.php
│   │       └── PostForm.php
│   ├── Mail/                         # Mailable classes
│   │   ├── WelcomeMail.php
│   │   ├── BookingSubmittedMail.php
│   │   ├── BookingApprovedMail.php
│   │   ├── BookingRejectedMail.php
│   │   ├── ConsultationReminderMail.php
│   │   ├── TimelineUpdatedMail.php
│   │   └── NewBookingRequestMail.php
│   ├── Models/                       # Eloquent models
│   │   ├── User.php
│   │   ├── Consultation.php
│   │   ├── Timeline.php
│   │   ├── TimelineUpdate.php
│   │   ├── Post.php
│   │   ├── WorkingHour.php
│   │   ├── BlockedTime.php
│   │   ├── Notification.php
│   │   ├── AdminLog.php
│   │   └── Setting.php
│   ├── Observers/                    # Model observers
│   │   ├── ConsultationObserver.php
│   │   ├── TimelineUpdateObserver.php
│   │   └── UserObserver.php
│   ├── Policies/                     # Authorization policies
│   │   ├── ConsultationPolicy.php
│   │   ├── TimelinePolicy.php
│   │   ├── PostPolicy.php
│   │   └── UserPolicy.php
│   ├── Providers/
│   │   ├── AppServiceProvider.php
│   │   └── FortifyServiceProvider.php
│   └── Services/                     # Cross-cutting services
│       ├── AvailabilityService.php
│       ├── CalendarService.php
│       ├── CaptchaService.php
│       └── ExportService.php
├── config/
│   └── libra.php                     # App-specific config
├── database/
│   ├── factories/
│   │   ├── UserFactory.php
│   │   ├── ConsultationFactory.php
│   │   ├── TimelineFactory.php
│   │   ├── TimelineUpdateFactory.php
│   │   └── PostFactory.php
│   ├── migrations/
│   └── seeders/
│       ├── DatabaseSeeder.php
│       ├── AdminSeeder.php
│       ├── WorkingHoursSeeder.php
│       └── DemoDataSeeder.php
├── docs/
│   ├── architecture.md               # This document
│   ├── prd.md                        # Product requirements
│   ├── epics/                        # Epic definitions
│   └── stories/                      # User stories
├── resources/
│   ├── views/
│   │   ├── components/               # Blade components
│   │   │   ├── layouts/
│   │   │   │   ├── app.blade.php    # Main app layout
│   │   │   │   ├── guest.blade.php  # Guest/public layout
│   │   │   │   └── admin.blade.php  # Admin layout
│   │   │   └── ui/                   # Reusable UI components
│   │   │       ├── card.blade.php
│   │   │       ├── stat-card.blade.php
│   │   │       ├── status-badge.blade.php
│   │   │       └── language-switcher.blade.php
│   │   ├── emails/                   # Email templates
│   │   │   ├── welcome.blade.php
│   │   │   ├── booking-submitted.blade.php
│   │   │   ├── booking-approved.blade.php
│   │   │   ├── booking-rejected.blade.php
│   │   │   ├── consultation-reminder.blade.php
│   │   │   ├── timeline-updated.blade.php
│   │   │   ├── new-booking-request.blade.php
│   │   │   ├── guest-booking-submitted.blade.php
│   │   │   ├── guest-booking-approved.blade.php
│   │   │   └── guest-booking-rejected.blade.php
│   │   ├── livewire/                 # Volt single-file components
│   │   │   ├── admin/               # Admin area
│   │   │   │   ├── dashboard.blade.php
│   │   │   │   ├── users/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   ├── create.blade.php
│   │   │   │   │   └── edit.blade.php
│   │   │   │   ├── consultations/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   ├── pending.blade.php
│   │   │   │   │   ├── calendar.blade.php
│   │   │   │   │   └── show.blade.php
│   │   │   │   ├── timelines/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   ├── create.blade.php
│   │   │   │   │   └── show.blade.php
│   │   │   │   ├── posts/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   ├── create.blade.php
│   │   │   │   │   └── edit.blade.php
│   │   │   │   ├── settings/
│   │   │   │   │   ├── working-hours.blade.php
│   │   │   │   │   ├── blocked-times.blade.php
│   │   │   │   │   └── content-pages.blade.php
│   │   │   │   └── reports/
│   │   │   │       └── index.blade.php
│   │   │   ├── client/              # Client area
│   │   │   │   ├── dashboard.blade.php
│   │   │   │   ├── consultations/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   └── book.blade.php
│   │   │   │   ├── timelines/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   └── show.blade.php
│   │   │   │   └── profile.blade.php
│   │   │   ├── pages/               # Public pages
│   │   │   │   ├── home.blade.php
│   │   │   │   ├── booking.blade.php     # Guest booking form
│   │   │   │   ├── posts/
│   │   │   │   │   ├── index.blade.php
│   │   │   │   │   └── show.blade.php
│   │   │   │   ├── terms.blade.php
│   │   │   │   └── privacy.blade.php
│   │   │   ├── auth/                # Auth pages (existing)
│   │   │   └── settings/            # User settings (existing)
│   │   ├── pdf/                     # PDF templates
│   │   │   ├── users-export.blade.php
│   │   │   ├── consultations-export.blade.php
│   │   │   └── monthly-report.blade.php
│   │   └── errors/                  # Error pages
│   │       ├── 404.blade.php
│   │       ├── 403.blade.php
│   │       └── 500.blade.php
│   ├── css/
│   │   └── app.css                  # Tailwind entry point
│   ├── js/
│   │   └── app.js                   # Minimal JS
│   └── lang/                        # Translations
│       ├── ar/
│       │   ├── auth.php
│       │   ├── validation.php
│       │   ├── pagination.php
│       │   ├── passwords.php
│       │   ├── messages.php
│       │   ├── models.php
│       │   ├── enums.php
│       │   └── emails.php
│       └── en/
│           └── ... (same structure)
├── routes/
│   ├── web.php                      # Web routes
│   └── console.php                  # Scheduled commands
├── tests/
│   ├── Feature/
│   │   ├── Admin/
│   │   │   ├── DashboardTest.php
│   │   │   ├── UserManagementTest.php
│   │   │   ├── ConsultationManagementTest.php
│   │   │   ├── TimelineManagementTest.php
│   │   │   ├── PostManagementTest.php
│   │   │   └── SettingsTest.php
│   │   ├── Client/
│   │   │   ├── DashboardTest.php
│   │   │   ├── BookingTest.php
│   │   │   ├── TimelineViewTest.php
│   │   │   └── ProfileTest.php
│   │   ├── Public/
│   │   │   ├── HomePageTest.php
│   │   │   ├── GuestBookingTest.php
│   │   │   ├── PostsTest.php
│   │   │   └── LanguageSwitchTest.php
│   │   └── Auth/
│   │       └── ... (existing tests)
│   └── Unit/
│       ├── Actions/
│       ├── Models/
│       ├── Services/
│       └── Enums/
├── storage/
│   └── app/
│       └── exports/                 # Generated export files
├── .github/
│   └── workflows/
│       └── ci.yml                   # GitHub Actions CI
└── public/
    └── build/                       # Compiled assets
```

---

## 7. Authentication & Authorization

### 7.1 Authentication (Fortify)

**Current Configuration:**
- Email/password login
- Email verification
- Two-factor authentication (TOTP)
- Password reset

**Modifications Required:**
1. **Disable public registration** - Admin creates all users
2. **Custom login redirect** - Admin → `/admin/dashboard`, Client → `/client/dashboard`
3. **Admin-triggered password reset** - No public reset

```php
// config/fortify.php
'features' => [
    // Features::registration(), // DISABLED
    Features::resetPasswords(),
    Features::emailVerification(),
    Features::updateProfileInformation(),
    Features::updatePasswords(),
    Features::twoFactorAuthentication([
        'confirm' => true,
        'confirmPassword' => true,
    ]),
],
```

### 7.2 Authorization (Policies)

```php
// app/Policies/ConsultationPolicy.php
class ConsultationPolicy
{
    public function viewAny(User $user): bool
    {
        return true; // All authenticated users can see their own
    }

    public function view(User $user, Consultation $consultation): bool
    {
        return $user->isAdmin() || $consultation->user_id === $user->id;
    }

    public function create(User $user): bool
    {
        return $user->isClient();
    }

    public function update(User $user, Consultation $consultation): bool
    {
        return $user->isAdmin();
    }

    public function delete(User $user, Consultation $consultation): bool
    {
        return $user->isAdmin();
    }
}

// app/Policies/TimelinePolicy.php
class TimelinePolicy
{
    public function viewAny(User $user): bool
    {
        return true;
    }

    public function view(User $user, Timeline $timeline): bool
    {
        return $user->isAdmin() || $timeline->user_id === $user->id;
    }

    public function create(User $user): bool
    {
        return $user->isAdmin();
    }

    public function update(User $user, Timeline $timeline): bool
    {
        return $user->isAdmin();
    }

    public function addUpdate(User $user, Timeline $timeline): bool
    {
        return $user->isAdmin();
    }
}

// app/Policies/UserPolicy.php
class UserPolicy
{
    public function viewAny(User $user): bool
    {
        return $user->isAdmin();
    }

    public function view(User $user, User $model): bool
    {
        return $user->isAdmin() || $user->id === $model->id;
    }

    public function create(User $user): bool
    {
        return $user->isAdmin();
    }

    public function update(User $user, User $model): bool
    {
        return $user->isAdmin();
    }

    public function delete(User $user, User $model): bool
    {
        return $user->isAdmin() && !$model->isAdmin();
    }
}
```

### 7.3 Middleware

```php
// app/Http/Middleware/SetLocale.php
class SetLocale
{
    public function handle(Request $request, Closure $next): Response
    {
        $locale = session('locale')
            ?? $request->user()?->preferred_language
            ?? config('app.locale');

        if (!in_array($locale, ['ar', 'en'])) {
            $locale = 'ar';
        }

        app()->setLocale($locale);

        return $next($request);
    }
}

// app/Http/Middleware/EnsureUserIsAdmin.php
class EnsureUserIsAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        if (!$request->user()?->isAdmin()) {
            abort(403, __('messages.unauthorized'));
        }

        return $next($request);
    }
}

// app/Http/Middleware/EnsureUserIsActive.php
class EnsureUserIsActive
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->user()?->status === UserStatus::Deactivated) {
            Auth::logout();
            $request->session()->invalidate();
            $request->session()->regenerateToken();

            return redirect()->route('login')
                ->with('error', __('messages.account_deactivated'));
        }

        return $next($request);
    }
}
```

### 7.4 Middleware Registration

```php
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \App\Http\Middleware\SetLocale::class,
    ]);

    $middleware->alias([
        'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,
        'active' => \App\Http\Middleware\EnsureUserIsActive::class,
    ]);

    $middleware->validateCsrfTokens(except: [
        // Add any exceptions here
    ]);
})
```

---

## 8. Core Workflows

### 8.1 Consultation Booking Flow

```mermaid
sequenceDiagram
    participant C as Client
    participant V as Volt Component
    participant A as Action
    participant S as AvailabilityService
    participant M as Model
    participant Q as Queue
    participant E as Email (Admin)

    C->>V: Open booking page
    V->>S: getAvailableSlots(date)
    S->>M: Query working_hours, blocked_times, consultations
    S-->>V: Available time slots
    V-->>C: Display calendar

    C->>V: Select date/time, enter summary
    V->>V: Validate (date future, slot available, no existing booking)

    alt Validation fails
        V-->>C: Show error message
    else Validation passes
        V->>A: CreateConsultationAction
        A->>M: Check user has no booking on date
        A->>M: Create consultation (status: pending)
        A->>M: Log admin action
        M-->>A: Consultation created
        A->>Q: Dispatch SendBookingNotification (to admin)
        Q->>E: Email admin at no-reply@libra.ps
        A-->>V: Success
        V-->>C: Show confirmation message
    end
```

### 8.2 Admin Consultation Approval Flow

```mermaid
sequenceDiagram
    participant Ad as Admin
    participant V as Volt Component
    participant A as Action
    participant M as Model
    participant Q as Queue
    participant E as Email (Client)

    Ad->>V: View pending consultation
    Ad->>V: Set type (free/paid), amount if paid
    Ad->>V: Click Approve

    V->>A: ApproveConsultationAction
    A->>M: Update consultation (status: approved, type, amount)
    A->>M: Log admin action
    M-->>A: Updated

    A->>Q: Dispatch SendBookingApprovedMail (with .ics attachment)
    A->>Q: Schedule SendConsultationReminder (24h before)
    A->>Q: Schedule SendConsultationReminder (2h before)

    Q->>E: Send approval email to client
    A-->>V: Success
    V-->>Ad: Refresh list, show success toast
```

### 8.3 Timeline Update Flow

```mermaid
sequenceDiagram
    participant Ad as Admin
    participant V as Volt Component
    participant A as Action
    participant M as Model
    participant O as Observer
    participant Q as Queue
    participant E as Email

    Ad->>V: Navigate to timeline
    Ad->>V: Enter update text
    Ad->>V: Submit

    V->>A: AddTimelineUpdateAction
    A->>M: Create TimelineUpdate
    M->>O: TimelineUpdateObserver::created
    O->>Q: Dispatch SendTimelineUpdateNotification
    A->>M: Log admin action
    A-->>V: Success

    Q->>E: Send notification to client
    V-->>Ad: Refresh timeline, show new update
```

### 8.4 User Creation Flow

```mermaid
sequenceDiagram
    participant Ad as Admin
    participant V as Volt Component
    participant A as Action
    participant M as Model
    participant Q as Queue
    participant E as Email

    Ad->>V: Fill user form (type, name, email, etc.)
    Ad->>V: Set password
    Ad->>V: Submit

    V->>V: Validate form
    V->>A: CreateClientAction
    A->>M: Create User
    A->>M: Log admin action
    M-->>A: User created
    A->>Q: Dispatch SendWelcomeEmail (with credentials)
    Q->>E: Send welcome email to new user
    A-->>V: Success
    V-->>Ad: Redirect to user list
```

---

## 9. Routing Structure

### 9.1 Route Definitions

```php
// routes/web.php

use App\Http\Controllers\LanguageController;
use App\Http\Controllers\CalendarDownloadController;
use Illuminate\Support\Facades\Route;
use Livewire\Volt\Volt;

/*
|--------------------------------------------------------------------------
| Public Routes
|--------------------------------------------------------------------------
*/

// Home
Volt::route('/', 'pages.home')->name('home');

// Posts
Volt::route('/posts', 'pages.posts.index')->name('posts.index');
Volt::route('/posts/{post}', 'pages.posts.show')->name('posts.show');

// Legal pages
Volt::route('/terms', 'pages.terms')->name('terms');
Volt::route('/privacy', 'pages.privacy')->name('privacy');

// Language switch
Route::get('/language/{locale}', LanguageController::class)
    ->name('language.switch')
    ->where('locale', 'ar|en');

/*
|--------------------------------------------------------------------------
| Client Routes (Authenticated Non-Admin)
|--------------------------------------------------------------------------
*/

Route::middleware(['auth', 'verified', 'active'])->prefix('client')->name('client.')->group(function () {
    Volt::route('/dashboard', 'client.dashboard')->name('dashboard');

    // Consultations
    Volt::route('/consultations', 'client.consultations.index')->name('consultations.index');
    Volt::route('/consultations/book', 'client.consultations.book')->name('consultations.book');
    Route::get('/consultations/{consultation}/calendar', CalendarDownloadController::class)
        ->name('consultations.calendar');

    // Timelines
    Volt::route('/timelines', 'client.timelines.index')->name('timelines.index');
    Volt::route('/timelines/{timeline}', 'client.timelines.show')->name('timelines.show');

    // Profile (view only)
    Volt::route('/profile', 'client.profile')->name('profile');
});

/*
|--------------------------------------------------------------------------
| Admin Routes
|--------------------------------------------------------------------------
*/

Route::middleware(['auth', 'verified', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    Volt::route('/dashboard', 'admin.dashboard')->name('dashboard');

    // Users
    Volt::route('/users', 'admin.users.index')->name('users.index');
    Volt::route('/users/create', 'admin.users.create')->name('users.create');
    Volt::route('/users/{user}/edit', 'admin.users.edit')->name('users.edit');

    // Consultations
    Volt::route('/consultations', 'admin.consultations.index')->name('consultations.index');
    Volt::route('/consultations/pending', 'admin.consultations.pending')->name('consultations.pending');
    Volt::route('/consultations/calendar', 'admin.consultations.calendar')->name('consultations.calendar');
    Volt::route('/consultations/{consultation}', 'admin.consultations.show')->name('consultations.show');

    // Timelines
    Volt::route('/timelines', 'admin.timelines.index')->name('timelines.index');
    Volt::route('/timelines/create', 'admin.timelines.create')->name('timelines.create');
    Volt::route('/timelines/{timeline}', 'admin.timelines.show')->name('timelines.show');

    // Posts
    Volt::route('/posts', 'admin.posts.index')->name('posts.index');
    Volt::route('/posts/create', 'admin.posts.create')->name('posts.create');
    Volt::route('/posts/{post}/edit', 'admin.posts.edit')->name('posts.edit');

    // Settings
    Volt::route('/settings/working-hours', 'admin.settings.working-hours')->name('settings.working-hours');
    Volt::route('/settings/blocked-times', 'admin.settings.blocked-times')->name('settings.blocked-times');
    Volt::route('/settings/content-pages', 'admin.settings.content-pages')->name('settings.content-pages');

    // Reports
    Volt::route('/reports', 'admin.reports.index')->name('reports.index');
});
```

### 9.2 Route Summary

| Area | Route Prefix | Middleware | Description |
|------|--------------|------------|-------------|
| Public | `/` | none | Home, posts, legal pages |
| Client | `/client` | auth, verified, active | Client dashboard, bookings, timelines |
| Admin | `/admin` | auth, verified, admin | Full management interface |
| Auth | `/` (Fortify) | varies | Login, logout, password, 2FA |
| Settings | `/settings` | auth, verified | User profile settings |

---

## 10. Email System

### 10.1 Configuration

```php
// config/mail.php (via .env)
MAIL_MAILER=smtp
MAIL_HOST=smtp.libra.ps
MAIL_PORT=587
MAIL_USERNAME=no-reply@libra.ps
MAIL_PASSWORD=****
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@libra.ps
MAIL_FROM_NAME="${APP_NAME}"
```

### 10.2 Email Templates

| Email Class | Trigger | Recipient | Queue | Attachments |
|-------------|---------|-----------|-------|-------------|
| `WelcomeMail` | User created | Client | Yes | None |
| `BookingSubmittedMail` | Consultation created | Client | Yes | None |
| `BookingApprovedMail` | Consultation approved | Client | Yes | .ics file |
| `BookingRejectedMail` | Consultation rejected | Client | Yes | None |
| `ConsultationReminderMail` | 24h/2h before | Client | Scheduled | .ics file |
| `TimelineUpdatedMail` | Timeline update added | Client | Yes | None |
| `NewBookingRequestMail` | Consultation created | Admin | Yes | None |

### 10.3 Email Template Example

```php
// app/Mail/BookingApprovedMail.php
class BookingApprovedMail extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public function __construct(
        public Consultation $consultation
    ) {}

    public function envelope(): Envelope
    {
        $locale = $this->consultation->user->preferred_language;

        return new Envelope(
            subject: $locale === 'ar'
                ? 'تمت الموافقة على موعدك - مكتب ليبرا للمحاماة'
                : 'Your Consultation Approved - Libra Law Firm',
        );
    }

    public function content(): Content
    {
        return new Content(
            markdown: 'emails.booking-approved',
            with: [
                'consultation' => $this->consultation,
                'user' => $this->consultation->user,
                'locale' => $this->consultation->user->preferred_language,
            ],
        );
    }

    public function attachments(): array
    {
        $calendar = app(CalendarService::class);

        return [
            Attachment::fromData(
                fn () => $calendar->generateIcs($this->consultation),
                'consultation.ics'
            )->withMime('text/calendar'),
        ];
    }
}
```

### 10.4 Email Template Structure (Blade)

```blade
{{-- resources/views/emails/booking-approved.blade.php --}}
<x-mail::message>
@if($locale === 'ar')
# مرحباً {{ $user->full_name }}،

تمت الموافقة على موعد استشارتك.

**تفاصيل الموعد:**
- **التاريخ:** {{ $consultation->booking_date->format('d/m/Y') }}
- **الوقت:** {{ $consultation->booking_time->format('h:i A') }}
- **المدة:** 45 دقيقة
@if($consultation->consultation_type === 'paid')
- **نوع الاستشارة:** مدفوعة
- **المبلغ:** {{ $consultation->payment_amount }} شيكل
@else
- **نوع الاستشارة:** مجانية
@endif

يرجى إضافة الموعد إلى تقويمك باستخدام الملف المرفق.

@else
# Hello {{ $user->full_name }},

Your consultation has been approved.

**Appointment Details:**
- **Date:** {{ $consultation->booking_date->format('m/d/Y') }}
- **Time:** {{ $consultation->booking_time->format('h:i A') }}
- **Duration:** 45 minutes
@if($consultation->consultation_type === 'paid')
- **Type:** Paid Consultation
- **Amount:** {{ $consultation->payment_amount }} ILS
@else
- **Type:** Free Consultation
@endif

Please add this appointment to your calendar using the attached file.
@endif

<x-mail::button :url="route('client.consultations.index')">
{{ $locale === 'ar' ? 'عرض موعدي' : 'View My Consultation' }}
</x-mail::button>

{{ $locale === 'ar' ? 'مع تحيات،' : 'Best regards,' }}<br>
{{ config('app.name') }}
</x-mail::message>
```

---

## 11. Background Jobs & Scheduling

### 11.1 Queue Configuration

```php
// config/queue.php
'default' => env('QUEUE_CONNECTION', 'database'),

'connections' => [
    'database' => [
        'driver' => 'database',
        'connection' => null,
        'table' => 'jobs',
        'queue' => 'default',
        'retry_after' => 90,
        'after_commit' => true,
    ],
],
```

### 11.2 Queue Jobs

| Job | Purpose | Queue | Retries | Timeout |
|-----|---------|-------|---------|---------|
| `SendWelcomeEmail` | Welcome email to new user | `emails` | 3 | 60s |
| `SendBookingNotification` | Notify admin of new booking | `emails` | 3 | 60s |
| `SendBookingApprovedMail` | Approval email with .ics | `emails` | 3 | 60s |
| `SendConsultationReminder` | Reminder 24h/2h before | `reminders` | 3 | 60s |
| `SendTimelineUpdateNotification` | Timeline update notification | `emails` | 3 | 60s |
| `GenerateExportFile` | Generate CSV/PDF export | `exports` | 1 | 300s |

### 11.3 Job Example

```php
// app/Jobs/SendConsultationReminder.php
class SendConsultationReminder implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $timeout = 60;
    public string $queue = 'reminders';

    public function __construct(
        public Consultation $consultation,
        public string $reminderType // '24h' or '2h'
    ) {}

    public function handle(): void
    {
        // Don't send if consultation is no longer approved
        if ($this->consultation->status !== ConsultationStatus::Approved) {
            return;
        }

        Mail::to($this->consultation->user)
            ->send(new ConsultationReminderMail(
                $this->consultation,
                $this->reminderType
            ));
    }

    public function failed(\Throwable $exception): void
    {
        Log::error('Failed to send consultation reminder', [
            'consultation_id' => $this->consultation->id,
            'reminder_type' => $this->reminderType,
            'error' => $exception->getMessage(),
        ]);
    }
}
```

### 11.4 Scheduled Tasks

```php
// routes/console.php
use Illuminate\Support\Facades\Schedule;
use App\Models\Consultation;
use App\Jobs\SendConsultationReminder;
use App\Enums\ConsultationStatus;

// Send 24-hour reminders at 9 AM Gaza time
Schedule::call(function () {
    $tomorrow = now()->addDay()->toDateString();

    Consultation::where('status', ConsultationStatus::Approved)
        ->where('booking_date', $tomorrow)
        ->each(function ($consultation) {
            SendConsultationReminder::dispatch($consultation, '24h');
        });
})->dailyAt('09:00')->timezone('Asia/Gaza');

// Send 2-hour reminders (check every 15 minutes)
Schedule::call(function () {
    $targetTime = now()->addHours(2);

    Consultation::where('status', ConsultationStatus::Approved)
        ->where('booking_date', $targetTime->toDateString())
        ->whereBetween('booking_time', [
            $targetTime->subMinutes(7)->format('H:i:s'),
            $targetTime->addMinutes(8)->format('H:i:s'),
        ])
        ->each(function ($consultation) {
            SendConsultationReminder::dispatch($consultation, '2h');
        });
})->everyFifteenMinutes()->timezone('Asia/Gaza');

// Prune old failed jobs
Schedule::command('queue:prune-failed --hours=168')->weekly();

// Clear old cache
Schedule::command('cache:prune-stale-tags')->hourly();
```

### 11.5 Supervisor Configuration

```ini
; /etc/supervisor/conf.d/libra-worker.conf
[program:libra-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/libra/artisan queue:work database --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/libra/storage/logs/worker.log
stopwaitsecs=3600
```

---

## 12. Localization & Timezone

### 12.1 Timezone Configuration

**Application Timezone:** `Asia/Gaza` (Palestine)

```php
// config/app.php
'timezone' => 'Asia/Gaza',

// config/libra.php
return [
    'timezone' => 'Asia/Gaza',
    'date_formats' => [
        'ar' => 'd/m/Y',      // 21/12/2025
        'en' => 'm/d/Y',      // 12/21/2025
    ],
    'time_format' => 'h:i A', // 02:30 PM (both languages)
    'datetime_formats' => [
        'ar' => 'd/m/Y h:i A',
        'en' => 'm/d/Y h:i A',
    ],
];
```

### 12.2 Date/Time Helper

```php
// app/Helpers/DateTimeHelper.php
class DateTimeHelper
{
    public static function formatDate(Carbon $date, ?string $locale = null): string
    {
        $locale = $locale ?? app()->getLocale();
        $format = config("libra.date_formats.{$locale}", 'd/m/Y');

        return $date->format($format);
    }

    public static function formatTime(Carbon $time): string
    {
        return $time->format(config('libra.time_format'));
    }

    public static function formatDateTime(Carbon $datetime, ?string $locale = null): string
    {
        $locale = $locale ?? app()->getLocale();
        $format = config("libra.datetime_formats.{$locale}");

        return $datetime->format($format);
    }

    public static function parseUserDate(string $date, ?string $locale = null): Carbon
    {
        $locale = $locale ?? app()->getLocale();
        $format = config("libra.date_formats.{$locale}");

        return Carbon::createFromFormat($format, $date, config('libra.timezone'));
    }
}
```

### 12.3 Localization Structure

```
resources/lang/
├── ar/
│   ├── auth.php          # Authentication messages
│   ├── validation.php    # Validation messages
│   ├── pagination.php    # Pagination
│   ├── passwords.php     # Password reset messages
│   ├── messages.php      # General app messages
│   ├── models.php        # Model field labels
│   ├── enums.php         # Enum labels
│   └── emails.php        # Email content
└── en/
    └── ... (same structure)
```

### 12.4 RTL/LTR Support

```blade
{{-- resources/views/components/layouts/app.blade.php --}}
<!DOCTYPE html>
<html
    lang="{{ app()->getLocale() }}"
    dir="{{ app()->getLocale() === 'ar' ? 'rtl' : 'ltr' }}"
    class="{{ app()->getLocale() === 'ar' ? 'font-arabic' : 'font-english' }}"
>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ $title ?? config('app.name') }}</title>

    {{-- Fonts --}}
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&family=Montserrat:wght@300;400;600;700&display=swap" rel="stylesheet">

    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
```

```css
/* resources/css/app.css */
@import "tailwindcss";

@theme {
    --font-arabic: 'Cairo', sans-serif;
    --font-english: 'Montserrat', sans-serif;

    /* Brand colors - Updated in Epic 10 */
    --color-charcoal: #4A4A42;
    --color-warm-gray: #C9C4BA;
    --color-off-white: #E8E4DC;
    --color-deep-black: #1A1A1A;
}

/* RTL utilities */
[dir="rtl"] .rtl\:space-x-reverse > :not([hidden]) ~ :not([hidden]) {
    --tw-space-x-reverse: 1;
}

.font-arabic {
    font-family: var(--font-arabic);
}

.font-english {
    font-family: var(--font-english);
}
```

---

## 13. Security

### 13.1 Security Measures

| Measure | Implementation | Configuration |
|---------|----------------|---------------|
| **CSRF Protection** | Laravel default | All POST/PUT/DELETE forms |
| **XSS Prevention** | Blade escaping `{{ }}` | Default for all output |
| **SQL Injection** | Eloquent ORM | Parameterized queries |
| **Password Hashing** | bcrypt | Laravel default |
| **Session Security** | Secure cookies | `SESSION_SECURE_COOKIE=true` |
| **Rate Limiting** | Throttle middleware | 5 login attempts/minute |
| **Input Validation** | Form Requests | All user input |
| **HTTPS** | Forced in production | `APP_URL=https://` |
| **CSP Headers** | Middleware | Restrictive policy |

### 13.2 Rate Limiting

```php
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->throttleApi();
})

// app/Providers/AppServiceProvider.php
public function boot(): void
{
    RateLimiter::for('login', function (Request $request) {
        return Limit::perMinute(5)->by($request->ip());
    });

    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
}
```

### 13.3 Content Security Policy

```php
// app/Http/Middleware/ContentSecurityPolicy.php
class ContentSecurityPolicy
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set('Content-Security-Policy', implode('; ', [
            "default-src 'self'",
            "script-src 'self' 'unsafe-inline' 'unsafe-eval'", // Required for Livewire
            "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
            "font-src 'self' https://fonts.gstatic.com",
            "img-src 'self' data: https:",
            "connect-src 'self'",
            "frame-ancestors 'none'",
        ]));

        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'DENY');
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');

        return $response;
    }
}
```

### 13.4 Sensitive Data Handling

```php
// app/Models/User.php
// Encrypt sensitive fields
protected function nationalId(): Attribute
{
    return Attribute::make(
        get: fn (?string $value) => $value ? decrypt($value) : null,
        set: fn (?string $value) => $value ? encrypt($value) : null,
    );
}

// Never expose in API/exports
protected $hidden = [
    'password',
    'remember_token',
    'two_factor_secret',
    'two_factor_recovery_codes',
    'national_id', // Encrypted, don't expose
];
```

### 13.5 Audit Logging

```php
// app/Observers/UserObserver.php
class UserObserver
{
    public function created(User $user): void
    {
        $this->log('created', $user);
    }

    public function updated(User $user): void
    {
        if ($user->wasChanged()) {
            $this->log('updated', $user, $user->getOriginal(), $user->getAttributes());
        }
    }

    public function deleted(User $user): void
    {
        $this->log('deleted', $user);
    }

    private function log(string $action, User $user, ?array $old = null, ?array $new = null): void
    {
        AdminLog::create([
            'admin_id' => auth()->id(),
            'action' => $action,
            'target_type' => User::class,
            'target_id' => $user->id,
            'old_values' => $old ? $this->filterSensitive($old) : null,
            'new_values' => $new ? $this->filterSensitive($new) : null,
            'ip_address' => request()->ip(),
        ]);
    }

    private function filterSensitive(array $values): array
    {
        unset(
            $values['password'],
            $values['remember_token'],
            $values['two_factor_secret'],
            $values['two_factor_recovery_codes']
        );

        return $values;
    }
}
```

---

## 14. Testing Strategy

### 14.1 Test Organization

```
tests/
├── Feature/                    # Integration tests
│   ├── Admin/
│   │   ├── DashboardTest.php
│   │   ├── UserManagementTest.php
│   │   ├── ConsultationManagementTest.php
│   │   ├── TimelineManagementTest.php
│   │   ├── PostManagementTest.php
│   │   └── SettingsTest.php
│   ├── Client/
│   │   ├── DashboardTest.php
│   │   ├── BookingTest.php
│   │   ├── TimelineViewTest.php
│   │   └── ProfileTest.php
│   ├── Public/
│   │   ├── HomePageTest.php
│   │   ├── GuestBookingTest.php
│   │   ├── PostsTest.php
│   │   └── LanguageSwitchTest.php
│   ├── Auth/                   # Existing auth tests
│   ├── Email/
│   │   └── NotificationTest.php
│   └── Jobs/
│       └── QueueJobsTest.php
└── Unit/
    ├── Actions/
    │   ├── CreateConsultationActionTest.php
    │   └── ApproveConsultationActionTest.php
    ├── Models/
    │   ├── UserTest.php
    │   ├── ConsultationTest.php
    │   └── TimelineTest.php
    ├── Services/
    │   ├── AvailabilityServiceTest.php
    │   ├── CalendarServiceTest.php
    │   └── CaptchaServiceTest.php
    └── Enums/
        └── ConsultationStatusTest.php
```

### 14.2 Test Examples

```php
// tests/Feature/Client/BookingTest.php
<?php

use App\Models\User;
use App\Models\Consultation;
use App\Models\WorkingHour;
use App\Enums\ConsultationStatus;
use App\Jobs\SendBookingNotification;
use Livewire\Volt\Volt;
use Illuminate\Support\Facades\Queue;

beforeEach(function () {
    // Seed working hours
    WorkingHour::factory()->weekdays()->create();
});

test('client can book consultation on available date', function () {
    Queue::fake();

    $client = User::factory()->client()->create();
    $bookingDate = now()->addDays(3)->startOfDay();

    // Ensure it's a weekday
    while ($bookingDate->isWeekend()) {
        $bookingDate->addDay();
    }

    Volt::test('client.consultations.book')
        ->actingAs($client)
        ->set('form.booking_date', $bookingDate->format('Y-m-d'))
        ->set('form.booking_time', '10:00')
        ->set('form.problem_summary', 'I need legal advice regarding a contract dispute.')
        ->call('submit')
        ->assertHasNoErrors()
        ->assertDispatched('notify');

    expect(Consultation::where('user_id', $client->id)->exists())->toBeTrue();

    $consultation = Consultation::where('user_id', $client->id)->first();
    expect($consultation->status)->toBe(ConsultationStatus::Pending);
    expect($consultation->booking_date->format('Y-m-d'))->toBe($bookingDate->format('Y-m-d'));

    Queue::assertPushed(SendBookingNotification::class);
});

test('client cannot book more than once per day', function () {
    $client = User::factory()->client()->create();
    $bookingDate = now()->addDays(3);

    // Create existing booking
    Consultation::factory()->for($client)->create([
        'booking_date' => $bookingDate,
    ]);

    Volt::test('client.consultations.book')
        ->actingAs($client)
        ->set('form.booking_date', $bookingDate->format('Y-m-d'))
        ->set('form.booking_time', '14:00')
        ->set('form.problem_summary', 'Another consultation request.')
        ->call('submit')
        ->assertHasErrors(['form.booking_date']);

    expect(Consultation::where('user_id', $client->id)->count())->toBe(1);
});

test('client cannot book on blocked date', function () {
    $client = User::factory()->client()->create();
    $blockedDate = now()->addDays(5);

    BlockedTime::factory()->create([
        'block_date' => $blockedDate,
        'start_time' => null, // Full day block
        'end_time' => null,
    ]);

    Volt::test('client.consultations.book')
        ->actingAs($client)
        ->set('form.booking_date', $blockedDate->format('Y-m-d'))
        ->set('form.booking_time', '10:00')
        ->set('form.problem_summary', 'Test booking.')
        ->call('submit')
        ->assertHasErrors(['form.booking_date']);
});
```

```php
// tests/Feature/Admin/UserManagementTest.php
<?php

use App\Models\User;
use App\Enums\UserType;
use App\Enums\UserStatus;
use App\Jobs\SendWelcomeEmail;
use Livewire\Volt\Volt;
use Illuminate\Support\Facades\Queue;

test('admin can create individual client', function () {
    Queue::fake();

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

    Volt::test('admin.users.create')
        ->actingAs($admin)
        ->set('form.user_type', 'individual')
        ->set('form.full_name', 'Ahmad Hassan')
        ->set('form.national_id', '123456789')
        ->set('form.email', 'ahmad@example.com')
        ->set('form.phone', '+970591234567')
        ->set('form.password', 'SecurePass123!')
        ->set('form.password_confirmation', 'SecurePass123!')
        ->set('form.preferred_language', 'ar')
        ->call('submit')
        ->assertHasNoErrors()
        ->assertRedirect(route('admin.users.index'));

    $user = User::where('email', 'ahmad@example.com')->first();

    expect($user)->not->toBeNull()
        ->and($user->user_type)->toBe(UserType::Individual)
        ->and($user->status)->toBe(UserStatus::Active);

    Queue::assertPushed(SendWelcomeEmail::class);
});

test('admin can deactivate client', function () {
    $admin = User::factory()->admin()->create();
    $client = User::factory()->client()->create();

    Volt::test('admin.users.edit', ['user' => $client])
        ->actingAs($admin)
        ->call('deactivate')
        ->assertHasNoErrors();

    $client->refresh();
    expect($client->status)->toBe(UserStatus::Deactivated);
});

test('admin cannot delete another admin', function () {
    $admin1 = User::factory()->admin()->create();
    $admin2 = User::factory()->admin()->create();

    Volt::test('admin.users.edit', ['user' => $admin2])
        ->actingAs($admin1)
        ->call('delete')
        ->assertForbidden();
});
```

### 14.3 Coverage Targets

| Area | Target | Priority |
|------|--------|----------|
| Actions | 95% | High |
| Models (methods) | 90% | High |
| Policies | 100% | High |
| Services | 90% | High |
| Volt Components | 80% | Medium |
| Critical user flows | 100% | Critical |

### 14.4 Running Tests

```bash
# Run all tests
php artisan test

# Run specific test file
php artisan test tests/Feature/Client/BookingTest.php

# Run tests matching filter
php artisan test --filter=booking

# Run with coverage
php artisan test --coverage --min=80

# Run parallel
php artisan test --parallel
```

---

## 15. Performance & Optimization

### 15.1 Performance Targets

| Metric | Target | Measurement |
|--------|--------|-------------|
| Page Load (TTFB) | < 200ms | Server response time |
| Full Page Load | < 3s | Including assets |
| Database Queries | < 20 per page | Query monitoring |
| Memory Usage | < 128MB | Per request |
| Queue Job Processing | < 5s | For email jobs |

### 15.2 Database Optimization

```php
// Eager loading in components
public function mount(): void
{
    $this->consultations = Consultation::query()
        ->with('user:id,full_name,email,phone')
        ->where('status', ConsultationStatus::Pending)
        ->orderBy('created_at', 'desc')
        ->get();
}

// Chunking for large operations
User::query()
    ->where('status', UserStatus::Active)
    ->chunk(100, function ($users) {
        foreach ($users as $user) {
            // Process
        }
    });
```

### 15.3 Caching Strategy

| Cache | TTL | Key Pattern | Invalidation |
|-------|-----|-------------|--------------|
| Working Hours | 1 hour | `working_hours:all` | On update |
| Blocked Times | 1 hour | `blocked_times:{month}` | On update |
| Dashboard Stats | 5 minutes | `admin:stats:{date}` | Time-based |
| Published Posts | 10 minutes | `posts:published` | On publish |
| User Permissions | 1 hour | `user:{id}:permissions` | On role change |

```php
// app/Services/AvailabilityService.php
public function getWorkingHours(): Collection
{
    return Cache::remember('working_hours:all', now()->addHour(), function () {
        return WorkingHour::where('is_active', true)->get();
    });
}

public function invalidateWorkingHoursCache(): void
{
    Cache::forget('working_hours:all');
}
```

### 15.4 Asset Optimization

```js
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
    build: {
        rollupOptions: {
            output: {
                manualChunks: {
                    vendor: ['alpinejs'],
                },
            },
        },
    },
});
```

---

## 16. Deployment & CI/CD

### 16.1 GitHub Actions CI Pipeline

```yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  tests:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite
          coverage: xdebug

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache composer dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install PHP dependencies
        run: composer install --no-interaction --prefer-dist --optimize-autoloader

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install NPM dependencies
        run: npm ci

      - name: Build assets
        run: npm run build

      - name: Prepare Laravel
        run: |
          cp .env.example .env
          php artisan key:generate
          touch database/database.sqlite

      - name: Run Pint (code style)
        run: vendor/bin/pint --test

      - name: Run tests
        run: php artisan test --parallel --coverage --min=80
        env:
          DB_CONNECTION: sqlite
          DB_DATABASE: database/database.sqlite

  deploy-staging:
    needs: tests
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to staging
        run: echo "Deploy to staging server"
        # Add actual deployment steps

  deploy-production:
    needs: tests
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy to production
        run: echo "Deploy to production server"
        # Add actual deployment steps
```

### 16.2 Deployment Script

```bash
#!/bin/bash
# deploy.sh - Production deployment script

set -e

echo "🚀 Starting deployment..."

# Variables
APP_DIR="/var/www/libra"
BACKUP_DIR="/var/www/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Create backup
echo "📦 Creating backup..."
mysqldump -u libra_user -p libra > "${BACKUP_DIR}/libra_${TIMESTAMP}.sql"

# Pull latest code
echo "📥 Pulling latest code..."
cd $APP_DIR
git fetch origin main
git reset --hard origin/main

# Install dependencies
echo "📚 Installing dependencies..."
composer install --no-dev --optimize-autoloader --no-interaction
npm ci
npm run build

# Run migrations
echo "🗃️ Running migrations..."
php artisan migrate --force

# Clear and rebuild caches
echo "🔄 Rebuilding caches..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

# Restart queue workers
echo "👷 Restarting queue workers..."
php artisan queue:restart

# Set permissions
echo "🔒 Setting permissions..."
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

echo "✅ Deployment complete!"
```

### 16.3 Environment Configuration

```bash
# Production .env
APP_NAME="Libra Law Firm"
APP_ENV=production
APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
APP_DEBUG=false
APP_TIMEZONE=Asia/Gaza
APP_URL=https://libra.ps

LOG_CHANNEL=daily
LOG_LEVEL=warning

DB_CONNECTION=mariadb
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=libra
DB_USERNAME=libra_user
DB_PASSWORD=secure_password_here

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_SECURE_COOKIE=true

MAIL_MAILER=smtp
MAIL_HOST=smtp.libra.ps
MAIL_PORT=587
MAIL_USERNAME=no-reply@libra.ps
MAIL_PASSWORD=secure_password_here
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@libra.ps
MAIL_FROM_NAME="Libra Law Firm"
```

### 16.4 Server Requirements

| Component | Requirement |
|-----------|-------------|
| OS | Rocky Linux 9 |
| PHP | 8.4+ with extensions: BCMath, Ctype, Fileinfo, JSON, Mbstring, OpenSSL, PDO, PDO_MySQL, Tokenizer, XML, cURL |
| Database | MariaDB 10.11+ |
| Web Server | Nginx 1.24+ |
| Process Manager | Supervisor (for queue workers) |
| Cron | For Laravel scheduler |
| SSL | Let's Encrypt or client-provided |

---

## 17. Monitoring & Alerting

### 17.1 Logging Configuration

```php
// config/logging.php
'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['daily', 'slack'],
        'ignore_exceptions' => false,
    ],

    'daily' => [
        'driver' => 'daily',
        'path' => storage_path('logs/laravel.log'),
        'level' => env('LOG_LEVEL', 'debug'),
        'days' => 14,
        'replace_placeholders' => true,
    ],

    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => 'Libra Alert',
        'emoji' => ':boom:',
        'level' => 'error',
    ],

    'queue' => [
        'driver' => 'daily',
        'path' => storage_path('logs/queue.log'),
        'level' => 'info',
        'days' => 7,
    ],

    'audit' => [
        'driver' => 'daily',
        'path' => storage_path('logs/audit.log'),
        'level' => 'info',
        'days' => 90,
    ],
],
```

### 17.2 Alerting Thresholds

| Alert | Condition | Channel | Severity |
|-------|-----------|---------|----------|
| Application Error | Any exception | Slack | High |
| Failed Job | 3 consecutive failures | Slack, Email | High |
| High Error Rate | > 10 errors/minute | Slack | Critical |
| Queue Backup | > 100 pending jobs | Slack | Medium |
| Disk Space | < 10% free | Email | High |
| Database Connection | Connection failure | Slack, Email | Critical |

### 17.3 Health Check Endpoint

```php
// routes/web.php
Route::get('/health', function () {
    $checks = [
        'app' => true,
        'database' => false,
        'cache' => false,
        'queue' => false,
    ];

    try {
        DB::connection()->getPdo();
        $checks['database'] = true;
    } catch (\Exception $e) {
        Log::error('Health check: Database connection failed', ['error' => $e->getMessage()]);
    }

    try {
        Cache::put('health-check', true, 10);
        $checks['cache'] = Cache::get('health-check') === true;
    } catch (\Exception $e) {
        Log::error('Health check: Cache failed', ['error' => $e->getMessage()]);
    }

    try {
        $pendingJobs = DB::table('jobs')->count();
        $checks['queue'] = $pendingJobs < 1000;
        $checks['queue_pending'] = $pendingJobs;
    } catch (\Exception $e) {
        Log::error('Health check: Queue check failed', ['error' => $e->getMessage()]);
    }

    $allHealthy = !in_array(false, $checks, true);

    return response()->json([
        'status' => $allHealthy ? 'healthy' : 'unhealthy',
        'timestamp' => now()->toIso8601String(),
        'checks' => $checks,
    ], $allHealthy ? 200 : 503);
})->name('health');
```

### 17.4 Application Metrics

```php
// app/Console/Commands/CollectMetrics.php
class CollectMetrics extends Command
{
    protected $signature = 'metrics:collect';
    protected $description = 'Collect application metrics for monitoring';

    public function handle(): void
    {
        $metrics = [
            'users' => [
                'total' => User::count(),
                'active' => User::where('status', UserStatus::Active)->count(),
                'new_this_month' => User::where('created_at', '>=', now()->startOfMonth())->count(),
            ],
            'consultations' => [
                'pending' => Consultation::where('status', ConsultationStatus::Pending)->count(),
                'today' => Consultation::where('booking_date', today())->count(),
                'this_month' => Consultation::where('booking_date', '>=', now()->startOfMonth())->count(),
            ],
            'queue' => [
                'pending_jobs' => DB::table('jobs')->count(),
                'failed_jobs' => DB::table('failed_jobs')->count(),
            ],
            'system' => [
                'disk_free_percent' => round((disk_free_space('/') / disk_total_space('/')) * 100, 2),
            ],
        ];

        Log::channel('metrics')->info('Application metrics', $metrics);

        // Alert if thresholds exceeded
        if ($metrics['queue']['pending_jobs'] > 100) {
            Log::channel('slack')->warning('Queue backup detected', $metrics['queue']);
        }

        if ($metrics['system']['disk_free_percent'] < 10) {
            Log::channel('slack')->error('Low disk space', $metrics['system']);
        }
    }
}

// Schedule in routes/console.php
Schedule::command('metrics:collect')->everyFiveMinutes();
```

---

## 18. Error Handling & Graceful Degradation

### 18.1 Exception Handling

```php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    // Don't report certain exceptions
    $exceptions->dontReport([
        \Illuminate\Auth\AuthenticationException::class,
        \Illuminate\Validation\ValidationException::class,
    ]);

    // Custom rendering for specific exceptions
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('admin/*')) {
            return response()->view('errors.admin-404', [], 404);
        }
        if ($request->is('client/*')) {
            return response()->view('errors.client-404', [], 404);
        }
        return response()->view('errors.404', [], 404);
    });

    $exceptions->render(function (AuthorizationException $e, Request $request) {
        return response()->view('errors.403', [
            'message' => $e->getMessage(),
        ], 403);
    });

    // Report to external service
    $exceptions->reportable(function (\Throwable $e) {
        if (app()->bound('sentry')) {
            app('sentry')->captureException($e);
        }
    });
})
```

### 18.2 Graceful Degradation Strategy

| Failure | Impact | Degradation Strategy |
|---------|--------|---------------------|
| **Email Service Down** | Notifications not sent | Queue emails, retry with backoff, log failures, continue operation |
| **Cache Unavailable** | Slower responses | Fallback to database queries, log warning |
| **Queue Worker Down** | Delayed processing | Jobs remain in database, process when restored |
| **Database Slow** | Slow page loads | Show cached data where possible, timeout long queries |

### 18.3 Email Failure Handling

```php
// app/Jobs/SendBookingNotification.php
class SendBookingNotification implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public array $backoff = [60, 300, 900]; // 1min, 5min, 15min

    public function handle(): void
    {
        Mail::to(config('libra.admin_email'))
            ->send(new NewBookingRequestMail($this->consultation));
    }

    public function failed(\Throwable $exception): void
    {
        // Log the failure
        Log::error('Failed to send booking notification', [
            'consultation_id' => $this->consultation->id,
            'attempts' => $this->attempts(),
            'error' => $exception->getMessage(),
        ]);

        // Create in-app notification as fallback
        Notification::create([
            'user_id' => User::where('user_type', UserType::Admin)->first()?->id,
            'type' => 'booking_notification_failed',
            'data' => [
                'consultation_id' => $this->consultation->id,
                'message' => 'Email notification failed - please check manually',
            ],
        ]);
    }
}
```

### 18.4 Livewire Error Handling

```php
// In Volt components
public function submit(): void
{
    try {
        $this->validate();

        $result = app(CreateConsultationAction::class)->execute(
            $this->user,
            $this->form->toArray()
        );

        $this->dispatch('notify', [
            'type' => 'success',
            'message' => __('messages.booking_submitted'),
        ]);

        $this->redirect(route('client.consultations.index'));

    } catch (ValidationException $e) {
        throw $e; // Let Livewire handle validation

    } catch (BookingLimitExceededException $e) {
        $this->addError('form.booking_date', $e->getMessage());

    } catch (\Exception $e) {
        report($e); // Log the error

        $this->dispatch('notify', [
            'type' => 'error',
            'message' => __('messages.general_error'),
        ]);
    }
}
```

---

## 19. Rollback & Recovery Procedures

### 19.1 Pre-Deployment Checklist

```markdown
## Pre-Deployment Checklist

- [ ] Database backup completed
- [ ] Code backup/tag created
- [ ] All tests passing on CI
- [ ] Staging environment verified
- [ ] Rollback procedure reviewed
- [ ] Team notified of deployment window
```

### 19.2 Rollback Procedure

```bash
#!/bin/bash
# rollback.sh - Emergency rollback script

set -e

echo "🔄 Starting rollback..."

APP_DIR="/var/www/libra"
BACKUP_DIR="/var/www/backups"

# Get the previous commit or tag
PREVIOUS_VERSION=${1:-$(git describe --tags --abbrev=0 HEAD^)}

echo "Rolling back to: ${PREVIOUS_VERSION}"

# Step 1: Put application in maintenance mode
echo "🚧 Enabling maintenance mode..."
cd $APP_DIR
php artisan down --render="errors::503" --retry=60

# Step 2: Restore database (if needed)
read -p "Restore database backup? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    echo "📦 Restoring database..."
    LATEST_BACKUP=$(ls -t ${BACKUP_DIR}/libra_*.sql | head -1)
    mysql -u libra_user -p libra < $LATEST_BACKUP
    echo "Database restored from: ${LATEST_BACKUP}"
fi

# Step 3: Rollback code
echo "⏪ Rolling back code..."
git fetch --all --tags
git checkout $PREVIOUS_VERSION

# Step 4: Install dependencies for that version
echo "📚 Installing dependencies..."
composer install --no-dev --optimize-autoloader --no-interaction
npm ci
npm run build

# Step 5: Run any rollback migrations (if applicable)
# Note: Be careful with this - may need manual intervention
# php artisan migrate:rollback --step=1

# Step 6: Clear caches
echo "🔄 Clearing caches..."
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear

# Step 7: Rebuild caches
echo "🔄 Rebuilding caches..."
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Step 8: Restart queue workers
echo "👷 Restarting queue workers..."
php artisan queue:restart

# Step 9: Bring application back up
echo "✅ Disabling maintenance mode..."
php artisan up

echo "🎉 Rollback complete to version: ${PREVIOUS_VERSION}"
echo "⚠️  Please verify the application is working correctly!"
```

### 19.3 Database Migration Rollback

```bash
# Rollback last migration
php artisan migrate:rollback --step=1

# Rollback to specific batch
php artisan migrate:rollback --batch=5

# Check migration status
php artisan migrate:status
```

### 19.4 Recovery Scenarios

| Scenario | Recovery Steps |
|----------|---------------|
| **Failed Deployment** | 1. Enable maintenance mode, 2. Run rollback.sh, 3. Verify functionality |
| **Database Corruption** | 1. Enable maintenance mode, 2. Restore from backup, 3. Clear caches, 4. Verify data |
| **Queue Stuck** | 1. Clear failed jobs, 2. Restart workers, 3. Monitor processing |
| **High Memory Usage** | 1. Restart PHP-FPM, 2. Check for memory leaks, 3. Review recent changes |
| **SSL Certificate Expired** | 1. Renew certificate, 2. Restart Nginx, 3. Verify HTTPS |

### 19.5 Backup Strategy

| Backup Type | Frequency | Retention | Storage |
|-------------|-----------|-----------|---------|
| Database (full) | Daily at 2 AM | 30 days | `/var/www/backups/` |
| Database (incremental) | Every 6 hours | 7 days | `/var/www/backups/` |
| Application files | Weekly | 4 weeks | Off-site |
| User uploads | Daily | 90 days | Off-site |

```bash
# /etc/cron.d/libra-backups
0 2 * * * root /var/www/libra/scripts/backup-database.sh >> /var/log/libra-backup.log 2>&1
0 */6 * * * root /var/www/libra/scripts/backup-incremental.sh >> /var/log/libra-backup.log 2>&1
```

---

## 20. Coding Standards

### 20.1 Critical Rules

| Rule | Description |
|------|-------------|
| **Volt Pattern** | Use class-based Volt components (per CLAUDE.md) |
| **Flux UI** | Use Flux components when available |
| **Form Validation** | Always use Form Request or Livewire form objects |
| **Eloquent** | Prefer `Model::query()` over `DB::` facade |
| **Actions** | Use single-purpose Action classes for business logic |
| **Testing** | Every feature must have corresponding Pest tests |
| **Pint** | Run `vendor/bin/pint --dirty` before committing |

### 20.2 Naming Conventions

| Element | Convention | Example |
|---------|------------|---------|
| Models | Singular PascalCase | `Consultation` |
| Tables | Plural snake_case | `consultations` |
| Columns | snake_case | `booking_date` |
| Controllers | PascalCase + Controller | `ConsultationController` |
| Actions | Verb + Noun + Action | `CreateConsultationAction` |
| Jobs | Verb + Noun | `SendBookingNotification` |
| Events | Past tense | `ConsultationApproved` |
| Listeners | Verb phrase | `SendApprovalNotification` |
| Volt Components | kebab-case path | `admin/consultations/index` |
| Enums | PascalCase | `ConsultationStatus` |
| Enum Cases | PascalCase | `NoShow` |
| Traits | Adjective or -able | `HasConsultations`, `Bookable` |
| Interfaces | Adjective or -able | `Exportable` |

### 20.3 Volt Component Template

```php
<?php
// resources/views/livewire/admin/consultations/index.blade.php

use App\Models\Consultation;
use App\Enums\ConsultationStatus;
use Livewire\Volt\Component;
use Livewire\WithPagination;

new class extends Component {
    use WithPagination;

    public string $search = '';
    public string $status = '';

    public function updatedSearch(): void
    {
        $this->resetPage();
    }

    public function updatedStatus(): void
    {
        $this->resetPage();
    }

    public function with(): array
    {
        return [
            'consultations' => Consultation::query()
                ->with('user:id,full_name,email')
                ->when($this->search, fn ($q) => $q->whereHas('user', fn ($q) =>
                    $q->where('full_name', 'like', "%{$this->search}%")
                ))
                ->when($this->status, fn ($q) => $q->where('status', $this->status))
                ->orderBy('booking_date', 'desc')
                ->paginate(15),
            'statuses' => ConsultationStatus::cases(),
        ];
    }
}; ?>

<div>
    <x-slot name="header">
        <flux:heading size="xl">{{ __('models.consultations') }}</flux:heading>
    </x-slot>

    <div class="flex gap-4 mb-6">
        <flux:input
            wire:model.live.debounce.300ms="search"
            placeholder="{{ __('messages.search') }}"
            icon="magnifying-glass"
        />

        <flux:select wire:model.live="status">
            <option value="">{{ __('messages.all_statuses') }}</option>
            @foreach($statuses as $s)
                <option value="{{ $s->value }}">{{ $s->label() }}</option>
            @endforeach
        </flux:select>
    </div>

    <div class="space-y-4">
        @forelse($consultations as $consultation)
            <x-ui.card wire:key="consultation-{{ $consultation->id }}">
                {{-- Card content --}}
            </x-ui.card>
        @empty
            <x-ui.empty-state :message="__('messages.no_consultations')" />
        @endforelse
    </div>

    <div class="mt-6">
        {{ $consultations->links() }}
    </div>
</div>
```

### 20.4 Action Class Template

```php
<?php
// app/Actions/Consultation/CreateConsultationAction.php

namespace App\Actions\Consultation;

use App\Models\Consultation;
use App\Models\User;
use App\Enums\ConsultationStatus;
use App\Enums\PaymentStatus;
use App\Jobs\SendBookingNotification;
use App\Exceptions\BookingLimitExceededException;
use Illuminate\Support\Facades\DB;

class CreateConsultationAction
{
    public function execute(User $user, array $data): Consultation
    {
        // Validate booking limit
        if ($user->hasBookingOnDate($data['booking_date'])) {
            throw new BookingLimitExceededException(
                __('messages.booking_limit_exceeded')
            );
        }

        return DB::transaction(function () use ($user, $data) {
            $consultation = Consultation::create([
                'user_id' => $user->id,
                'booking_date' => $data['booking_date'],
                'booking_time' => $data['booking_time'],
                'problem_summary' => $data['problem_summary'],
                'status' => ConsultationStatus::Pending,
                'payment_status' => PaymentStatus::NotApplicable,
            ]);

            SendBookingNotification::dispatch($consultation);

            return $consultation;
        });
    }
}
```

---

## 21. Technology Alternatives Considered

### 21.1 Frontend Framework

| Option | Pros | Cons | Decision |
|--------|------|------|----------|
| **Livewire 3 + Volt** | No JS build, Laravel native, simple state management | Learning curve, larger payload | **SELECTED** |
| React/Vue SPA | Rich ecosystem, familiar to many | Separate build, API needed, complexity | Rejected |
| Blade + Alpine only | Simplest, no Livewire overhead | Limited interactivity | Considered for simpler features |
| Inertia.js | Best of both worlds | Additional complexity, less documentation | Rejected |

**Rationale:** Livewire 3 provides excellent developer experience with Laravel, eliminates API development overhead, and Volt single-file components align well with the project's maintainability goals.

### 21.2 Database

| Option | Pros | Cons | Decision |
|--------|------|------|----------|
| **MariaDB** | MySQL compatible, open source, client preference | - | **SELECTED (Production)** |
| **SQLite** | Zero config, fast tests, portable | Not suitable for production | **SELECTED (Development)** |
| PostgreSQL | Advanced features, JSON support | Overkill for this project | Rejected |
| MySQL | Industry standard | MariaDB preferred by client | Rejected |

### 21.3 Authentication

| Option | Pros | Cons | Decision |
|--------|------|------|----------|
| **Laravel Fortify** | Headless, flexible, 2FA support | Requires custom views | **SELECTED** |
| Laravel Breeze | Quick setup, includes views | Less flexible, opinionated | Rejected |
| Laravel Jetstream | Full-featured, team support | Too complex, Livewire/Inertia choice | Rejected |
| Custom auth | Full control | Reinventing wheel, security risks | Rejected |

### 21.4 Queue System

| Option | Pros | Cons | Decision |
|--------|------|------|----------|
| **Database** | Simple, no extra services | Slower than Redis | **SELECTED** |
| Redis | Fast, feature-rich | Additional service to maintain | Future consideration |
| Amazon SQS | Managed, scalable | Overkill, cost, lock-in | Rejected |
| Beanstalkd | Lightweight, fast | Less Laravel integration | Rejected |

**Rationale:** Database queue driver is sufficient for expected volume (~100 jobs/day). Redis can be added later if performance requires.

### 21.5 CSS Framework

| Option | Pros | Cons | Decision |
|--------|------|------|----------|
| **Tailwind CSS 4** | Utility-first, RTL support, Flux UI compatible | Learning curve | **SELECTED** |
| Bootstrap 5 | Familiar, RTL plugin | Larger bundle, less flexible | PRD mentioned but Tailwind preferred |
| Custom CSS | Full control | Time-consuming, maintenance burden | Rejected |

---

## 22. Appendices

### A. Flux UI Free Components

Available components in Flux UI Free edition:

```
avatar, badge, brand, breadcrumbs, button, callout, checkbox, dropdown,
field, heading, icon, input, modal, navbar, otp-input, profile, radio,
select, separator, skeleton, switch, text, textarea, tooltip
```

### B. Color Palette

> **Note:** Color palette updated in Epic 10 (Brand Color Refresh).
> See `docs/brand.md` for complete brand guidelines.

| Name | Hex | CSS Variable | Usage |
|------|-----|--------------|-------|
| Charcoal | `#4A4A42` | `--color-charcoal` | Primary brand, buttons |
| Warm Gray | `#C9C4BA` | `--color-warm-gray` | Accents, borders |
| Off-White | `#E8E4DC` | `--color-off-white` | Backgrounds, cards |
| Deep Black | `#1A1A1A` | `--color-deep-black` | Text, footer |
| Success Green | `#27AE60` | `--color-success` | Success states |
| Warning Red | `#E74C3C` | `--color-danger` | Error states |
| Pending Yellow | `#F39C12` | `--color-warning` | Pending states |

### C. Consultation Duration Constants

```php
// config/libra.php
return [
    'consultation' => [
        'duration_minutes' => 45,
        'buffer_minutes' => 15,
        'slot_minutes' => 60, // duration + buffer
    ],
];
```

### D. External Resources

- [Laravel 12 Documentation](https://laravel.com/docs/12.x)
- [Livewire 3 Documentation](https://livewire.laravel.com/docs)
- [Volt Documentation](https://livewire.laravel.com/docs/volt)
- [Flux UI Documentation](https://fluxui.dev/docs)
- [Tailwind CSS 4 Documentation](https://tailwindcss.com/docs)
- [Pest Testing Documentation](https://pestphp.com/docs)
- [Laravel Fortify Documentation](https://laravel.com/docs/12.x/fortify)

### E. Contact Information

- **Admin Email:** (configured in settings)
- **System Email:** no-reply@libra.ps
- **Domain:** libra.ps

---

**END OF DOCUMENT**
