diff --git a/docs/stories/story-1.1-project-setup-database-schema.md b/docs/stories/story-1.1-project-setup-database-schema.md
new file mode 100644
index 0000000..f0da3c1
--- /dev/null
+++ b/docs/stories/story-1.1-project-setup-database-schema.md
@@ -0,0 +1,116 @@
+# Story 1.1: Project Setup & Database Schema
+
+## Epic Reference
+**Epic 1:** Core Foundation & Infrastructure
+
+## User Story
+As a **developer**,
+I want **the Laravel 12 project configured with all required packages and complete database schema**,
+So that **the foundation is established for all subsequent feature development**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** New project setup (greenfield)
+- **Technology:** Laravel 12, PHP 8.4, Livewire 3, Volt, Flux UI Free, Tailwind CSS 4
+- **Follows pattern:** Laravel 12 streamlined file structure
+- **Touch points:** Database migrations, model factories, development environment
+
+## Acceptance Criteria
+
+### Functional Requirements
+- [ ] Laravel 12 project created with Livewire 3, Volt, Flux UI
+- [ ] Tailwind CSS 4 configured with `@theme` directive
+- [ ] Database migrations for all core tables:
+ - `users` (with user_type, national_id, company fields)
+ - `consultations`
+ - `timelines`
+ - `timeline_updates`
+ - `posts`
+ - `working_hours`
+ - `blocked_times`
+ - `notifications`
+ - `admin_logs`
+- [ ] Model factories created for testing
+- [ ] Development environment working (`composer run dev`)
+
+### Integration Requirements
+- [ ] SQLite configured for development database
+- [ ] All migrations run without errors
+- [ ] Factories generate valid test data
+- [ ] Composer and npm scripts functional
+
+### Quality Requirements
+- [ ] All database tables have proper indexes
+- [ ] Foreign key constraints properly defined
+- [ ] Factories cover all required fields
+- [ ] Development server starts without errors
+
+## Technical Notes
+
+- **Database:** Use SQLite for development (configurable for MariaDB in production)
+- **Reference:** PRD Section 16.1 for complete schema details
+- **Pattern:** Follow existing Volt class-based component pattern
+- **Structure:** Use Laravel 12 streamlined file structure (no app/Http/Middleware, bootstrap/app.php for config)
+
+### Database Schema Reference
+
+```
+users:
+ - id, name, email, password, user_type (enum: admin/individual/company)
+ - national_id (nullable), company_name (nullable), company_registration (nullable)
+ - phone, preferred_language (enum: ar/en), status (enum: active/deactivated)
+ - timestamps, email_verified_at
+
+consultations:
+ - id, user_id, scheduled_date, scheduled_time, duration (default 45)
+ - status (enum: pending/approved/completed/cancelled/no_show)
+ - type (enum: free/paid), payment_amount (nullable), payment_status
+ - problem_summary, admin_notes, timestamps
+
+timelines:
+ - id, user_id, case_name, case_reference (unique nullable)
+ - status (enum: active/archived), timestamps
+
+timeline_updates:
+ - id, timeline_id, admin_id, update_text, timestamps
+
+posts:
+ - id, title_ar, title_en, body_ar, body_en
+ - status (enum: draft/published), timestamps
+
+working_hours:
+ - id, day_of_week (0-6), start_time, end_time, is_active, timestamps
+
+blocked_times:
+ - id, block_date, start_time (nullable), end_time (nullable)
+ - reason, timestamps
+
+admin_logs:
+ - id, admin_id, action_type, target_type, target_id
+ - old_values (json), new_values (json), ip_address, timestamps
+```
+
+## Definition of Done
+
+- [ ] All database migrations created and tested
+- [ ] All model factories functional
+- [ ] Development environment runs with `composer run dev`
+- [ ] Tests pass for model creation using factories
+- [ ] Code formatted with Pint
+- [ ] No errors on fresh install
+
+## Dependencies
+
+- None (this is the foundation story)
+
+## Risk Assessment
+
+- **Primary Risk:** Schema design changes required later
+- **Mitigation:** Follow PRD specifications closely, validate with stakeholder
+- **Rollback:** Fresh migration reset possible in development
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-6 hours
diff --git a/docs/stories/story-1.2-authentication-role-system.md b/docs/stories/story-1.2-authentication-role-system.md
new file mode 100644
index 0000000..ac510bc
--- /dev/null
+++ b/docs/stories/story-1.2-authentication-role-system.md
@@ -0,0 +1,112 @@
+# Story 1.2: Authentication & Role System
+
+## Epic Reference
+**Epic 1:** Core Foundation & Infrastructure
+
+## User Story
+As an **admin**,
+I want **a secure authentication system with Admin/Client roles**,
+So that **only authorized users can access the platform with appropriate permissions**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Fortify authentication, users table
+- **Technology:** Laravel Fortify, Livewire Volt
+- **Follows pattern:** Existing `app/Actions/Fortify/` for custom logic
+- **Touch points:** FortifyServiceProvider, login views, middleware
+
+## Acceptance Criteria
+
+### Functional Requirements
+- [ ] Fortify configured with custom Volt views
+- [ ] Login page with bilingual support (Arabic/English)
+- [ ] Session timeout after 2 hours of inactivity
+- [ ] Rate limiting on login attempts (5 attempts per minute)
+- [ ] Admin role with full access to all features
+- [ ] Client role with restricted access (own data only)
+- [ ] Registration feature DISABLED (admin creates all accounts)
+
+### Security Requirements
+- [ ] CSRF protection enabled on all forms
+- [ ] Password hashing using bcrypt
+- [ ] Gates/Policies for authorization checks
+- [ ] Secure session configuration
+- [ ] Remember me functionality (optional)
+
+### Integration Requirements
+- [ ] Login redirects to appropriate dashboard (admin vs client)
+- [ ] Logout clears session properly
+- [ ] Middleware protects admin-only routes
+- [ ] Failed login attempts logged
+
+### Quality Requirements
+- [ ] Login form validates inputs properly
+- [ ] Error messages are clear and bilingual
+- [ ] Tests cover authentication flow
+- [ ] No security vulnerabilities
+
+## Technical Notes
+
+### Fortify Configuration
+```php
+// config/fortify.php
+'features' => [
+ // Features::registration(), // DISABLED
+ Features::resetPasswords(),
+ Features::emailVerification(),
+ Features::updateProfileInformation(),
+ Features::updatePasswords(),
+],
+```
+
+### Custom Views Setup
+```php
+// FortifyServiceProvider boot()
+Fortify::loginView(fn () => view('auth.login'));
+// No registerView - registration disabled
+```
+
+### Role Implementation
+- Use `user_type` column: 'admin', 'individual', 'company'
+- Admin check: `$user->user_type === 'admin'`
+- Client check: `in_array($user->user_type, ['individual', 'company'])`
+
+### Gate Definitions
+```php
+// AuthServiceProvider or AppServiceProvider
+Gate::define('admin', fn (User $user) => $user->user_type === 'admin');
+Gate::define('client', fn (User $user) => $user->user_type !== 'admin');
+```
+
+### Middleware
+- `auth` - Require authentication
+- `can:admin` - Require admin role
+- Custom middleware for session timeout if needed
+
+## Definition of Done
+
+- [ ] Login page renders correctly in both languages
+- [ ] Users can log in with valid credentials
+- [ ] Invalid credentials show proper error
+- [ ] Rate limiting prevents brute force
+- [ ] Session expires after 2 hours inactivity
+- [ ] Admin routes protected from clients
+- [ ] Tests pass for authentication flow
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 1.1:** Database schema (users table)
+- **Story 1.3:** Bilingual infrastructure (for login page translations)
+
+## Risk Assessment
+
+- **Primary Risk:** Security misconfiguration
+- **Mitigation:** Use Laravel's built-in security features, no custom auth logic
+- **Rollback:** Restore Fortify defaults
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-1.3-bilingual-infrastructure.md b/docs/stories/story-1.3-bilingual-infrastructure.md
new file mode 100644
index 0000000..4ccc396
--- /dev/null
+++ b/docs/stories/story-1.3-bilingual-infrastructure.md
@@ -0,0 +1,154 @@
+# Story 1.3: Bilingual Infrastructure (Arabic/English)
+
+## Epic Reference
+**Epic 1:** Core Foundation & Infrastructure
+
+## User Story
+As a **user (admin or client)**,
+I want **full bilingual support with Arabic as primary and English as secondary language**,
+So that **I can use the platform in my preferred language with proper RTL/LTR layout**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Laravel localization, Tailwind CSS, user preferences
+- **Technology:** Laravel lang files, Tailwind RTL, Google Fonts
+- **Follows pattern:** Laravel localization best practices
+- **Touch points:** All views, navigation, date/time formatting
+
+## Acceptance Criteria
+
+### Functional Requirements
+- [ ] Language files for Arabic (ar) and English (en)
+- [ ] Language toggle in navigation (visible on all pages)
+- [ ] User language preference stored in `users.preferred_language`
+- [ ] Guest language stored in session
+- [ ] RTL layout for Arabic, LTR for English
+- [ ] All UI elements translatable via `__()` helper
+
+### Date/Time Formatting
+- [ ] Arabic: DD/MM/YYYY format
+- [ ] English: MM/DD/YYYY format
+- [ ] Both: 12-hour time format (AM/PM)
+- [ ] Both: Western numerals (123) - no Arabic numerals
+
+### Typography
+- [ ] Arabic fonts: Cairo or Tajawal (Google Fonts)
+- [ ] English fonts: Montserrat or Lato (Google Fonts)
+- [ ] Font weights: 300, 400, 600, 700
+- [ ] font-display: swap for performance
+
+### Integration Requirements
+- [ ] Language middleware sets locale from user preference or session
+- [ ] Direction attribute (`dir="rtl"` or `dir="ltr"`) on HTML element
+- [ ] Tailwind RTL utilities working
+- [ ] Forms align correctly in both directions
+
+### Quality Requirements
+- [ ] No hardcoded strings in views
+- [ ] All translation keys organized by feature
+- [ ] Tests verify language switching
+- [ ] No layout breaks when switching languages
+
+## Technical Notes
+
+### Language Middleware
+```php
+// Middleware to set locale
+public function handle($request, Closure $next)
+{
+ $locale = session('locale',
+ auth()->user()?->preferred_language ?? 'ar'
+ );
+ app()->setLocale($locale);
+ return $next($request);
+}
+```
+
+### Translation File Structure
+```
+resources/lang/
+ ar/
+ auth.php
+ pagination.php
+ validation.php
+ messages.php
+ navigation.php
+ en/
+ auth.php
+ pagination.php
+ validation.php
+ messages.php
+ navigation.php
+```
+
+### RTL Support with Tailwind 4
+```css
+/* In app.css */
+@import "tailwindcss";
+
+@theme {
+ /* RTL support via logical properties */
+}
+```
+
+### Font Configuration
+```css
+/* Google Fonts import */
+@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&family=Montserrat:wght@300;400;600;700&display=swap');
+
+@theme {
+ --font-arabic: 'Cairo', 'Tajawal', sans-serif;
+ --font-english: 'Montserrat', 'Lato', sans-serif;
+}
+```
+
+### Layout Template
+```blade
+
+
+
+
+```
+
+### Date Formatting Helper
+```php
+// In a helper or service
+public function formatDate($date, $locale = null): string
+{
+ $locale = $locale ?? app()->getLocale();
+ $format = $locale === 'ar' ? 'd/m/Y' : 'm/d/Y';
+ return Carbon::parse($date)->format($format);
+}
+```
+
+## Definition of Done
+
+- [ ] Language toggle works in navigation
+- [ ] Arabic and English translations complete for core UI
+- [ ] RTL layout renders correctly for Arabic
+- [ ] LTR layout renders correctly for English
+- [ ] User preference persists in database
+- [ ] Guest preference persists in session
+- [ ] Dates format correctly per language
+- [ ] Fonts load properly for both languages
+- [ ] Tests pass for language switching
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 1.1:** Database schema (users.preferred_language column)
+- **Story 1.4:** Base UI (navigation for language toggle)
+
+## Risk Assessment
+
+- **Primary Risk:** RTL edge cases in complex layouts
+- **Mitigation:** Use Tailwind logical properties (start/end vs left/right), test early
+- **Rollback:** Fallback to LTR-only temporarily
+
+## Estimation
+
+**Complexity:** Medium-High
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-1.4-base-ui-navigation.md b/docs/stories/story-1.4-base-ui-navigation.md
new file mode 100644
index 0000000..7284859
--- /dev/null
+++ b/docs/stories/story-1.4-base-ui-navigation.md
@@ -0,0 +1,181 @@
+# Story 1.4: Base UI & Navigation
+
+## Epic Reference
+**Epic 1:** Core Foundation & Infrastructure
+
+## User Story
+As a **website visitor or logged-in user**,
+I want **a professional, responsive navigation system with brand colors**,
+So that **I can easily navigate the platform on any device**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Flux UI Free, Tailwind CSS 4, bilingual system
+- **Technology:** Livewire Volt, Flux UI components, Alpine.js
+- **Follows pattern:** Flux UI navbar patterns, mobile-first design
+- **Touch points:** All pages (layout component)
+
+## Acceptance Criteria
+
+### Color Scheme
+- [ ] Primary: Dark Navy Blue (#0A1F44) - backgrounds, headers
+- [ ] Accent: Gold (#D4AF37) - buttons, links, accents
+- [ ] Light Gold: #F4E4B8 - hover states
+- [ ] Off-White/Cream: #F9F7F4 - cards, content areas
+- [ ] Charcoal Gray: #2C3E50 - secondary text
+- [ ] Custom Tailwind colors configured via @theme
+
+### Navigation Bar
+- [ ] Fixed top position
+- [ ] Navy blue background
+- [ ] Logo placement: left on desktop, centered on mobile
+- [ ] Main menu items: Home, Booking, Posts, Login/Dashboard
+- [ ] Language toggle (Arabic/English) visible
+- [ ] Responsive mobile hamburger menu
+- [ ] Gold text for links, hover effects
+
+### Mobile Menu
+- [ ] Full-width dropdown or slide-in
+- [ ] Navy background with gold text
+- [ ] Touch-friendly targets (44px+ height)
+- [ ] Smooth open/close animation
+- [ ] Close on outside click or navigation
+
+### Footer
+- [ ] Navy blue background
+- [ ] Libra logo (smaller version)
+- [ ] Firm contact information
+- [ ] Links: Terms of Service, Privacy Policy
+- [ ] Copyright notice with current year
+- [ ] Sticky footer (always at bottom of viewport)
+
+### Layout Components
+- [ ] Card-based layouts with proper shadows and border-radius
+- [ ] Consistent spacing using Tailwind utilities
+- [ ] Container max-width: 1200px, centered
+- [ ] WCAG AA contrast compliance verified
+
+### Integration Requirements
+- [ ] Flux UI components used where available
+- [ ] Works with RTL and LTR layouts
+- [ ] Navigation state reflects current page
+- [ ] Login/logout state reflected in menu
+
+### Quality Requirements
+- [ ] Responsive on all breakpoints (mobile, tablet, desktop)
+- [ ] No horizontal scroll on any viewport
+- [ ] Fast loading (minimal CSS/JS)
+- [ ] Tests verify navigation rendering
+
+## Technical Notes
+
+### Tailwind Color Configuration
+```css
+/* In resources/css/app.css */
+@import "tailwindcss";
+
+@theme {
+ --color-navy: #0A1F44;
+ --color-gold: #D4AF37;
+ --color-gold-light: #F4E4B8;
+ --color-cream: #F9F7F4;
+ --color-charcoal: #2C3E50;
+ --color-success: #27AE60;
+ --color-danger: #E74C3C;
+ --color-warning: #F39C12;
+}
+```
+
+### Layout Component Structure
+```blade
+
+
+
+...
+
+
+
+ {{ $slot }}
+
+
+
+
+```
+
+### Navigation Component
+```blade
+
+
+
+
+
+
+
+
+ {{ __('navigation.home') }}
+
+
+
+
+
+
+```
+
+### Mobile Menu with Alpine.js
+```blade
+
+```
+
+### Logo Component
+```blade
+
+@props(['size' => 'default'])
+
+
$size === 'small',
+ 'h-12' => $size === 'default',
+ 'h-16' => $size === 'large',
+ ])
+/>
+```
+
+## Definition of Done
+
+- [ ] Navigation renders correctly on all viewports
+- [ ] Color scheme matches brand guidelines
+- [ ] Mobile menu opens/closes smoothly
+- [ ] Footer sticks to bottom of page
+- [ ] Language toggle functional
+- [ ] RTL/LTR layouts correct
+- [ ] All navigation links work
+- [ ] Login state reflected in menu
+- [ ] Tests pass for navigation
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 1.1:** Database schema (for user authentication state)
+- **Story 1.2:** Authentication (for login/logout state in nav)
+- **Story 1.3:** Bilingual infrastructure (for language toggle and translations)
+
+## Risk Assessment
+
+- **Primary Risk:** Flux UI limitations for custom styling
+- **Mitigation:** Extend Flux components with custom Tailwind classes
+- **Rollback:** Build custom navigation if Flux doesn't meet needs
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-2.1-individual-client-account-management.md b/docs/stories/story-2.1-individual-client-account-management.md
new file mode 100644
index 0000000..17b9002
--- /dev/null
+++ b/docs/stories/story-2.1-individual-client-account-management.md
@@ -0,0 +1,165 @@
+# Story 2.1: Individual Client Account Management
+
+## Epic Reference
+**Epic 2:** User Management System
+
+## User Story
+As an **admin**,
+I want **to create, view, edit, and search individual client accounts**,
+So that **I can manage client information and provide them platform access**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Users table, Fortify authentication
+- **Technology:** Livewire Volt, Flux UI forms
+- **Follows pattern:** Admin CRUD patterns, class-based Volt components
+- **Touch points:** User model, admin dashboard
+
+## Acceptance Criteria
+
+### Create Individual Client
+- [ ] Form with required fields:
+ - Full Name (required)
+ - National ID Number (required, unique)
+ - Email Address (required, unique)
+ - Phone Number (required)
+ - Password (admin-set, required)
+ - Preferred Language (Arabic/English dropdown)
+- [ ] Validation for all required fields
+- [ ] Duplicate email/National ID prevention with clear error message
+- [ ] Password strength indicator (optional)
+- [ ] Success message on creation
+
+### List View
+- [ ] Display all individual clients (user_type = 'individual')
+- [ ] Columns: Name, Email, National ID, Phone, Status, Created Date
+- [ ] Pagination (10/25/50 per page)
+- [ ] Default sort by created date (newest first)
+
+### Search & Filter
+- [ ] Search by name, email, or National ID
+- [ ] Filter by status (active/deactivated/all)
+- [ ] Real-time search with debounce (300ms)
+- [ ] Clear filters button
+
+### Edit Client
+- [ ] Edit all client information
+- [ ] Cannot change user_type from this form
+- [ ] Validation same as create
+- [ ] Success message on update
+
+### View Client Profile
+- [ ] Display all client information
+- [ ] Show consultation history summary
+- [ ] Show timeline history summary
+- [ ] Quick links to related records
+
+### Quality Requirements
+- [ ] Bilingual form labels and messages
+- [ ] Proper form validation with error display
+- [ ] Audit log entries for all operations
+- [ ] Tests for CRUD operations
+
+## Technical Notes
+
+### User Model Scope
+```php
+// In User model
+public function scopeIndividual($query)
+{
+ return $query->where('user_type', 'individual');
+}
+```
+
+### Volt Component Structure
+```php
+resetPage();
+ }
+
+ public function with(): array
+ {
+ return [
+ 'clients' => User::individual()
+ ->when($this->search, fn($q) => $q->where(function($q) {
+ $q->where('name', 'like', "%{$this->search}%")
+ ->orWhere('email', 'like', "%{$this->search}%")
+ ->orWhere('national_id', 'like', "%{$this->search}%");
+ }))
+ ->when($this->statusFilter, fn($q) => $q->where('status', $this->statusFilter))
+ ->latest()
+ ->paginate(10),
+ ];
+ }
+};
+```
+
+### Validation Rules
+```php
+public function rules(): array
+{
+ return [
+ 'name' => ['required', 'string', 'max:255'],
+ 'national_id' => ['required', 'string', 'unique:users,national_id'],
+ 'email' => ['required', 'email', 'unique:users,email'],
+ 'phone' => ['required', 'string'],
+ 'password' => ['required', 'string', 'min:8'],
+ 'preferred_language' => ['required', 'in:ar,en'],
+ ];
+}
+```
+
+### Admin Logging
+```php
+// After creating user
+AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'create',
+ 'target_type' => 'user',
+ 'target_id' => $user->id,
+ 'new_values' => $user->only(['name', 'email', 'national_id']),
+ 'ip_address' => request()->ip(),
+]);
+```
+
+## Definition of Done
+
+- [ ] Create individual client form works
+- [ ] List view displays all individual clients
+- [ ] Search and filter functional
+- [ ] Edit client works with validation
+- [ ] View profile shows complete information
+- [ ] Duplicate prevention works
+- [ ] Audit logging implemented
+- [ ] Bilingual support complete
+- [ ] Tests pass for all CRUD operations
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Epic 1:** Authentication system, database schema, bilingual support
+
+## Risk Assessment
+
+- **Primary Risk:** Duplicate National ID from different sources
+- **Mitigation:** Database unique constraint + form validation
+- **Rollback:** Remove user and notify if duplicate discovered
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-2.2-company-client-account-management.md b/docs/stories/story-2.2-company-client-account-management.md
new file mode 100644
index 0000000..a74dc26
--- /dev/null
+++ b/docs/stories/story-2.2-company-client-account-management.md
@@ -0,0 +1,187 @@
+# Story 2.2: Company/Corporate Client Account Management
+
+## Epic Reference
+**Epic 2:** User Management System
+
+## User Story
+As an **admin**,
+I want **to create, view, edit, and manage company/corporate client accounts**,
+So that **I can serve corporate clients with their unique data requirements**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Users table, potential contact_persons table
+- **Technology:** Livewire Volt, Flux UI forms
+- **Follows pattern:** Same CRUD pattern as individual clients
+- **Touch points:** User model, admin dashboard
+
+## Acceptance Criteria
+
+### Create Company Client
+- [ ] Form with required fields:
+ - Company Name (required)
+ - Company Registration Number (required, unique)
+ - Contact Person Name (required)
+ - Contact Person ID (required)
+ - Email Address (required, unique)
+ - Phone Number (required)
+ - Password (admin-set, required)
+ - Preferred Language (Arabic/English dropdown)
+- [ ] Validation for all required fields
+- [ ] Duplicate email/registration number prevention
+- [ ] Success message on creation
+
+### Multiple Contact Persons (Optional Enhancement)
+- [ ] Support unlimited contact persons per company
+- [ ] Each contact: Name, ID, Phone, Email
+- [ ] Primary contact indicator
+- [ ] Add/remove contacts dynamically
+
+### List View
+- [ ] Display all company clients (user_type = 'company')
+- [ ] Columns: Company Name, Contact Person, Email, Reg #, Status, Created Date
+- [ ] Pagination (10/25/50 per page)
+- [ ] Default sort by created date
+
+### Search & Filter
+- [ ] Search by company name, email, or registration number
+- [ ] Filter by status (active/deactivated/all)
+- [ ] Real-time search with debounce
+
+### Edit Company
+- [ ] Edit all company information
+- [ ] Update contact person details
+- [ ] Validation same as create
+- [ ] Success message on update
+
+### View Company Profile
+- [ ] Display all company information
+- [ ] List all contact persons
+- [ ] Show consultation history summary
+- [ ] Show timeline history summary
+
+### Quality Requirements
+- [ ] Bilingual form labels and messages
+- [ ] Proper form validation
+- [ ] Audit log entries for all operations
+- [ ] Tests for CRUD operations
+
+## Technical Notes
+
+### User Model Scope
+```php
+public function scopeCompany($query)
+{
+ return $query->where('user_type', 'company');
+}
+```
+
+### Database Fields for Company
+```
+users table:
+ - company_name (nullable, required for company type)
+ - company_registration (nullable, unique when not null)
+ - contact_person_name (nullable, required for company)
+ - contact_person_id (nullable, required for company)
+```
+
+### Alternative: Separate Contact Persons Table
+```php
+// contact_persons migration
+Schema::create('contact_persons', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+ $table->string('name');
+ $table->string('national_id');
+ $table->string('phone')->nullable();
+ $table->string('email')->nullable();
+ $table->boolean('is_primary')->default(false);
+ $table->timestamps();
+});
+```
+
+### Validation Rules
+```php
+public function rules(): array
+{
+ return [
+ 'company_name' => ['required', 'string', 'max:255'],
+ 'company_registration' => ['required', 'string', 'unique:users,company_registration'],
+ 'contact_person_name' => ['required', 'string', 'max:255'],
+ 'contact_person_id' => ['required', 'string'],
+ 'email' => ['required', 'email', 'unique:users,email'],
+ 'phone' => ['required', 'string'],
+ 'password' => ['required', 'string', 'min:8'],
+ 'preferred_language' => ['required', 'in:ar,en'],
+ ];
+}
+```
+
+### Volt Component for Create
+```php
+validate();
+
+ $user = User::create([
+ ...$validated,
+ 'user_type' => 'company',
+ 'name' => $this->company_name, // For display purposes
+ 'password' => Hash::make($this->password),
+ 'status' => 'active',
+ ]);
+
+ // Log action
+ // Send welcome email
+
+ session()->flash('success', __('messages.company_created'));
+ $this->redirect(route('admin.users.index'));
+ }
+};
+```
+
+## Definition of Done
+
+- [ ] Create company client form works
+- [ ] List view displays all company clients
+- [ ] Search and filter functional
+- [ ] Edit company works with validation
+- [ ] View profile shows complete information
+- [ ] Duplicate prevention works
+- [ ] Audit logging implemented
+- [ ] Bilingual support complete
+- [ ] Tests pass for all CRUD operations
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Epic 1:** Authentication system, database schema
+- **Story 2.1:** Same CRUD patterns
+
+## Risk Assessment
+
+- **Primary Risk:** Complex contact persons relationship
+- **Mitigation:** Start simple (single contact), enhance later if needed
+- **Rollback:** Use simple fields on users table
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-2.3-account-type-conversion.md b/docs/stories/story-2.3-account-type-conversion.md
new file mode 100644
index 0000000..a810ae5
--- /dev/null
+++ b/docs/stories/story-2.3-account-type-conversion.md
@@ -0,0 +1,215 @@
+# Story 2.3: Account Type Conversion
+
+## Epic Reference
+**Epic 2:** User Management System
+
+## User Story
+As an **admin**,
+I want **to convert individual accounts to company accounts and vice versa**,
+So that **I can accommodate clients whose business structure changes**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Users table, consultations, timelines
+- **Technology:** Livewire Volt, modal dialogs
+- **Follows pattern:** Admin action pattern with confirmation
+- **Touch points:** User model, related records
+
+## Acceptance Criteria
+
+### Convert Individual to Company
+- [ ] "Convert to Company" action available on individual profiles
+- [ ] Modal/form prompts for additional company fields:
+ - Company Name (required)
+ - Company Registration Number (required)
+ - Contact Person Name (pre-filled with current name)
+ - Contact Person ID (pre-filled with National ID)
+- [ ] Preserve existing data (email, phone, password)
+- [ ] Preserve all consultation history
+- [ ] Preserve all timeline history
+- [ ] Confirmation dialog before conversion
+- [ ] Success message after conversion
+
+### Convert Company to Individual
+- [ ] "Convert to Individual" action available on company profiles
+- [ ] Modal/form prompts for individual-specific fields:
+ - Full Name (pre-filled with contact person or company name)
+ - National ID (pre-filled with contact person ID if available)
+- [ ] Handle company-specific data:
+ - Company fields set to null
+ - Clear company registration
+- [ ] Preserve all consultation history
+- [ ] Preserve all timeline history
+- [ ] Confirmation dialog before conversion
+
+### Notifications & Logging
+- [ ] Audit log entry capturing:
+ - Old user_type
+ - New user_type
+ - Old values
+ - New values
+ - Timestamp
+ - Admin who performed action
+- [ ] Email notification to user about account type change
+
+### Quality Requirements
+- [ ] Bilingual confirmation dialogs
+- [ ] Bilingual email notification
+- [ ] All data preserved (verify in tests)
+- [ ] No broken relationships after conversion
+
+## Technical Notes
+
+### Conversion Logic
+```php
+ company conversion
+ public string $company_name = '';
+ public string $company_registration = '';
+ public string $contact_person_name = '';
+ public string $contact_person_id = '';
+
+ public function mount(User $user): void
+ {
+ $this->user = $user;
+
+ // Pre-fill for company conversion
+ if ($user->user_type === 'individual') {
+ $this->contact_person_name = $user->name;
+ $this->contact_person_id = $user->national_id ?? '';
+ }
+ }
+
+ public function convertToCompany(): void
+ {
+ $this->validate([
+ 'company_name' => 'required|string|max:255',
+ 'company_registration' => 'required|string|unique:users,company_registration',
+ 'contact_person_name' => 'required|string|max:255',
+ 'contact_person_id' => 'required|string',
+ ]);
+
+ $oldValues = $this->user->only([
+ 'user_type', 'name', 'national_id',
+ 'company_name', 'company_registration'
+ ]);
+
+ $this->user->update([
+ 'user_type' => 'company',
+ 'name' => $this->company_name,
+ 'company_name' => $this->company_name,
+ 'company_registration' => $this->company_registration,
+ 'contact_person_name' => $this->contact_person_name,
+ 'contact_person_id' => $this->contact_person_id,
+ 'national_id' => null, // Company doesn't have individual national ID
+ ]);
+
+ $this->logConversion($oldValues);
+ $this->notifyUser();
+
+ session()->flash('success', __('messages.account_converted'));
+ }
+
+ public function convertToIndividual(): void
+ {
+ // Similar logic for company -> individual
+ }
+
+ private function logConversion(array $oldValues): void
+ {
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'convert_account',
+ 'target_type' => 'user',
+ 'target_id' => $this->user->id,
+ 'old_values' => $oldValues,
+ 'new_values' => $this->user->fresh()->only([
+ 'user_type', 'name', 'national_id',
+ 'company_name', 'company_registration'
+ ]),
+ 'ip_address' => request()->ip(),
+ ]);
+ }
+
+ private function notifyUser(): void
+ {
+ $this->user->notify(new AccountTypeChangedNotification(
+ $this->user->user_type
+ ));
+ }
+};
+```
+
+### Verification Query
+```php
+// Verify relationships preserved
+public function testConversionPreservesRelationships(): void
+{
+ $user = User::factory()->individual()->create();
+ $consultation = Consultation::factory()->for($user)->create();
+ $timeline = Timeline::factory()->for($user)->create();
+
+ // Perform conversion
+ $user->update(['user_type' => 'company', ...]);
+
+ // Verify relationships
+ expect($user->consultations)->toHaveCount(1);
+ expect($user->timelines)->toHaveCount(1);
+ expect($consultation->fresh()->user_id)->toBe($user->id);
+}
+```
+
+### Email Template
+```blade
+
+@component('mail::message')
+# {{ __('emails.account_type_changed.title') }}
+
+{{ __('emails.account_type_changed.body', ['type' => $newType]) }}
+
+@component('mail::button', ['url' => route('login')])
+{{ __('emails.login_now') }}
+@endcomponent
+
+@endcomponent
+```
+
+## Definition of Done
+
+- [ ] Convert individual to company works
+- [ ] Convert company to individual works
+- [ ] All existing data preserved
+- [ ] Consultation history intact
+- [ ] Timeline history intact
+- [ ] Confirmation dialog shows before action
+- [ ] Audit log entry created
+- [ ] Email notification sent
+- [ ] Bilingual support complete
+- [ ] Tests verify data preservation
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 2.1:** Individual client management
+- **Story 2.2:** Company client management
+- **Epic 3:** Consultations (for preservation testing)
+- **Epic 4:** Timelines (for preservation testing)
+
+## Risk Assessment
+
+- **Primary Risk:** Data loss during conversion
+- **Mitigation:** Transaction wrapping, thorough testing, audit log
+- **Rollback:** Restore from audit log old_values
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-2.4-account-lifecycle-management.md b/docs/stories/story-2.4-account-lifecycle-management.md
new file mode 100644
index 0000000..7cab7f9
--- /dev/null
+++ b/docs/stories/story-2.4-account-lifecycle-management.md
@@ -0,0 +1,285 @@
+# Story 2.4: Account Lifecycle Management
+
+## Epic Reference
+**Epic 2:** User Management System
+
+## User Story
+As an **admin**,
+I want **to deactivate, reactivate, and permanently delete client accounts**,
+So that **I can manage the full lifecycle of client relationships**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Users table, consultations, timelines, notifications
+- **Technology:** Livewire Volt, confirmation modals
+- **Follows pattern:** Soft deactivation, hard deletion with cascade
+- **Touch points:** User model, all related models
+
+## Acceptance Criteria
+
+### Deactivate Account
+- [ ] "Deactivate" button on user profile and list
+- [ ] Confirmation dialog explaining consequences
+- [ ] Effects of deactivation:
+ - User cannot log in
+ - All data retained (consultations, timelines)
+ - Status changes to 'deactivated'
+ - Can be reactivated by admin
+- [ ] Visual indicator in user list (grayed out, badge)
+- [ ] Audit log entry created
+
+### Reactivate Account
+- [ ] "Reactivate" button on deactivated profiles
+- [ ] Confirmation dialog
+- [ ] Effects of reactivation:
+ - Restore login ability
+ - Status changes to 'active'
+ - All data intact
+- [ ] Email notification sent to user
+- [ ] Audit log entry created
+
+### Delete Account (Permanent)
+- [ ] "Delete" button (with danger styling)
+- [ ] Confirmation dialog with strong warning:
+ - "This action cannot be undone"
+ - Lists what will be deleted
+ - Requires typing confirmation (e.g., user email)
+- [ ] Effects of deletion:
+ - User record permanently removed
+ - Cascades to: consultations, timelines, timeline_updates, notifications
+ - Cannot be recovered
+- [ ] Audit log entry preserved (for audit trail)
+- [ ] No email sent (user no longer exists)
+
+### Password Reset
+- [ ] "Reset Password" action on user profile
+- [ ] Options:
+ - Generate random password
+ - Set specific password manually
+- [ ] Email new credentials to client
+- [ ] Force password change on next login (optional)
+- [ ] Audit log entry created
+
+### Quality Requirements
+- [ ] All actions logged in admin_logs table
+- [ ] Bilingual confirmation messages
+- [ ] Clear visual states for account status
+- [ ] Tests for all lifecycle operations
+
+## Technical Notes
+
+### User Status Management
+```php
+// User model
+public function isActive(): bool
+{
+ return $this->status === 'active';
+}
+
+public function isDeactivated(): bool
+{
+ return $this->status === 'deactivated';
+}
+
+public function deactivate(): void
+{
+ $this->update(['status' => 'deactivated']);
+}
+
+public function reactivate(): void
+{
+ $this->update(['status' => 'active']);
+}
+```
+
+### Authentication Check
+```php
+// In FortifyServiceProvider or custom auth
+Fortify::authenticateUsing(function (Request $request) {
+ $user = User::where('email', $request->email)->first();
+
+ if ($user &&
+ $user->isActive() &&
+ Hash::check($request->password, $user->password)) {
+ return $user;
+ }
+
+ // Optionally: different error for deactivated
+ return null;
+});
+```
+
+### Cascade Deletion
+```php
+// In User model
+protected static function booted(): void
+{
+ static::deleting(function (User $user) {
+ // Log before deletion
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'delete',
+ 'target_type' => 'user',
+ 'target_id' => $user->id,
+ 'old_values' => $user->toArray(),
+ 'ip_address' => request()->ip(),
+ ]);
+
+ // Cascade delete (or use foreign key CASCADE)
+ $user->consultations()->delete();
+ $user->timelines->each(function ($timeline) {
+ $timeline->updates()->delete();
+ $timeline->delete();
+ });
+ $user->notifications()->delete();
+ });
+}
+```
+
+### Volt Component for Lifecycle Actions
+```php
+user->deactivate();
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'deactivate',
+ 'target_type' => 'user',
+ 'target_id' => $this->user->id,
+ 'old_values' => ['status' => 'active'],
+ 'new_values' => ['status' => 'deactivated'],
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.user_deactivated'));
+ }
+
+ public function reactivate(): void
+ {
+ $this->user->reactivate();
+
+ // Send notification
+ $this->user->notify(new AccountReactivatedNotification());
+
+ // Log action
+ AdminLog::create([...]);
+
+ session()->flash('success', __('messages.user_reactivated'));
+ }
+
+ public function delete(): void
+ {
+ if ($this->deleteConfirmation !== $this->user->email) {
+ $this->addError('deleteConfirmation', __('validation.email_confirmation'));
+ return;
+ }
+
+ $this->user->delete();
+
+ session()->flash('success', __('messages.user_deleted'));
+ $this->redirect(route('admin.users.index'));
+ }
+
+ public function resetPassword(): void
+ {
+ $newPassword = Str::random(12);
+
+ $this->user->update([
+ 'password' => Hash::make($newPassword),
+ ]);
+
+ // Send new credentials email
+ $this->user->notify(new PasswordResetByAdminNotification($newPassword));
+
+ // Log action
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'password_reset',
+ 'target_type' => 'user',
+ 'target_id' => $this->user->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.password_reset_sent'));
+ }
+};
+```
+
+### Delete Confirmation Modal
+```blade
+
+ {{ __('messages.confirm_delete') }}
+
+
+ {{ __('messages.delete_warning') }}
+
+
+ {{ __('messages.will_be_deleted') }}
+
+ - {{ __('messages.all_consultations') }}
+ - {{ __('messages.all_timelines') }}
+ - {{ __('messages.all_notifications') }}
+
+
+
+ {{ __('messages.type_email_to_confirm', ['email' => $user->email]) }}
+
+
+
+
+
+
+ {{ __('messages.cancel') }}
+
+
+ {{ __('messages.delete_permanently') }}
+
+
+
+```
+
+## Definition of Done
+
+- [ ] Deactivate prevents login but preserves data
+- [ ] Reactivate restores login ability
+- [ ] Delete permanently removes all user data
+- [ ] Delete requires email confirmation
+- [ ] Password reset sends new credentials
+- [ ] Visual indicators show account status
+- [ ] Audit logging for all actions
+- [ ] Email notifications sent appropriately
+- [ ] Bilingual support complete
+- [ ] Tests for all lifecycle states
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 2.1:** Individual client management
+- **Story 2.2:** Company client management
+- **Epic 3:** Consultations (cascade delete)
+- **Epic 4:** Timelines (cascade delete)
+
+## Risk Assessment
+
+- **Primary Risk:** Accidental permanent deletion
+- **Mitigation:** Strong confirmation dialog, email confirmation, audit log
+- **Rollback:** Not possible for delete - warn user clearly
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-2.5-account-creation-email-notification.md b/docs/stories/story-2.5-account-creation-email-notification.md
new file mode 100644
index 0000000..ac85c3e
--- /dev/null
+++ b/docs/stories/story-2.5-account-creation-email-notification.md
@@ -0,0 +1,278 @@
+# Story 2.5: Account Creation Email Notification
+
+## Epic Reference
+**Epic 2:** User Management System
+
+## User Story
+As an **admin**,
+I want **welcome emails sent automatically when I create client accounts**,
+So that **clients receive their login credentials and know how to access the platform**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** User creation flow, Laravel Mail
+- **Technology:** Laravel Mailable, queued jobs
+- **Follows pattern:** Laravel notification/mailable patterns
+- **Touch points:** User model events, email templates
+
+## Acceptance Criteria
+
+### Welcome Email Trigger
+- [ ] Email sent automatically on account creation
+- [ ] Works for both individual and company accounts
+- [ ] Queued for performance (async sending)
+- [ ] No email sent for admin accounts
+
+### Email Content
+- [ ] Personalized greeting:
+ - Individual: "Dear [Name]"
+ - Company: "Dear [Company Name]"
+- [ ] Message: "Your account has been created"
+- [ ] Login credentials:
+ - Email address
+ - Password (shown in email)
+- [ ] Login URL (clickable button/link)
+- [ ] Brief platform introduction
+- [ ] Contact information for questions
+
+### Email Design
+- [ ] Professional template with Libra branding
+- [ ] Colors: Navy blue (#0A1F44) and Gold (#D4AF37)
+- [ ] Libra logo in header
+- [ ] Footer with firm information
+- [ ] Mobile-responsive layout
+
+### Sender Configuration
+- [ ] From: no-reply@libra.ps
+- [ ] From Name: Libra Law Firm / مكتب ليبرا للمحاماة
+- [ ] Reply-To: (firm contact email)
+
+### Language Support
+- [ ] Email in user's preferred_language
+- [ ] Arabic email for Arabic preference
+- [ ] English email for English preference
+- [ ] All text translated
+
+### Plain Text Fallback
+- [ ] Plain text version generated
+- [ ] All essential information included
+- [ ] Readable without HTML
+
+### Quality Requirements
+- [ ] Email passes spam filters
+- [ ] Links work correctly
+- [ ] Password visible but not overly prominent
+- [ ] Tests verify email sending
+
+## Technical Notes
+
+### Mailable Class
+```php
+user->preferred_language ?? 'ar';
+
+ return new Envelope(
+ subject: $locale === 'ar'
+ ? 'مرحباً بك في مكتب ليبرا للمحاماة'
+ : 'Welcome to Libra Law Firm',
+ );
+ }
+
+ public function content(): Content
+ {
+ $locale = $this->user->preferred_language ?? 'ar';
+
+ return new Content(
+ markdown: "emails.welcome.{$locale}",
+ with: [
+ 'user' => $this->user,
+ 'password' => $this->password,
+ 'loginUrl' => route('login'),
+ ],
+ );
+ }
+}
+```
+
+### Email Template (Arabic)
+```blade
+
+
+# مرحباً بك في مكتب ليبرا للمحاماة
+
+@if($user->user_type === 'company')
+عزيزي {{ $user->company_name }},
+@else
+عزيزي {{ $user->name }},
+@endif
+
+تم إنشاء حسابك بنجاح على منصة مكتب ليبرا للمحاماة.
+
+**بيانات تسجيل الدخول:**
+
+- **البريد الإلكتروني:** {{ $user->email }}
+- **كلمة المرور:** {{ $password }}
+
+
+تسجيل الدخول
+
+
+يمكنك الآن الوصول إلى:
+- حجز المواعيد
+- متابعة قضاياك
+- عرض التحديثات
+
+إذا كان لديك أي استفسار، لا تتردد في التواصل معنا.
+
+مع أطيب التحيات,
+مكتب ليبرا للمحاماة
+
+```
+
+### Email Template (English)
+```blade
+
+
+# Welcome to Libra Law Firm
+
+@if($user->user_type === 'company')
+Dear {{ $user->company_name }},
+@else
+Dear {{ $user->name }},
+@endif
+
+Your account has been successfully created on the Libra Law Firm platform.
+
+**Login Credentials:**
+
+- **Email:** {{ $user->email }}
+- **Password:** {{ $password }}
+
+
+Login Now
+
+
+You can now access:
+- Book consultations
+- Track your cases
+- View updates
+
+If you have any questions, please don't hesitate to contact us.
+
+Best regards,
+Libra Law Firm
+
+```
+
+### Trigger on User Creation
+```php
+// In User creation flow (Story 2.1/2.2)
+public function create(): void
+{
+ $validated = $this->validate();
+ $plainPassword = $this->password;
+
+ $user = User::create([
+ ...$validated,
+ 'password' => Hash::make($this->password),
+ ]);
+
+ // Send welcome email with plain password
+ Mail::to($user)->queue(new WelcomeEmail($user, $plainPassword));
+
+ session()->flash('success', __('messages.user_created'));
+}
+```
+
+### Email Theme Customization
+```php
+// In AppServiceProvider boot()
+use Illuminate\Support\Facades\View;
+
+View::composer('vendor.mail.*', function ($view) {
+ $view->with('logoUrl', asset('images/logo.png'));
+ $view->with('primaryColor', '#0A1F44');
+ $view->with('accentColor', '#D4AF37');
+});
+```
+
+### Testing
+```php
+use App\Mail\WelcomeEmail;
+use Illuminate\Support\Facades\Mail;
+
+it('sends welcome email on user creation', function () {
+ Mail::fake();
+
+ // Create user through admin flow
+ // ...
+
+ Mail::assertQueued(WelcomeEmail::class, function ($mail) use ($user) {
+ return $mail->user->id === $user->id;
+ });
+});
+
+it('sends email in user preferred language', function () {
+ Mail::fake();
+
+ $user = User::factory()->create(['preferred_language' => 'ar']);
+
+ Mail::to($user)->send(new WelcomeEmail($user, 'password123'));
+
+ Mail::assertSent(WelcomeEmail::class, function ($mail) {
+ return str_contains($mail->envelope()->subject, 'مرحباً');
+ });
+});
+```
+
+## Definition of Done
+
+- [ ] Welcome email sent on user creation
+- [ ] Email contains all required information
+- [ ] Login credentials included
+- [ ] Branding matches design guidelines
+- [ ] Arabic email for Arabic preference
+- [ ] English email for English preference
+- [ ] Plain text fallback works
+- [ ] Email queued (not blocking)
+- [ ] Tests verify email sending
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 2.1:** Individual client creation
+- **Story 2.2:** Company client creation
+- **Epic 8:** Full email infrastructure (shared base template)
+
+## Risk Assessment
+
+- **Primary Risk:** Email delivery failures
+- **Mitigation:** Queue with retry, logging, admin notification on failure
+- **Rollback:** Manual credential sharing if email fails
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-3.1-working-hours-configuration.md b/docs/stories/story-3.1-working-hours-configuration.md
new file mode 100644
index 0000000..8565495
--- /dev/null
+++ b/docs/stories/story-3.1-working-hours-configuration.md
@@ -0,0 +1,261 @@
+# Story 3.1: Working Hours Configuration
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As an **admin**,
+I want **to configure available working hours for each day of the week**,
+So that **clients can only book consultations during my available times**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** working_hours table, availability calendar
+- **Technology:** Livewire Volt, Flux UI forms
+- **Follows pattern:** Admin settings pattern
+- **Touch points:** Booking availability calculation
+
+## Acceptance Criteria
+
+### Working Hours Management
+- [ ] Set available days (enable/disable each day of week)
+- [ ] Set start time for each enabled day
+- [ ] Set end time for each enabled day
+- [ ] Support different hours for different days
+- [ ] 15-minute buffer automatically applied between appointments
+- [ ] 12-hour time format display (AM/PM)
+
+### Configuration Interface
+- [ ] Visual weekly schedule view
+- [ ] Toggle for each day (Sunday-Saturday)
+- [ ] Time pickers for start/end times
+- [ ] Preview of available slots per day
+- [ ] Save button with confirmation
+
+### Behavior
+- [ ] Changes take effect immediately for new bookings
+- [ ] Existing approved bookings NOT affected by changes
+- [ ] Warning if changing hours that have pending bookings
+- [ ] Validation: end time must be after start time
+
+### Quality Requirements
+- [ ] Bilingual labels and messages
+- [ ] Default working hours on initial setup
+- [ ] Audit log entry on changes
+- [ ] Tests for configuration logic
+
+## Technical Notes
+
+### Database Schema
+```php
+// working_hours table
+Schema::create('working_hours', function (Blueprint $table) {
+ $table->id();
+ $table->tinyInteger('day_of_week'); // 0=Sunday, 6=Saturday
+ $table->time('start_time');
+ $table->time('end_time');
+ $table->boolean('is_active')->default(true);
+ $table->timestamps();
+});
+```
+
+### Model
+```php
+ 'boolean',
+ ];
+
+ public static function getDayName(int $dayOfWeek, string $locale = null): string
+ {
+ $locale = $locale ?? app()->getLocale();
+ $days = [
+ 'en' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ 'ar' => ['الأحد', 'الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ ];
+ return $days[$locale][$dayOfWeek] ?? $days['en'][$dayOfWeek];
+ }
+
+ public function getSlots(int $duration = 60): array
+ {
+ $slots = [];
+ $start = Carbon::parse($this->start_time);
+ $end = Carbon::parse($this->end_time);
+
+ while ($start->copy()->addMinutes($duration)->lte($end)) {
+ $slots[] = $start->format('H:i');
+ $start->addMinutes($duration);
+ }
+
+ return $slots;
+ }
+}
+```
+
+### Volt Component
+```php
+first();
+
+ $this->schedule[$day] = [
+ 'is_active' => $workingHour?->is_active ?? false,
+ 'start_time' => $workingHour?->start_time ?? '09:00',
+ 'end_time' => $workingHour?->end_time ?? '17:00',
+ ];
+ }
+ }
+
+ public function save(): void
+ {
+ foreach ($this->schedule as $day => $config) {
+ WorkingHour::updateOrCreate(
+ ['day_of_week' => $day],
+ [
+ 'is_active' => $config['is_active'],
+ 'start_time' => $config['start_time'],
+ 'end_time' => $config['end_time'],
+ ]
+ );
+ }
+
+ // Log action
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'update',
+ 'target_type' => 'working_hours',
+ 'new_values' => $this->schedule,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.working_hours_saved'));
+ }
+};
+```
+
+### Blade Template
+```blade
+
+
{{ __('admin.working_hours') }}
+
+ @foreach(range(0, 6) as $day)
+
+
+
+
+ {{ \App\Models\WorkingHour::getDayName($day) }}
+
+
+ @if($schedule[$day]['is_active'])
+
+ {{ __('common.to') }}
+
+ @else
+ {{ __('admin.closed') }}
+ @endif
+
+ @endforeach
+
+
+ {{ __('common.save') }}
+
+
+```
+
+### Slot Calculation Service
+```php
+dayOfWeek;
+ $workingHour = WorkingHour::where('day_of_week', $dayOfWeek)
+ ->where('is_active', true)
+ ->first();
+
+ if (!$workingHour) {
+ return [];
+ }
+
+ // Get all slots for the day
+ $slots = $workingHour->getSlots(60); // 1 hour slots (45min + 15min buffer)
+
+ // Remove already booked slots
+ $bookedSlots = Consultation::where('scheduled_date', $date->toDateString())
+ ->whereIn('status', ['pending', 'approved'])
+ ->pluck('scheduled_time')
+ ->map(fn($time) => Carbon::parse($time)->format('H:i'))
+ ->toArray();
+
+ // Remove blocked times
+ $blockedSlots = $this->getBlockedSlots($date);
+
+ return array_diff($slots, $bookedSlots, $blockedSlots);
+ }
+}
+```
+
+## Definition of Done
+
+- [ ] Can enable/disable each day of week
+- [ ] Can set start/end times per day
+- [ ] Changes save correctly to database
+- [ ] Existing bookings not affected
+- [ ] Preview shows available slots
+- [ ] 12-hour time format displayed
+- [ ] Audit log created on save
+- [ ] Bilingual support complete
+- [ ] Tests for configuration
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Epic 1:** Database schema, admin authentication
+
+## Risk Assessment
+
+- **Primary Risk:** Changing hours affects availability incorrectly
+- **Mitigation:** Clear separation between existing bookings and new availability
+- **Rollback:** Restore previous working hours from audit log
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-3.2-time-slot-blocking.md b/docs/stories/story-3.2-time-slot-blocking.md
new file mode 100644
index 0000000..6fe6632
--- /dev/null
+++ b/docs/stories/story-3.2-time-slot-blocking.md
@@ -0,0 +1,328 @@
+# Story 3.2: Time Slot Blocking
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As an **admin**,
+I want **to block specific dates or time ranges for personal events or holidays**,
+So that **clients cannot book during my unavailable times**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** blocked_times table, availability calendar
+- **Technology:** Livewire Volt, Flux UI
+- **Follows pattern:** CRUD pattern with calendar integration
+- **Touch points:** Availability calculation service
+
+## Acceptance Criteria
+
+### Block Time Management
+- [ ] Block entire days (all-day events)
+- [ ] Block specific time ranges within a day
+- [ ] Add reason/note for blocked time
+- [ ] View list of all blocked times (upcoming and past)
+- [ ] Edit blocked times
+- [ ] Delete blocked times
+
+### Creating Blocked Time
+- [ ] Select date (date picker)
+- [ ] Choose: All day OR specific time range
+- [ ] If time range: start time and end time
+- [ ] Optional reason/note field
+- [ ] Confirmation on save
+
+### Display & Integration
+- [ ] Blocked times show as unavailable in calendar
+- [ ] Visual distinction from "already booked" slots
+- [ ] Future blocked times don't affect existing approved bookings
+- [ ] Warning if blocking time with pending bookings
+
+### List View
+- [ ] Show all blocked times
+- [ ] Sort by date (upcoming first)
+- [ ] Filter: past/upcoming/all
+- [ ] Quick actions: edit, delete
+- [ ] Show reason if provided
+
+### Quality Requirements
+- [ ] Bilingual support
+- [ ] Audit log for create/edit/delete
+- [ ] Validation: end time after start time
+- [ ] Tests for blocking logic
+
+## Technical Notes
+
+### Database Schema
+```php
+// blocked_times table
+Schema::create('blocked_times', function (Blueprint $table) {
+ $table->id();
+ $table->date('block_date');
+ $table->time('start_time')->nullable(); // null = all day
+ $table->time('end_time')->nullable(); // null = all day
+ $table->string('reason')->nullable();
+ $table->timestamps();
+});
+```
+
+### Model
+```php
+ 'date',
+ ];
+
+ public function isAllDay(): bool
+ {
+ return is_null($this->start_time) && is_null($this->end_time);
+ }
+
+ public function scopeUpcoming($query)
+ {
+ return $query->where('block_date', '>=', today());
+ }
+
+ public function scopePast($query)
+ {
+ return $query->where('block_date', '<', today());
+ }
+
+ public function scopeForDate($query, $date)
+ {
+ return $query->where('block_date', $date);
+ }
+
+ public function blocksSlot(string $time): bool
+ {
+ if ($this->isAllDay()) {
+ return true;
+ }
+
+ $slotTime = Carbon::parse($time);
+ $start = Carbon::parse($this->start_time);
+ $end = Carbon::parse($this->end_time);
+
+ return $slotTime->between($start, $end) ||
+ $slotTime->eq($start);
+ }
+}
+```
+
+### Volt Component for Create/Edit
+```php
+exists) {
+ $this->blockedTime = $blockedTime;
+ $this->block_date = $blockedTime->block_date->format('Y-m-d');
+ $this->is_all_day = $blockedTime->isAllDay();
+ $this->start_time = $blockedTime->start_time ?? '09:00';
+ $this->end_time = $blockedTime->end_time ?? '17:00';
+ $this->reason = $blockedTime->reason ?? '';
+ } else {
+ $this->block_date = today()->format('Y-m-d');
+ }
+ }
+
+ public function save(): void
+ {
+ $validated = $this->validate([
+ 'block_date' => ['required', 'date', 'after_or_equal:today'],
+ 'is_all_day' => ['boolean'],
+ 'start_time' => ['required_if:is_all_day,false'],
+ 'end_time' => ['required_if:is_all_day,false', 'after:start_time'],
+ 'reason' => ['nullable', 'string', 'max:255'],
+ ]);
+
+ $data = [
+ 'block_date' => $this->block_date,
+ 'start_time' => $this->is_all_day ? null : $this->start_time,
+ 'end_time' => $this->is_all_day ? null : $this->end_time,
+ 'reason' => $this->reason ?: null,
+ ];
+
+ if ($this->blockedTime) {
+ $this->blockedTime->update($data);
+ $action = 'update';
+ } else {
+ $this->blockedTime = BlockedTime::create($data);
+ $action = 'create';
+ }
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => $action,
+ 'target_type' => 'blocked_time',
+ 'target_id' => $this->blockedTime->id,
+ 'new_values' => $data,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.blocked_time_saved'));
+ $this->redirect(route('admin.blocked-times.index'));
+ }
+
+ public function delete(): void
+ {
+ $this->blockedTime->delete();
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'delete',
+ 'target_type' => 'blocked_time',
+ 'target_id' => $this->blockedTime->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.blocked_time_deleted'));
+ $this->redirect(route('admin.blocked-times.index'));
+ }
+};
+```
+
+### Integration with Availability Service
+```php
+// In AvailabilityService
+public function getBlockedSlots(Carbon $date): array
+{
+ $blockedTimes = BlockedTime::forDate($date)->get();
+ $blockedSlots = [];
+
+ foreach ($blockedTimes as $blocked) {
+ if ($blocked->isAllDay()) {
+ // Return all possible slots as blocked
+ $workingHour = WorkingHour::where('day_of_week', $date->dayOfWeek)->first();
+ return $workingHour ? $workingHour->getSlots(60) : [];
+ }
+
+ // Get slots that fall within blocked range
+ $start = Carbon::parse($blocked->start_time);
+ $end = Carbon::parse($blocked->end_time);
+ $current = $start->copy();
+
+ while ($current->lt($end)) {
+ $blockedSlots[] = $current->format('H:i');
+ $current->addMinutes(60);
+ }
+ }
+
+ return array_unique($blockedSlots);
+}
+
+public function isDateFullyBlocked(Carbon $date): bool
+{
+ return BlockedTime::forDate($date)
+ ->where(function ($query) {
+ $query->whereNull('start_time')
+ ->whereNull('end_time');
+ })
+ ->exists();
+}
+```
+
+### List View Component
+```blade
+
+
+ {{ __('admin.blocked_times') }}
+
+ {{ __('admin.add_blocked_time') }}
+
+
+
+
+ @forelse($blockedTimes as $blocked)
+
+
+
+ {{ $blocked->block_date->format('d/m/Y') }}
+
+
+ @if($blocked->isAllDay())
+ {{ __('admin.all_day') }}
+ @else
+ {{ $blocked->start_time }} - {{ $blocked->end_time }}
+ @endif
+
+ @if($blocked->reason)
+
+ {{ $blocked->reason }}
+
+ @endif
+
+
+
+ {{ __('common.edit') }}
+
+
+ {{ __('common.delete') }}
+
+
+
+ @empty
+
{{ __('admin.no_blocked_times') }}
+ @endforelse
+
+
+```
+
+## Definition of Done
+
+- [ ] Can create all-day blocks
+- [ ] Can create time-range blocks
+- [ ] Can add reason to blocked time
+- [ ] List view shows all blocked times
+- [ ] Can edit blocked times
+- [ ] Can delete blocked times
+- [ ] Blocked times show as unavailable in calendar
+- [ ] Existing bookings not affected
+- [ ] Audit logging complete
+- [ ] Bilingual support
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.1:** Working hours configuration
+- **Story 3.3:** Availability calendar (consumes blocked times)
+
+## Risk Assessment
+
+- **Primary Risk:** Blocking times with pending bookings
+- **Mitigation:** Warning message, don't auto-cancel existing bookings
+- **Rollback:** Delete blocked time to restore availability
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-3.3-availability-calendar-display.md b/docs/stories/story-3.3-availability-calendar-display.md
new file mode 100644
index 0000000..7f9452b
--- /dev/null
+++ b/docs/stories/story-3.3-availability-calendar-display.md
@@ -0,0 +1,407 @@
+# Story 3.3: Availability Calendar Display
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As a **client**,
+I want **to see a calendar with available time slots**,
+So that **I can choose a convenient time for my consultation**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** working_hours, blocked_times, consultations tables
+- **Technology:** Livewire Volt, JavaScript calendar library
+- **Follows pattern:** Real-time availability checking
+- **Touch points:** Booking submission flow
+
+## Acceptance Criteria
+
+### Calendar Display
+- [ ] Monthly calendar view showing available dates
+- [ ] Visual distinction for date states:
+ - Available (has open slots)
+ - Partially available (some slots taken)
+ - Unavailable (fully booked or blocked)
+ - Past dates (grayed out)
+- [ ] Navigate between months
+- [ ] Current month shown by default
+
+### Time Slot Display
+- [ ] Clicking a date shows available time slots
+- [ ] 1-hour slots (45min consultation + 15min buffer)
+- [ ] Clear indication of slot availability
+- [ ] Unavailable reasons (optional):
+ - Already booked
+ - Outside working hours
+ - Blocked by admin
+
+### Real-time Updates
+- [ ] Availability checked on date selection
+- [ ] Prevent double-booking (race condition handling)
+- [ ] Refresh availability when navigating months
+
+### Responsive Design
+- [ ] Mobile-friendly calendar
+- [ ] Touch-friendly slot selection
+- [ ] Proper RTL support for Arabic
+
+### Quality Requirements
+- [ ] Fast loading (eager load data)
+- [ ] Language-appropriate date formatting
+- [ ] Accessible (keyboard navigation)
+- [ ] Tests for availability logic
+
+## Technical Notes
+
+### Availability Service
+```php
+startOfMonth();
+ $endOfMonth = $startOfMonth->copy()->endOfMonth();
+
+ $availability = [];
+ $current = $startOfMonth->copy();
+
+ while ($current->lte($endOfMonth)) {
+ $availability[$current->format('Y-m-d')] = $this->getDateStatus($current);
+ $current->addDay();
+ }
+
+ return $availability;
+ }
+
+ public function getDateStatus(Carbon $date): string
+ {
+ // Past date
+ if ($date->lt(today())) {
+ return 'past';
+ }
+
+ // Check if fully blocked
+ if ($this->isDateFullyBlocked($date)) {
+ return 'blocked';
+ }
+
+ // Check working hours
+ $workingHour = WorkingHour::where('day_of_week', $date->dayOfWeek)
+ ->where('is_active', true)
+ ->first();
+
+ if (!$workingHour) {
+ return 'closed';
+ }
+
+ // Get available slots
+ $availableSlots = $this->getAvailableSlots($date);
+
+ if (empty($availableSlots)) {
+ return 'full';
+ }
+
+ $totalSlots = count($workingHour->getSlots(60));
+ if (count($availableSlots) < $totalSlots) {
+ return 'partial';
+ }
+
+ return 'available';
+ }
+
+ public function getAvailableSlots(Carbon $date): array
+ {
+ $workingHour = WorkingHour::where('day_of_week', $date->dayOfWeek)
+ ->where('is_active', true)
+ ->first();
+
+ if (!$workingHour) {
+ return [];
+ }
+
+ // All possible slots
+ $allSlots = $workingHour->getSlots(60);
+
+ // Booked slots
+ $bookedSlots = Consultation::where('scheduled_date', $date->toDateString())
+ ->whereIn('status', ['pending', 'approved'])
+ ->pluck('scheduled_time')
+ ->map(fn($t) => Carbon::parse($t)->format('H:i'))
+ ->toArray();
+
+ // Blocked slots
+ $blockedSlots = $this->getBlockedSlots($date);
+
+ return array_values(array_diff($allSlots, $bookedSlots, $blockedSlots));
+ }
+
+ private function getBlockedSlots(Carbon $date): array
+ {
+ $blockedTimes = BlockedTime::where('block_date', $date->toDateString())->get();
+ $blockedSlots = [];
+
+ foreach ($blockedTimes as $blocked) {
+ if ($blocked->isAllDay()) {
+ // Block all slots
+ $workingHour = WorkingHour::where('day_of_week', $date->dayOfWeek)->first();
+ return $workingHour ? $workingHour->getSlots(60) : [];
+ }
+
+ // Calculate blocked slots from time range
+ $start = Carbon::parse($blocked->start_time);
+ $end = Carbon::parse($blocked->end_time);
+ $current = $start->copy();
+
+ while ($current->lt($end)) {
+ $blockedSlots[] = $current->format('H:i');
+ $current->addMinutes(60);
+ }
+ }
+
+ return array_unique($blockedSlots);
+ }
+
+ private function isDateFullyBlocked(Carbon $date): bool
+ {
+ return BlockedTime::where('block_date', $date->toDateString())
+ ->whereNull('start_time')
+ ->exists();
+ }
+}
+```
+
+### Volt Component
+```php
+year = now()->year;
+ $this->month = now()->month;
+ $this->loadMonthAvailability();
+ }
+
+ public function loadMonthAvailability(): void
+ {
+ $service = app(AvailabilityService::class);
+ $this->monthAvailability = $service->getMonthAvailability($this->year, $this->month);
+ }
+
+ public function previousMonth(): void
+ {
+ $date = Carbon::create($this->year, $this->month, 1)->subMonth();
+ $this->year = $date->year;
+ $this->month = $date->month;
+ $this->selectedDate = null;
+ $this->availableSlots = [];
+ $this->loadMonthAvailability();
+ }
+
+ public function nextMonth(): void
+ {
+ $date = Carbon::create($this->year, $this->month, 1)->addMonth();
+ $this->year = $date->year;
+ $this->month = $date->month;
+ $this->selectedDate = null;
+ $this->availableSlots = [];
+ $this->loadMonthAvailability();
+ }
+
+ public function selectDate(string $date): void
+ {
+ $status = $this->monthAvailability[$date] ?? 'unavailable';
+
+ if (in_array($status, ['available', 'partial'])) {
+ $this->selectedDate = $date;
+ $this->loadAvailableSlots();
+ }
+ }
+
+ public function loadAvailableSlots(): void
+ {
+ if (!$this->selectedDate) {
+ $this->availableSlots = [];
+ return;
+ }
+
+ $service = app(AvailabilityService::class);
+ $this->availableSlots = $service->getAvailableSlots(
+ Carbon::parse($this->selectedDate)
+ );
+ }
+
+ public function with(): array
+ {
+ return [
+ 'monthName' => Carbon::create($this->year, $this->month, 1)->translatedFormat('F Y'),
+ 'calendarDays' => $this->buildCalendarDays(),
+ ];
+ }
+
+ private function buildCalendarDays(): array
+ {
+ $firstDay = Carbon::create($this->year, $this->month, 1);
+ $lastDay = $firstDay->copy()->endOfMonth();
+
+ // Pad start of month
+ $startPadding = $firstDay->dayOfWeek;
+ $days = array_fill(0, $startPadding, null);
+
+ // Fill month days
+ $current = $firstDay->copy();
+ while ($current->lte($lastDay)) {
+ $dateStr = $current->format('Y-m-d');
+ $days[] = [
+ 'date' => $dateStr,
+ 'day' => $current->day,
+ 'status' => $this->monthAvailability[$dateStr] ?? 'unavailable',
+ ];
+ $current->addDay();
+ }
+
+ return $days;
+ }
+};
+```
+
+### Blade Template
+```blade
+
+
+
+
+
+
+
+ {{ $monthName }}
+
+
+
+
+
+
+
+
+ @foreach(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as $day)
+
+ {{ __("calendar.{$day}") }}
+
+ @endforeach
+
+
+
+
+ @foreach($calendarDays as $dayData)
+ @if($dayData === null)
+
+ @else
+
+ @endif
+ @endforeach
+
+
+
+ @if($selectedDate)
+
+
+ {{ __('booking.available_times') }} -
+ {{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('d M Y') }}
+
+
+ @if(count($availableSlots) > 0)
+
+ @foreach($availableSlots as $slot)
+
+ @endforeach
+
+ @else
+
{{ __('booking.no_slots_available') }}
+ @endif
+
+ @endif
+
+
+
+
+
+
{{ __('booking.available') }}
+
+
+
+
{{ __('booking.partial') }}
+
+
+
+
{{ __('booking.unavailable') }}
+
+
+
+```
+
+## Definition of Done
+
+- [ ] Calendar displays current month
+- [ ] Can navigate between months
+- [ ] Available dates clearly indicated
+- [ ] Clicking date shows time slots
+- [ ] Time slots in 1-hour increments
+- [ ] Prevents selecting unavailable dates/times
+- [ ] Real-time availability updates
+- [ ] Mobile responsive
+- [ ] RTL support for Arabic
+- [ ] Tests for availability logic
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.1:** Working hours (defines available time)
+- **Story 3.2:** Blocked times (removes availability)
+- **Story 3.4:** Booking submission (consumes selected slot)
+
+## Risk Assessment
+
+- **Primary Risk:** Race condition on slot selection
+- **Mitigation:** Database-level unique constraint, check on submission
+- **Rollback:** Refresh availability if booking fails
+
+## Estimation
+
+**Complexity:** High
+**Estimated Effort:** 5-6 hours
diff --git a/docs/stories/story-3.4-booking-request-submission.md b/docs/stories/story-3.4-booking-request-submission.md
new file mode 100644
index 0000000..23831a5
--- /dev/null
+++ b/docs/stories/story-3.4-booking-request-submission.md
@@ -0,0 +1,334 @@
+# Story 3.4: Booking Request Submission
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As a **client**,
+I want **to submit a consultation booking request**,
+So that **I can schedule a meeting with the lawyer**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** consultations table, availability calendar, notifications
+- **Technology:** Livewire Volt, form validation
+- **Follows pattern:** Form submission with confirmation
+- **Touch points:** Client dashboard, admin notifications
+
+## Acceptance Criteria
+
+### Booking Form
+- [ ] Client must be logged in
+- [ ] Select date from availability calendar
+- [ ] Select available time slot
+- [ ] Problem summary field (required, textarea)
+- [ ] Confirmation before submission
+
+### Validation & Constraints
+- [ ] Validate: no more than 1 booking per day for this client
+- [ ] Validate: selected slot is still available
+- [ ] Validate: problem summary is not empty
+- [ ] Show clear error messages for violations
+
+### Submission Flow
+- [ ] Booking enters "pending" status
+- [ ] Client sees "Pending Review" confirmation
+- [ ] Admin receives email notification
+- [ ] Client receives submission confirmation email
+- [ ] Redirect to consultations list after submission
+
+### UI/UX
+- [ ] Clear step-by-step flow
+- [ ] Loading state during submission
+- [ ] Success message with next steps
+- [ ] Bilingual labels and messages
+
+### Quality Requirements
+- [ ] Prevent double-booking (race condition)
+- [ ] Audit log entry for booking creation
+- [ ] Tests for submission flow
+- [ ] Tests for validation rules
+
+## Technical Notes
+
+### Database Record
+```php
+// consultations table fields on creation
+$consultation = Consultation::create([
+ 'user_id' => auth()->id(),
+ 'scheduled_date' => $selectedDate,
+ 'scheduled_time' => $selectedTime,
+ 'duration' => 45, // default
+ 'status' => 'pending',
+ 'type' => null, // admin sets this later
+ 'payment_amount' => null,
+ 'payment_status' => 'not_applicable',
+ 'problem_summary' => $problemSummary,
+]);
+```
+
+### Volt Component
+```php
+selectedDate = $date;
+ $this->selectedTime = $time;
+ }
+
+ public function clearSelection(): void
+ {
+ $this->selectedDate = null;
+ $this->selectedTime = null;
+ }
+
+ public function showConfirm(): void
+ {
+ $this->validate([
+ 'selectedDate' => ['required', 'date', 'after_or_equal:today'],
+ 'selectedTime' => ['required'],
+ 'problemSummary' => ['required', 'string', 'min:20', 'max:2000'],
+ ]);
+
+ // Check 1-per-day limit
+ $existingBooking = Consultation::where('user_id', auth()->id())
+ ->where('scheduled_date', $this->selectedDate)
+ ->whereIn('status', ['pending', 'approved'])
+ ->exists();
+
+ if ($existingBooking) {
+ $this->addError('selectedDate', __('booking.already_booked_this_day'));
+ return;
+ }
+
+ // Verify slot still available
+ $service = app(AvailabilityService::class);
+ $availableSlots = $service->getAvailableSlots(Carbon::parse($this->selectedDate));
+
+ if (!in_array($this->selectedTime, $availableSlots)) {
+ $this->addError('selectedTime', __('booking.slot_no_longer_available'));
+ return;
+ }
+
+ $this->showConfirmation = true;
+ }
+
+ public function submit(): void
+ {
+ // Double-check availability with lock
+ DB::transaction(function () {
+ // Check slot one more time with lock
+ $exists = Consultation::where('scheduled_date', $this->selectedDate)
+ ->where('scheduled_time', $this->selectedTime)
+ ->whereIn('status', ['pending', 'approved'])
+ ->lockForUpdate()
+ ->exists();
+
+ if ($exists) {
+ throw new \Exception(__('booking.slot_taken'));
+ }
+
+ // Check 1-per-day again
+ $userBooking = Consultation::where('user_id', auth()->id())
+ ->where('scheduled_date', $this->selectedDate)
+ ->whereIn('status', ['pending', 'approved'])
+ ->lockForUpdate()
+ ->exists();
+
+ if ($userBooking) {
+ throw new \Exception(__('booking.already_booked_this_day'));
+ }
+
+ // Create booking
+ $consultation = Consultation::create([
+ 'user_id' => auth()->id(),
+ 'scheduled_date' => $this->selectedDate,
+ 'scheduled_time' => $this->selectedTime,
+ 'duration' => 45,
+ 'status' => 'pending',
+ 'problem_summary' => $this->problemSummary,
+ ]);
+
+ // Send notifications
+ auth()->user()->notify(new BookingSubmittedClient($consultation));
+
+ // Notify admin
+ $admin = User::where('user_type', 'admin')->first();
+ $admin?->notify(new NewBookingAdmin($consultation));
+
+ // Log action
+ AdminLog::create([
+ 'admin_id' => null, // Client action
+ 'action_type' => 'create',
+ 'target_type' => 'consultation',
+ 'target_id' => $consultation->id,
+ 'new_values' => $consultation->toArray(),
+ 'ip_address' => request()->ip(),
+ ]);
+ });
+
+ session()->flash('success', __('booking.submitted_successfully'));
+ $this->redirect(route('client.consultations.index'));
+ }
+};
+```
+
+### Blade Template
+```blade
+
+
{{ __('booking.request_consultation') }}
+
+ @if(!$selectedDate || !$selectedTime)
+
+
+
{{ __('booking.select_date_time') }}
+
+
+ @else
+
+
+
+
+
+
+
{{ __('booking.selected_time') }}
+
{{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('l, d M Y') }}
+
{{ \Carbon\Carbon::parse($selectedTime)->format('g:i A') }}
+
+
+ {{ __('common.change') }}
+
+
+
+
+ @if(!$showConfirmation)
+
+
+ {{ __('booking.problem_summary') }} *
+
+
+ {{ __('booking.problem_summary_help') }}
+
+
+
+
+
+ {{ __('booking.continue') }}
+ {{ __('common.loading') }}
+
+ @else
+
+
+ {{ __('booking.confirm_booking') }}
+ {{ __('booking.confirm_message') }}
+
+
+
{{ __('booking.date') }}:
+ {{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('l, d M Y') }}
+
{{ __('booking.time') }}:
+ {{ \Carbon\Carbon::parse($selectedTime)->format('g:i A') }}
+
{{ __('booking.duration') }}: 45 {{ __('common.minutes') }}
+
+
+
+
{{ __('booking.problem_summary') }}:
+
{{ $problemSummary }}
+
+
+
+
+
+ {{ __('common.back') }}
+
+
+ {{ __('booking.submit_request') }}
+ {{ __('common.submitting') }}
+
+
+ @endif
+
+ @endif
+
+```
+
+### 1-Per-Day Validation Rule
+```php
+// Custom validation rule
+use Illuminate\Contracts\Validation\ValidationRule;
+
+class OneBookingPerDay implements ValidationRule
+{
+ public function validate(string $attribute, mixed $value, Closure $fail): void
+ {
+ $exists = Consultation::where('user_id', auth()->id())
+ ->where('scheduled_date', $value)
+ ->whereIn('status', ['pending', 'approved'])
+ ->exists();
+
+ if ($exists) {
+ $fail(__('booking.already_booked_this_day'));
+ }
+ }
+}
+```
+
+## Definition of Done
+
+- [ ] Can select date from calendar
+- [ ] Can select time slot
+- [ ] Problem summary required
+- [ ] 1-per-day limit enforced
+- [ ] Race condition prevented
+- [ ] Confirmation step before submission
+- [ ] Booking created with "pending" status
+- [ ] Client notification sent
+- [ ] Admin notification sent
+- [ ] Bilingual support complete
+- [ ] Tests for submission flow
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.3:** Availability calendar
+- **Epic 2:** User authentication
+- **Epic 8:** Email notifications (partial)
+
+## Risk Assessment
+
+- **Primary Risk:** Double-booking from concurrent submissions
+- **Mitigation:** Database transaction with row locking
+- **Rollback:** Return to calendar with error message
+
+## Estimation
+
+**Complexity:** Medium-High
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-3.5-admin-booking-review-approval.md b/docs/stories/story-3.5-admin-booking-review-approval.md
new file mode 100644
index 0000000..79e032e
--- /dev/null
+++ b/docs/stories/story-3.5-admin-booking-review-approval.md
@@ -0,0 +1,328 @@
+# Story 3.5: Admin Booking Review & Approval
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As an **admin**,
+I want **to review, categorize, and approve or reject booking requests**,
+So that **I can manage my consultation schedule and set appropriate consultation types**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** consultations table, notifications, .ics generation
+- **Technology:** Livewire Volt, Flux UI
+- **Follows pattern:** Admin action workflow
+- **Touch points:** Client notifications, calendar file
+
+## Acceptance Criteria
+
+### Pending Bookings List
+- [ ] View all pending booking requests
+- [ ] Display: client name, requested date/time, submission date
+- [ ] Show problem summary preview
+- [ ] Click to view full details
+- [ ] Sort by date (oldest first default)
+- [ ] Filter by date range
+
+### Booking Details View
+- [ ] Full client information
+- [ ] Complete problem summary
+- [ ] Client consultation history
+- [ ] Requested date and time
+
+### Approval Workflow
+- [ ] Set consultation type:
+ - Free consultation
+ - Paid consultation
+- [ ] If paid: set payment amount
+- [ ] If paid: add payment instructions (optional)
+- [ ] Approve button with confirmation
+- [ ] On approval:
+ - Status changes to 'approved'
+ - Client notified via email
+ - .ics calendar file attached to email
+ - Payment instructions included if paid
+
+### Rejection Workflow
+- [ ] Optional rejection reason field
+- [ ] Reject button with confirmation
+- [ ] On rejection:
+ - Status changes to 'rejected'
+ - Client notified via email with reason
+
+### Quick Actions
+- [ ] Quick approve (free) button on list
+- [ ] Quick reject button on list
+- [ ] Bulk actions (optional)
+
+### Quality Requirements
+- [ ] Audit log for all decisions
+- [ ] Bilingual notifications
+- [ ] Tests for approval/rejection flow
+
+## Technical Notes
+
+### Consultation Status Flow
+```
+pending -> approved (admin approves)
+pending -> rejected (admin rejects)
+approved -> completed (after consultation)
+approved -> no_show (client didn't attend)
+approved -> cancelled (admin cancels)
+```
+
+### Volt Component for Review
+```php
+consultation = $consultation;
+ }
+
+ public function approve(): void
+ {
+ $this->validate([
+ 'consultationType' => ['required', 'in:free,paid'],
+ 'paymentAmount' => ['required_if:consultationType,paid', 'nullable', 'numeric', 'min:0'],
+ 'paymentInstructions' => ['nullable', 'string', 'max:1000'],
+ ]);
+
+ $this->consultation->update([
+ 'status' => 'approved',
+ 'type' => $this->consultationType,
+ 'payment_amount' => $this->consultationType === 'paid' ? $this->paymentAmount : null,
+ 'payment_status' => $this->consultationType === 'paid' ? 'pending' : 'not_applicable',
+ ]);
+
+ // Generate calendar file
+ $calendarService = app(CalendarService::class);
+ $icsContent = $calendarService->generateIcs($this->consultation);
+
+ // Send notification with .ics attachment
+ $this->consultation->user->notify(
+ new BookingApproved(
+ $this->consultation,
+ $icsContent,
+ $this->paymentInstructions
+ )
+ );
+
+ // Log action
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'approve',
+ 'target_type' => 'consultation',
+ 'target_id' => $this->consultation->id,
+ 'old_values' => ['status' => 'pending'],
+ 'new_values' => [
+ 'status' => 'approved',
+ 'type' => $this->consultationType,
+ 'payment_amount' => $this->paymentAmount,
+ ],
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.booking_approved'));
+ $this->redirect(route('admin.bookings.pending'));
+ }
+
+ public function reject(): void
+ {
+ $this->validate([
+ 'rejectionReason' => ['nullable', 'string', 'max:1000'],
+ ]);
+
+ $this->consultation->update([
+ 'status' => 'rejected',
+ ]);
+
+ // Send rejection notification
+ $this->consultation->user->notify(
+ new BookingRejected($this->consultation, $this->rejectionReason)
+ );
+
+ // Log action
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'reject',
+ 'target_type' => 'consultation',
+ 'target_id' => $this->consultation->id,
+ 'old_values' => ['status' => 'pending'],
+ 'new_values' => [
+ 'status' => 'rejected',
+ 'reason' => $this->rejectionReason,
+ ],
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.booking_rejected'));
+ $this->redirect(route('admin.bookings.pending'));
+ }
+};
+```
+
+### Blade Template for Approval Modal
+```blade
+
+ {{ __('admin.approve_booking') }}
+
+
+
+
+
{{ __('admin.client') }}: {{ $consultation->user->name }}
+
{{ __('admin.date') }}:
+ {{ $consultation->scheduled_date->translatedFormat('l, d M Y') }}
+
{{ __('admin.time') }}:
+ {{ Carbon::parse($consultation->scheduled_time)->format('g:i A') }}
+
+
+
+
+ {{ __('admin.consultation_type') }}
+
+
+
+
+
+
+
+ @if($consultationType === 'paid')
+
+ {{ __('admin.payment_amount') }} *
+
+
+
+
+
+ {{ __('admin.payment_instructions') }}
+
+
+ @endif
+
+
+
+
+ {{ __('common.cancel') }}
+
+
+ {{ __('admin.approve') }}
+
+
+
+```
+
+### Pending Bookings List Component
+```php
+ Consultation::where('status', 'pending')
+ ->when($this->dateFrom, fn($q) => $q->where('scheduled_date', '>=', $this->dateFrom))
+ ->when($this->dateTo, fn($q) => $q->where('scheduled_date', '<=', $this->dateTo))
+ ->with('user')
+ ->orderBy('scheduled_date')
+ ->orderBy('scheduled_time')
+ ->paginate(15),
+ ];
+ }
+
+ public function quickApprove(int $id): void
+ {
+ $consultation = Consultation::findOrFail($id);
+ $consultation->update([
+ 'status' => 'approved',
+ 'type' => 'free',
+ 'payment_status' => 'not_applicable',
+ ]);
+
+ // Generate and send notification with .ics
+ // ...
+
+ session()->flash('success', __('messages.booking_approved'));
+ }
+
+ public function quickReject(int $id): void
+ {
+ $consultation = Consultation::findOrFail($id);
+ $consultation->update(['status' => 'rejected']);
+
+ // Send rejection notification
+ // ...
+
+ session()->flash('success', __('messages.booking_rejected'));
+ }
+};
+```
+
+## Definition of Done
+
+- [ ] Pending bookings list displays correctly
+- [ ] Can view booking details
+- [ ] Can approve as free consultation
+- [ ] Can approve as paid with amount
+- [ ] Can reject with optional reason
+- [ ] Approval sends email with .ics file
+- [ ] Rejection sends email with reason
+- [ ] Quick actions work from list
+- [ ] Audit log entries created
+- [ ] Bilingual support complete
+- [ ] Tests for approval/rejection
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.4:** Booking submission (creates pending bookings)
+- **Story 3.6:** Calendar file generation (.ics)
+- **Epic 8:** Email notifications
+
+## Risk Assessment
+
+- **Primary Risk:** Approving wrong booking
+- **Mitigation:** Confirmation dialog, clear booking details display
+- **Rollback:** Admin can cancel approved booking
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-3.6-calendar-file-generation.md b/docs/stories/story-3.6-calendar-file-generation.md
new file mode 100644
index 0000000..9d094f1
--- /dev/null
+++ b/docs/stories/story-3.6-calendar-file-generation.md
@@ -0,0 +1,309 @@
+# Story 3.6: Calendar File Generation (.ics)
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As a **client**,
+I want **to receive a calendar file when my booking is approved**,
+So that **I can easily add the consultation to my calendar app**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** Consultation model, email attachments
+- **Technology:** iCalendar format (RFC 5545)
+- **Follows pattern:** Service class for generation
+- **Touch points:** Approval email, client dashboard download
+
+## Acceptance Criteria
+
+### Calendar File Generation
+- [ ] Generate valid .ics file on booking approval
+- [ ] File follows iCalendar specification (RFC 5545)
+- [ ] Compatible with major calendar apps:
+ - Google Calendar
+ - Apple Calendar
+ - Microsoft Outlook
+ - Other standard clients
+
+### Event Details
+- [ ] Event title: "Consultation with Libra Law Firm" (bilingual)
+- [ ] Date and time (correct timezone)
+- [ ] Duration: 45 minutes
+- [ ] Location (office address or "Phone consultation")
+- [ ] Description with:
+ - Booking reference
+ - Consultation type (free/paid)
+ - Contact information
+- [ ] Reminder: 1 hour before
+
+### Delivery
+- [ ] Attach to approval email
+- [ ] Available for download from client dashboard
+- [ ] Proper MIME type (text/calendar)
+- [ ] Correct filename (consultation-{date}.ics)
+
+### Language Support
+- [ ] Event title in client's preferred language
+- [ ] Description in client's preferred language
+
+### Quality Requirements
+- [ ] Valid iCalendar format (passes validators)
+- [ ] Tests for file generation
+- [ ] Tests for calendar app compatibility
+
+## Technical Notes
+
+### Calendar Service
+```php
+user;
+ $locale = $user->preferred_language ?? 'ar';
+
+ $startDateTime = Carbon::parse(
+ $consultation->scheduled_date->format('Y-m-d') . ' ' . $consultation->scheduled_time
+ );
+ $endDateTime = $startDateTime->copy()->addMinutes($consultation->duration);
+
+ $title = $locale === 'ar'
+ ? 'استشارة مع مكتب ليبرا للمحاماة'
+ : 'Consultation with Libra Law Firm';
+
+ $description = $this->buildDescription($consultation, $locale);
+ $location = $this->getLocation($locale);
+
+ $uid = sprintf(
+ 'consultation-%d@libra.ps',
+ $consultation->id
+ );
+
+ $ics = [
+ 'BEGIN:VCALENDAR',
+ 'VERSION:2.0',
+ 'PRODID:-//Libra Law Firm//Consultation Booking//EN',
+ 'CALSCALE:GREGORIAN',
+ 'METHOD:REQUEST',
+ 'BEGIN:VTIMEZONE',
+ 'TZID:Asia/Jerusalem',
+ 'BEGIN:STANDARD',
+ 'DTSTART:19701025T020000',
+ 'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU',
+ 'TZOFFSETFROM:+0300',
+ 'TZOFFSETTO:+0200',
+ 'END:STANDARD',
+ 'BEGIN:DAYLIGHT',
+ 'DTSTART:19700329T020000',
+ 'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1FR',
+ 'TZOFFSETFROM:+0200',
+ 'TZOFFSETTO:+0300',
+ 'END:DAYLIGHT',
+ 'END:VTIMEZONE',
+ 'BEGIN:VEVENT',
+ 'UID:' . $uid,
+ 'DTSTAMP:' . gmdate('Ymd\THis\Z'),
+ 'DTSTART;TZID=Asia/Jerusalem:' . $startDateTime->format('Ymd\THis'),
+ 'DTEND;TZID=Asia/Jerusalem:' . $endDateTime->format('Ymd\THis'),
+ 'SUMMARY:' . $this->escapeIcs($title),
+ 'DESCRIPTION:' . $this->escapeIcs($description),
+ 'LOCATION:' . $this->escapeIcs($location),
+ 'STATUS:CONFIRMED',
+ 'SEQUENCE:0',
+ 'BEGIN:VALARM',
+ 'TRIGGER:-PT1H',
+ 'ACTION:DISPLAY',
+ 'DESCRIPTION:Reminder',
+ 'END:VALARM',
+ 'END:VEVENT',
+ 'END:VCALENDAR',
+ ];
+
+ return implode("\r\n", $ics);
+ }
+
+ private function buildDescription(Consultation $consultation, string $locale): string
+ {
+ $lines = [];
+
+ if ($locale === 'ar') {
+ $lines[] = 'رقم الحجز: ' . $consultation->id;
+ $lines[] = 'نوع الاستشارة: ' . ($consultation->type === 'free' ? 'مجانية' : 'مدفوعة');
+ if ($consultation->type === 'paid') {
+ $lines[] = 'المبلغ: ' . number_format($consultation->payment_amount, 2) . ' شيكل';
+ }
+ $lines[] = '';
+ $lines[] = 'للاستفسارات:';
+ $lines[] = 'مكتب ليبرا للمحاماة';
+ $lines[] = 'libra.ps';
+ } else {
+ $lines[] = 'Booking Reference: ' . $consultation->id;
+ $lines[] = 'Consultation Type: ' . ucfirst($consultation->type);
+ if ($consultation->type === 'paid') {
+ $lines[] = 'Amount: ' . number_format($consultation->payment_amount, 2) . ' ILS';
+ }
+ $lines[] = '';
+ $lines[] = 'For inquiries:';
+ $lines[] = 'Libra Law Firm';
+ $lines[] = 'libra.ps';
+ }
+
+ return implode('\n', $lines);
+ }
+
+ private function getLocation(string $locale): string
+ {
+ // Configure in config/libra.php
+ return config('libra.office_address.' . $locale, 'Libra Law Firm');
+ }
+
+ private function escapeIcs(string $text): string
+ {
+ return str_replace(
+ [',', ';', '\\'],
+ ['\,', '\;', '\\\\'],
+ $text
+ );
+ }
+
+ public function generateDownloadResponse(Consultation $consultation): \Symfony\Component\HttpFoundation\Response
+ {
+ $content = $this->generateIcs($consultation);
+ $filename = sprintf(
+ 'consultation-%s.ics',
+ $consultation->scheduled_date->format('Y-m-d')
+ );
+
+ return response($content)
+ ->header('Content-Type', 'text/calendar; charset=utf-8')
+ ->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
+ }
+}
+```
+
+### Email Attachment
+```php
+// In BookingApproved notification
+public function toMail(object $notifiable): MailMessage
+{
+ $locale = $notifiable->preferred_language ?? 'ar';
+
+ return (new MailMessage)
+ ->subject($this->getSubject($locale))
+ ->markdown('emails.booking.approved.' . $locale, [
+ 'consultation' => $this->consultation,
+ 'paymentInstructions' => $this->paymentInstructions,
+ ])
+ ->attachData(
+ $this->icsContent,
+ 'consultation.ics',
+ ['mime' => 'text/calendar']
+ );
+}
+```
+
+### Download Route
+```php
+// routes/web.php
+Route::middleware(['auth'])->group(function () {
+ Route::get('/consultations/{consultation}/calendar', function (Consultation $consultation) {
+ // Verify user owns this consultation
+ abort_unless($consultation->user_id === auth()->id(), 403);
+ abort_unless($consultation->status === 'approved', 404);
+
+ return app(CalendarService::class)->generateDownloadResponse($consultation);
+ })->name('client.consultations.calendar');
+});
+```
+
+### Client Dashboard Button
+```blade
+@if($consultation->status === 'approved')
+
+
+ {{ __('client.add_to_calendar') }}
+
+@endif
+```
+
+### Testing Calendar File
+```php
+use App\Services\CalendarService;
+use App\Models\Consultation;
+
+it('generates valid ics file', function () {
+ $consultation = Consultation::factory()->approved()->create([
+ 'scheduled_date' => '2024-03-15',
+ 'scheduled_time' => '10:00:00',
+ 'duration' => 45,
+ ]);
+
+ $service = new CalendarService();
+ $ics = $service->generateIcs($consultation);
+
+ expect($ics)
+ ->toContain('BEGIN:VCALENDAR')
+ ->toContain('BEGIN:VEVENT')
+ ->toContain('DTSTART')
+ ->toContain('DTEND')
+ ->toContain('END:VCALENDAR');
+});
+
+it('includes correct duration', function () {
+ $consultation = Consultation::factory()->approved()->create([
+ 'scheduled_date' => '2024-03-15',
+ 'scheduled_time' => '10:00:00',
+ 'duration' => 45,
+ ]);
+
+ $service = new CalendarService();
+ $ics = $service->generateIcs($consultation);
+
+ // Start at 10:00, end at 10:45
+ expect($ics)
+ ->toContain('DTSTART;TZID=Asia/Jerusalem:20240315T100000')
+ ->toContain('DTEND;TZID=Asia/Jerusalem:20240315T104500');
+});
+```
+
+## Definition of Done
+
+- [ ] .ics file generated on approval
+- [ ] File follows iCalendar RFC 5545
+- [ ] Works with Google Calendar
+- [ ] Works with Apple Calendar
+- [ ] Works with Microsoft Outlook
+- [ ] Attached to approval email
+- [ ] Downloadable from client dashboard
+- [ ] Bilingual event details
+- [ ] Includes 1-hour reminder
+- [ ] Tests pass for generation
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.5:** Booking approval (triggers generation)
+- **Epic 8:** Email system (for attachment)
+
+## Risk Assessment
+
+- **Primary Risk:** Calendar app compatibility issues
+- **Mitigation:** Test with multiple calendar apps, follow RFC strictly
+- **Rollback:** Provide manual calendar details if .ics fails
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-3.7-consultation-management.md b/docs/stories/story-3.7-consultation-management.md
new file mode 100644
index 0000000..b25f8ce
--- /dev/null
+++ b/docs/stories/story-3.7-consultation-management.md
@@ -0,0 +1,384 @@
+# Story 3.7: Consultation Management
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As an **admin**,
+I want **to manage consultations throughout their lifecycle**,
+So that **I can track completed sessions, handle no-shows, and maintain accurate records**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** consultations table, notifications
+- **Technology:** Livewire Volt, Flux UI
+- **Follows pattern:** Admin management dashboard
+- **Touch points:** Consultation status, payment tracking, admin notes
+
+## Acceptance Criteria
+
+### Consultations List View
+- [ ] View all consultations with filters:
+ - Status (pending/approved/completed/cancelled/no_show)
+ - Type (free/paid)
+ - Payment status (pending/received/not_applicable)
+ - Date range
+ - Client name/email search
+- [ ] Sort by date, status, client name
+- [ ] Pagination (15/25/50 per page)
+- [ ] Quick status indicators
+
+### Status Management
+- [ ] Mark consultation as completed
+- [ ] Mark consultation as no-show
+- [ ] Cancel booking on behalf of client
+- [ ] Status change confirmation
+
+### Rescheduling
+- [ ] Reschedule appointment to new date/time
+- [ ] Validate new slot availability
+- [ ] Send notification to client
+- [ ] Generate new .ics file
+
+### Payment Tracking
+- [ ] Mark payment as received (for paid consultations)
+- [ ] Payment date recorded
+- [ ] Payment status visible in list
+
+### Admin Notes
+- [ ] Add internal admin notes
+- [ ] Notes not visible to client
+- [ ] View notes in consultation detail
+- [ ] Edit/delete notes
+
+### Client History
+- [ ] View all consultations for a specific client
+- [ ] Linked from user profile
+- [ ] Summary statistics per client
+
+### Quality Requirements
+- [ ] Audit log for all status changes
+- [ ] Bilingual labels
+- [ ] Tests for status transitions
+
+## Technical Notes
+
+### Status Enum
+```php
+enum ConsultationStatus: string
+{
+ case Pending = 'pending';
+ case Approved = 'approved';
+ case Completed = 'completed';
+ case Cancelled = 'cancelled';
+ case NoShow = 'no_show';
+}
+
+enum PaymentStatus: string
+{
+ case Pending = 'pending';
+ case Received = 'received';
+ case NotApplicable = 'not_applicable';
+}
+```
+
+### Consultation Model Methods
+```php
+class Consultation extends Model
+{
+ public function markAsCompleted(): void
+ {
+ $this->update(['status' => ConsultationStatus::Completed]);
+ }
+
+ public function markAsNoShow(): void
+ {
+ $this->update(['status' => ConsultationStatus::NoShow]);
+ }
+
+ public function cancel(): void
+ {
+ $this->update(['status' => ConsultationStatus::Cancelled]);
+ }
+
+ public function markPaymentReceived(): void
+ {
+ $this->update([
+ 'payment_status' => PaymentStatus::Received,
+ 'payment_received_at' => now(),
+ ]);
+ }
+
+ public function reschedule(string $newDate, string $newTime): void
+ {
+ $this->update([
+ 'scheduled_date' => $newDate,
+ 'scheduled_time' => $newTime,
+ ]);
+ }
+
+ // Scopes
+ public function scopeUpcoming($query)
+ {
+ return $query->where('scheduled_date', '>=', today())
+ ->where('status', ConsultationStatus::Approved);
+ }
+
+ public function scopePast($query)
+ {
+ return $query->where('scheduled_date', '<', today())
+ ->orWhereIn('status', [
+ ConsultationStatus::Completed,
+ ConsultationStatus::Cancelled,
+ ConsultationStatus::NoShow,
+ ]);
+ }
+}
+```
+
+### Volt Component for Management
+```php
+resetPage();
+ }
+
+ public function markCompleted(int $id): void
+ {
+ $consultation = Consultation::findOrFail($id);
+ $oldStatus = $consultation->status;
+
+ $consultation->markAsCompleted();
+
+ $this->logStatusChange($consultation, $oldStatus, 'completed');
+
+ session()->flash('success', __('messages.marked_completed'));
+ }
+
+ public function markNoShow(int $id): void
+ {
+ $consultation = Consultation::findOrFail($id);
+ $oldStatus = $consultation->status;
+
+ $consultation->markAsNoShow();
+
+ $this->logStatusChange($consultation, $oldStatus, 'no_show');
+
+ session()->flash('success', __('messages.marked_no_show'));
+ }
+
+ public function cancel(int $id): void
+ {
+ $consultation = Consultation::findOrFail($id);
+ $oldStatus = $consultation->status;
+
+ $consultation->cancel();
+
+ // Notify client
+ $consultation->user->notify(new ConsultationCancelled($consultation));
+
+ $this->logStatusChange($consultation, $oldStatus, 'cancelled');
+
+ session()->flash('success', __('messages.consultation_cancelled'));
+ }
+
+ public function markPaymentReceived(int $id): void
+ {
+ $consultation = Consultation::findOrFail($id);
+ $consultation->markPaymentReceived();
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'payment_received',
+ 'target_type' => 'consultation',
+ 'target_id' => $consultation->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.payment_marked_received'));
+ }
+
+ private function logStatusChange(Consultation $consultation, string $oldStatus, string $newStatus): void
+ {
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'status_change',
+ 'target_type' => 'consultation',
+ 'target_id' => $consultation->id,
+ 'old_values' => ['status' => $oldStatus],
+ 'new_values' => ['status' => $newStatus],
+ 'ip_address' => request()->ip(),
+ ]);
+ }
+
+ public function with(): array
+ {
+ return [
+ 'consultations' => Consultation::query()
+ ->with('user')
+ ->when($this->search, fn($q) => $q->whereHas('user', fn($uq) =>
+ $uq->where('name', 'like', "%{$this->search}%")
+ ->orWhere('email', 'like', "%{$this->search}%")
+ ))
+ ->when($this->statusFilter, fn($q) => $q->where('status', $this->statusFilter))
+ ->when($this->typeFilter, fn($q) => $q->where('type', $this->typeFilter))
+ ->when($this->paymentFilter, fn($q) => $q->where('payment_status', $this->paymentFilter))
+ ->when($this->dateFrom, fn($q) => $q->where('scheduled_date', '>=', $this->dateFrom))
+ ->when($this->dateTo, fn($q) => $q->where('scheduled_date', '<=', $this->dateTo))
+ ->orderBy($this->sortBy, $this->sortDir)
+ ->paginate(15),
+ ];
+ }
+};
+```
+
+### Reschedule Component
+```php
+consultation = $consultation;
+ $this->newDate = $consultation->scheduled_date->format('Y-m-d');
+ }
+
+ public function updatedNewDate(): void
+ {
+ if ($this->newDate) {
+ $service = app(AvailabilityService::class);
+ $this->availableSlots = $service->getAvailableSlots(
+ Carbon::parse($this->newDate)
+ );
+ $this->newTime = '';
+ }
+ }
+
+ public function reschedule(): void
+ {
+ $this->validate([
+ 'newDate' => ['required', 'date', 'after_or_equal:today'],
+ 'newTime' => ['required'],
+ ]);
+
+ // Verify slot available
+ $service = app(AvailabilityService::class);
+ $slots = $service->getAvailableSlots(Carbon::parse($this->newDate));
+
+ if (!in_array($this->newTime, $slots)) {
+ $this->addError('newTime', __('booking.slot_not_available'));
+ return;
+ }
+
+ $oldDate = $this->consultation->scheduled_date;
+ $oldTime = $this->consultation->scheduled_time;
+
+ $this->consultation->reschedule($this->newDate, $this->newTime);
+
+ // Generate new .ics
+ $calendarService = app(CalendarService::class);
+ $icsContent = $calendarService->generateIcs($this->consultation->fresh());
+
+ // Notify client
+ $this->consultation->user->notify(
+ new ConsultationRescheduled($this->consultation, $oldDate, $oldTime, $icsContent)
+ );
+
+ // Log
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'reschedule',
+ 'target_type' => 'consultation',
+ 'target_id' => $this->consultation->id,
+ 'old_values' => ['date' => $oldDate, 'time' => $oldTime],
+ 'new_values' => ['date' => $this->newDate, 'time' => $this->newTime],
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.consultation_rescheduled'));
+ $this->redirect(route('admin.consultations.index'));
+ }
+};
+```
+
+### Admin Notes
+```php
+// Add admin_notes column to consultations or separate table
+// In Consultation model:
+protected $casts = [
+ 'admin_notes' => 'array', // [{text, admin_id, created_at}]
+];
+
+public function addNote(string $note): void
+{
+ $notes = $this->admin_notes ?? [];
+ $notes[] = [
+ 'text' => $note,
+ 'admin_id' => auth()->id(),
+ 'created_at' => now()->toISOString(),
+ ];
+ $this->update(['admin_notes' => $notes]);
+}
+```
+
+## Definition of Done
+
+- [ ] List view with all filters working
+- [ ] Can mark consultation as completed
+- [ ] Can mark consultation as no-show
+- [ ] Can cancel consultation
+- [ ] Can reschedule consultation
+- [ ] Can mark payment as received
+- [ ] Can add admin notes
+- [ ] Client notified on reschedule/cancel
+- [ ] New .ics sent on reschedule
+- [ ] Audit logging complete
+- [ ] Bilingual support
+- [ ] Tests for all status changes
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.5:** Booking approval
+- **Story 3.6:** Calendar file generation
+- **Epic 8:** Email notifications
+
+## Risk Assessment
+
+- **Primary Risk:** Status change on wrong consultation
+- **Mitigation:** Confirmation dialogs, clear identification
+- **Rollback:** Manual status correction, audit log
+
+## Estimation
+
+**Complexity:** Medium-High
+**Estimated Effort:** 5-6 hours
diff --git a/docs/stories/story-3.8-consultation-reminders.md b/docs/stories/story-3.8-consultation-reminders.md
new file mode 100644
index 0000000..506241b
--- /dev/null
+++ b/docs/stories/story-3.8-consultation-reminders.md
@@ -0,0 +1,378 @@
+# Story 3.8: Consultation Reminders
+
+## Epic Reference
+**Epic 3:** Booking & Consultation System
+
+## User Story
+As a **client**,
+I want **to receive reminder emails before my consultation**,
+So that **I don't forget my appointment and can prepare accordingly**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** consultations table, Laravel scheduler, email notifications
+- **Technology:** Laravel Queue, Scheduler, Mailable
+- **Follows pattern:** Scheduled job pattern
+- **Touch points:** Email system, consultation status
+
+## Acceptance Criteria
+
+### 24-Hour Reminder
+- [ ] Sent 24 hours before consultation
+- [ ] Includes:
+ - Consultation date and time
+ - Type (free/paid)
+ - Payment reminder if paid and not received
+ - Calendar file link
+ - Any special instructions
+
+### 2-Hour Reminder
+- [ ] Sent 2 hours before consultation
+- [ ] Includes:
+ - Consultation date and time
+ - Final payment reminder if applicable
+ - Contact information for last-minute issues
+
+### Reminder Logic
+- [ ] Only for approved consultations
+- [ ] Skip cancelled consultations
+- [ ] Skip no-show consultations
+- [ ] Don't send duplicate reminders
+- [ ] Handle timezone correctly
+
+### Language Support
+- [ ] Email in client's preferred language
+- [ ] Arabic template for Arabic preference
+- [ ] English template for English preference
+
+### Quality Requirements
+- [ ] Scheduled jobs run reliably
+- [ ] Retry on failure
+- [ ] Logging for debugging
+- [ ] Tests for reminder logic
+
+## Technical Notes
+
+### Reminder Commands
+
+#### 24-Hour Reminder Command
+```php
+addHours(24);
+ $windowStart = $targetTime->copy()->subMinutes(30);
+ $windowEnd = $targetTime->copy()->addMinutes(30);
+
+ $consultations = Consultation::where('status', 'approved')
+ ->whereNull('reminder_24h_sent_at')
+ ->whereDate('scheduled_date', $targetTime->toDateString())
+ ->get()
+ ->filter(function ($consultation) use ($windowStart, $windowEnd) {
+ $consultationDateTime = Carbon::parse(
+ $consultation->scheduled_date->format('Y-m-d') . ' ' .
+ $consultation->scheduled_time
+ );
+ return $consultationDateTime->between($windowStart, $windowEnd);
+ });
+
+ $count = 0;
+ foreach ($consultations as $consultation) {
+ try {
+ $consultation->user->notify(new ConsultationReminder24h($consultation));
+
+ $consultation->update(['reminder_24h_sent_at' => now()]);
+ $count++;
+
+ $this->info("Sent 24h reminder for consultation #{$consultation->id}");
+ } catch (\Exception $e) {
+ $this->error("Failed to send reminder for consultation #{$consultation->id}: {$e->getMessage()}");
+ \Log::error('24h reminder failed', [
+ 'consultation_id' => $consultation->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ $this->info("Sent {$count} 24-hour reminders");
+
+ return Command::SUCCESS;
+ }
+}
+```
+
+#### 2-Hour Reminder Command
+```php
+addHours(2);
+ $windowStart = $targetTime->copy()->subMinutes(15);
+ $windowEnd = $targetTime->copy()->addMinutes(15);
+
+ $consultations = Consultation::where('status', 'approved')
+ ->whereNull('reminder_2h_sent_at')
+ ->whereDate('scheduled_date', $targetTime->toDateString())
+ ->get()
+ ->filter(function ($consultation) use ($windowStart, $windowEnd) {
+ $consultationDateTime = Carbon::parse(
+ $consultation->scheduled_date->format('Y-m-d') . ' ' .
+ $consultation->scheduled_time
+ );
+ return $consultationDateTime->between($windowStart, $windowEnd);
+ });
+
+ $count = 0;
+ foreach ($consultations as $consultation) {
+ try {
+ $consultation->user->notify(new ConsultationReminder2h($consultation));
+
+ $consultation->update(['reminder_2h_sent_at' => now()]);
+ $count++;
+
+ $this->info("Sent 2h reminder for consultation #{$consultation->id}");
+ } catch (\Exception $e) {
+ $this->error("Failed to send reminder for consultation #{$consultation->id}: {$e->getMessage()}");
+ \Log::error('2h reminder failed', [
+ 'consultation_id' => $consultation->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ $this->info("Sent {$count} 2-hour reminders");
+
+ return Command::SUCCESS;
+ }
+}
+```
+
+### Scheduler Configuration
+```php
+// In routes/console.php or app/Console/Kernel.php (Laravel 12)
+use Illuminate\Support\Facades\Schedule;
+
+Schedule::command('reminders:send-24h')->hourly();
+Schedule::command('reminders:send-2h')->everyFifteenMinutes();
+```
+
+### Migration for Tracking
+```php
+// Add reminder tracking columns to consultations
+Schema::table('consultations', function (Blueprint $table) {
+ $table->timestamp('reminder_24h_sent_at')->nullable();
+ $table->timestamp('reminder_2h_sent_at')->nullable();
+});
+```
+
+### 24-Hour Reminder Notification
+```php
+preferred_language ?? 'ar';
+
+ return (new MailMessage)
+ ->subject($this->getSubject($locale))
+ ->markdown('emails.reminders.24h.' . $locale, [
+ 'consultation' => $this->consultation,
+ 'user' => $notifiable,
+ 'showPaymentReminder' => $this->shouldShowPaymentReminder(),
+ ]);
+ }
+
+ private function getSubject(string $locale): string
+ {
+ return $locale === 'ar'
+ ? 'تذكير: موعدك غداً مع مكتب ليبرا للمحاماة'
+ : 'Reminder: Your consultation is tomorrow';
+ }
+
+ private function shouldShowPaymentReminder(): bool
+ {
+ return $this->consultation->type === 'paid' &&
+ $this->consultation->payment_status === 'pending';
+ }
+}
+```
+
+### Email Template (24h Arabic)
+```blade
+
+
+# تذكير بموعدك
+
+عزيزي {{ $user->name }}،
+
+نود تذكيرك بموعد استشارتك غداً:
+
+**التاريخ:** {{ $consultation->scheduled_date->translatedFormat('l، d F Y') }}
+**الوقت:** {{ \Carbon\Carbon::parse($consultation->scheduled_time)->format('g:i A') }}
+**المدة:** 45 دقيقة
+**النوع:** {{ $consultation->type === 'free' ? 'مجانية' : 'مدفوعة' }}
+
+@if($showPaymentReminder)
+
+**تذكير بالدفع:** يرجى إتمام عملية الدفع قبل موعد الاستشارة.
+المبلغ المطلوب: {{ number_format($consultation->payment_amount, 2) }} شيكل
+
+@endif
+
+
+تحميل ملف التقويم
+
+
+إذا كنت بحاجة لإعادة جدولة الموعد، يرجى التواصل معنا في أقرب وقت.
+
+مع أطيب التحيات،
+مكتب ليبرا للمحاماة
+
+```
+
+### Email Template (2h Arabic)
+```blade
+
+
+# موعدك بعد ساعتين
+
+عزيزي {{ $user->name }}،
+
+تذكير أخير: موعد استشارتك خلال ساعتين.
+
+**الوقت:** {{ \Carbon\Carbon::parse($consultation->scheduled_time)->format('g:i A') }}
+
+@if($showPaymentReminder)
+
+**هام:** لم نستلم الدفعة بعد. يرجى إتمام الدفع قبل بدء الاستشارة.
+
+@endif
+
+إذا كان لديك أي استفسار طارئ، يرجى التواصل معنا على:
+[رقم الهاتف]
+
+نتطلع للقائك،
+مكتب ليبرا للمحاماة
+
+```
+
+### Testing
+```php
+use App\Console\Commands\Send24HourReminders;
+use App\Models\Consultation;
+use App\Notifications\ConsultationReminder24h;
+use Illuminate\Support\Facades\Notification;
+
+it('sends 24h reminder for upcoming consultation', function () {
+ Notification::fake();
+
+ $consultation = Consultation::factory()->approved()->create([
+ 'scheduled_date' => now()->addHours(24)->toDateString(),
+ 'scheduled_time' => now()->addHours(24)->format('H:i:s'),
+ 'reminder_24h_sent_at' => null,
+ ]);
+
+ $this->artisan('reminders:send-24h')
+ ->assertSuccessful();
+
+ Notification::assertSentTo(
+ $consultation->user,
+ ConsultationReminder24h::class
+ );
+
+ expect($consultation->fresh()->reminder_24h_sent_at)->not->toBeNull();
+});
+
+it('does not send reminder for cancelled consultation', function () {
+ Notification::fake();
+
+ $consultation = Consultation::factory()->create([
+ 'status' => 'cancelled',
+ 'scheduled_date' => now()->addHours(24)->toDateString(),
+ 'scheduled_time' => now()->addHours(24)->format('H:i:s'),
+ ]);
+
+ $this->artisan('reminders:send-24h')
+ ->assertSuccessful();
+
+ Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
+});
+```
+
+## Definition of Done
+
+- [ ] 24h reminder command works
+- [ ] 2h reminder command works
+- [ ] Scheduler configured correctly
+- [ ] Only approved consultations receive reminders
+- [ ] Cancelled/no-show don't receive reminders
+- [ ] No duplicate reminders sent
+- [ ] Payment reminder included when applicable
+- [ ] Arabic emails work correctly
+- [ ] English emails work correctly
+- [ ] Logging for debugging
+- [ ] Tests pass for reminder logic
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 3.5:** Booking approval (creates approved consultations)
+- **Epic 8:** Email infrastructure
+
+## Risk Assessment
+
+- **Primary Risk:** Scheduler not running
+- **Mitigation:** Monitor scheduler, alert on failures
+- **Rollback:** Manual reminder sending if needed
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-4.1-timeline-creation.md b/docs/stories/story-4.1-timeline-creation.md
new file mode 100644
index 0000000..694ab5c
--- /dev/null
+++ b/docs/stories/story-4.1-timeline-creation.md
@@ -0,0 +1,139 @@
+# Story 4.1: Timeline Creation
+
+## Epic Reference
+**Epic 4:** Case Timeline System
+
+## User Story
+As an **admin**,
+I want **to create case timelines for clients**,
+So that **I can track and communicate progress on their legal matters**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** timelines table, users table
+- **Technology:** Livewire Volt, Flux UI
+- **Follows pattern:** Admin CRUD pattern
+- **Touch points:** User relationship, client dashboard
+
+## Acceptance Criteria
+
+### Timeline Creation Form
+- [ ] Select client (search by name/email)
+- [ ] Case name/title (required)
+- [ ] Case reference number (optional, unique if provided)
+- [ ] Initial notes (optional)
+
+### Behavior
+- [ ] Timeline assigned to selected client
+- [ ] Creation date automatically recorded
+- [ ] Status defaults to 'active'
+- [ ] Can create multiple timelines per client
+- [ ] Confirmation message on successful creation
+- [ ] Timeline immediately visible to client
+
+### Validation
+- [ ] Case name required
+- [ ] Case reference unique if provided
+- [ ] Client must exist
+
+### Quality Requirements
+- [ ] Audit log entry created
+- [ ] Bilingual labels and messages
+- [ ] Tests for creation flow
+
+## Technical Notes
+
+### Database Schema
+```php
+// timelines table
+Schema::create('timelines', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+ $table->string('case_name');
+ $table->string('case_reference')->nullable()->unique();
+ $table->enum('status', ['active', 'archived'])->default('active');
+ $table->timestamps();
+});
+```
+
+### Volt Component
+```php
+selectedUserId = $userId;
+ $this->selectedUser = User::find($userId);
+ }
+
+ public function create(): void
+ {
+ $this->validate([
+ 'selectedUserId' => ['required', 'exists:users,id'],
+ 'caseName' => ['required', 'string', 'max:255'],
+ 'caseReference' => ['nullable', 'string', 'max:50', 'unique:timelines,case_reference'],
+ ]);
+
+ $timeline = Timeline::create([
+ 'user_id' => $this->selectedUserId,
+ 'case_name' => $this->caseName,
+ 'case_reference' => $this->caseReference ?: null,
+ 'status' => 'active',
+ ]);
+
+ // Add initial notes as first update if provided
+ if ($this->initialNotes) {
+ $timeline->updates()->create([
+ 'admin_id' => auth()->id(),
+ 'update_text' => $this->initialNotes,
+ ]);
+ }
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'create',
+ 'target_type' => 'timeline',
+ 'target_id' => $timeline->id,
+ 'new_values' => $timeline->toArray(),
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.timeline_created'));
+ $this->redirect(route('admin.timelines.show', $timeline));
+ }
+};
+```
+
+## Definition of Done
+
+- [ ] Can search and select client
+- [ ] Can enter case name and reference
+- [ ] Timeline created with correct data
+- [ ] Initial notes saved as first update
+- [ ] Unique reference validation works
+- [ ] Client can view timeline immediately
+- [ ] Audit log created
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Epic 1:** Database schema, authentication
+- **Epic 2:** User accounts
+
+## Estimation
+
+**Complexity:** Low-Medium
+**Estimated Effort:** 2-3 hours
diff --git a/docs/stories/story-4.2-timeline-updates-management.md b/docs/stories/story-4.2-timeline-updates-management.md
new file mode 100644
index 0000000..b185abe
--- /dev/null
+++ b/docs/stories/story-4.2-timeline-updates-management.md
@@ -0,0 +1,157 @@
+# Story 4.2: Timeline Updates Management
+
+## Epic Reference
+**Epic 4:** Case Timeline System
+
+## User Story
+As an **admin**,
+I want **to add and edit updates within a timeline**,
+So that **I can keep clients informed about their case progress**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** timeline_updates table, timelines table
+- **Technology:** Livewire Volt, rich text editor
+- **Follows pattern:** Nested CRUD pattern
+- **Touch points:** Client notifications, timeline view
+
+## Acceptance Criteria
+
+### Add Update
+- [ ] Add new update to timeline
+- [ ] Update text content (required)
+- [ ] Rich text formatting supported:
+ - Bold, italic, underline
+ - Bullet/numbered lists
+ - Links
+- [ ] Timestamp automatically recorded
+- [ ] Admin name automatically recorded
+- [ ] Client notified via email on new update
+
+### Edit Update
+- [ ] Edit existing update text
+- [ ] Edit history preserved (updated_at changes)
+- [ ] Cannot change timestamp or admin
+
+### Display
+- [ ] Updates displayed in chronological order
+- [ ] Each update shows:
+ - Date/timestamp
+ - Admin name
+ - Update content
+- [ ] Visual timeline representation
+
+### Quality Requirements
+- [ ] HTML sanitization for security
+- [ ] Audit log for edits
+- [ ] Tests for add/edit operations
+
+## Technical Notes
+
+### Database Schema
+```php
+Schema::create('timeline_updates', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('timeline_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('admin_id')->constrained('users');
+ $table->text('update_text');
+ $table->timestamps();
+});
+```
+
+### Volt Component
+```php
+validate([
+ 'updateText' => ['required', 'string', 'min:10'],
+ ]);
+
+ $update = $this->timeline->updates()->create([
+ 'admin_id' => auth()->id(),
+ 'update_text' => clean($this->updateText), // Sanitize HTML
+ ]);
+
+ // Notify client
+ $this->timeline->user->notify(new TimelineUpdateNotification($update));
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'create',
+ 'target_type' => 'timeline_update',
+ 'target_id' => $update->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ $this->updateText = '';
+ session()->flash('success', __('messages.update_added'));
+ }
+
+ public function editUpdate(TimelineUpdate $update): void
+ {
+ $this->editingUpdate = $update;
+ $this->updateText = $update->update_text;
+ }
+
+ public function saveEdit(): void
+ {
+ $this->validate([
+ 'updateText' => ['required', 'string', 'min:10'],
+ ]);
+
+ $oldText = $this->editingUpdate->update_text;
+
+ $this->editingUpdate->update([
+ 'update_text' => clean($this->updateText),
+ ]);
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'update',
+ 'target_type' => 'timeline_update',
+ 'target_id' => $this->editingUpdate->id,
+ 'old_values' => ['update_text' => $oldText],
+ 'new_values' => ['update_text' => $this->updateText],
+ 'ip_address' => request()->ip(),
+ ]);
+
+ $this->editingUpdate = null;
+ $this->updateText = '';
+ session()->flash('success', __('messages.update_edited'));
+ }
+};
+```
+
+## Definition of Done
+
+- [ ] Can add new updates with rich text
+- [ ] Can edit existing updates
+- [ ] Updates display chronologically
+- [ ] Admin name and timestamp shown
+- [ ] Client notification sent
+- [ ] HTML properly sanitized
+- [ ] Audit log created
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 4.1:** Timeline creation
+- **Epic 8:** Email notifications
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-4.3-timeline-archiving.md b/docs/stories/story-4.3-timeline-archiving.md
new file mode 100644
index 0000000..09b6bb7
--- /dev/null
+++ b/docs/stories/story-4.3-timeline-archiving.md
@@ -0,0 +1,154 @@
+# Story 4.3: Timeline Archiving
+
+## Epic Reference
+**Epic 4:** Case Timeline System
+
+## User Story
+As an **admin**,
+I want **to archive completed cases and unarchive if needed**,
+So that **I can organize active and completed case timelines**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** timelines table
+- **Technology:** Livewire Volt
+- **Follows pattern:** Soft state change pattern
+- **Touch points:** Timeline list views
+
+## Acceptance Criteria
+
+### Archive Timeline
+- [ ] Archive button on timeline detail
+- [ ] Status changes to 'archived'
+- [ ] Timeline remains visible to client
+- [ ] No further updates can be added (until unarchived)
+- [ ] Visual indicator shows archived status
+
+### Unarchive Timeline
+- [ ] Unarchive button on archived timelines
+- [ ] Status returns to 'active'
+- [ ] Updates can be added again
+
+### List Filtering
+- [ ] Filter timelines by status (active/archived/all)
+- [ ] Archived timelines sorted separately in client view
+- [ ] Bulk archive option for multiple timelines
+
+### Quality Requirements
+- [ ] Audit log for status changes
+- [ ] Bilingual labels
+- [ ] Tests for archive/unarchive
+
+## Technical Notes
+
+### Timeline Model Methods
+```php
+class Timeline extends Model
+{
+ public function archive(): void
+ {
+ $this->update(['status' => 'archived']);
+ }
+
+ public function unarchive(): void
+ {
+ $this->update(['status' => 'active']);
+ }
+
+ public function isArchived(): bool
+ {
+ return $this->status === 'archived';
+ }
+
+ public function scopeActive($query)
+ {
+ return $query->where('status', 'active');
+ }
+
+ public function scopeArchived($query)
+ {
+ return $query->where('status', 'archived');
+ }
+}
+```
+
+### Volt Component Actions
+```php
+public function archive(): void
+{
+ if ($this->timeline->isArchived()) {
+ return;
+ }
+
+ $this->timeline->archive();
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'archive',
+ 'target_type' => 'timeline',
+ 'target_id' => $this->timeline->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.timeline_archived'));
+}
+
+public function unarchive(): void
+{
+ if (!$this->timeline->isArchived()) {
+ return;
+ }
+
+ $this->timeline->unarchive();
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'unarchive',
+ 'target_type' => 'timeline',
+ 'target_id' => $this->timeline->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.timeline_unarchived'));
+}
+
+public function bulkArchive(array $ids): void
+{
+ Timeline::whereIn('id', $ids)->update(['status' => 'archived']);
+
+ foreach ($ids as $id) {
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'archive',
+ 'target_type' => 'timeline',
+ 'target_id' => $id,
+ 'ip_address' => request()->ip(),
+ ]);
+ }
+
+ session()->flash('success', __('messages.timelines_archived', ['count' => count($ids)]));
+}
+```
+
+## Definition of Done
+
+- [ ] Can archive active timeline
+- [ ] Can unarchive archived timeline
+- [ ] Cannot add updates to archived timeline
+- [ ] Filter by status works
+- [ ] Bulk archive works
+- [ ] Visual indicators correct
+- [ ] Audit log created
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 4.1:** Timeline creation
+- **Story 4.2:** Timeline updates
+
+## Estimation
+
+**Complexity:** Low
+**Estimated Effort:** 2 hours
diff --git a/docs/stories/story-4.4-admin-timeline-dashboard.md b/docs/stories/story-4.4-admin-timeline-dashboard.md
new file mode 100644
index 0000000..094eab1
--- /dev/null
+++ b/docs/stories/story-4.4-admin-timeline-dashboard.md
@@ -0,0 +1,188 @@
+# Story 4.4: Admin Timeline Dashboard
+
+## Epic Reference
+**Epic 4:** Case Timeline System
+
+## User Story
+As an **admin**,
+I want **a central view to manage all timelines across all clients**,
+So that **I can efficiently track and update case progress**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** timelines table, users table
+- **Technology:** Livewire Volt with pagination
+- **Follows pattern:** Admin list/dashboard pattern
+- **Touch points:** All timeline operations
+
+## Acceptance Criteria
+
+### List View
+- [ ] Display all timelines with:
+ - Case name
+ - Client name
+ - Status (active/archived)
+ - Last update date
+ - Update count
+- [ ] Pagination (15/25/50 per page)
+
+### Filtering
+- [ ] Filter by client (search/select)
+- [ ] Filter by status (active/archived/all)
+- [ ] Filter by date range (created/updated)
+- [ ] Search by case name or reference
+
+### Sorting
+- [ ] Sort by client name
+- [ ] Sort by case name
+- [ ] Sort by last updated
+- [ ] Sort by created date
+
+### Quick Actions
+- [ ] View timeline details
+- [ ] Add update (inline or link)
+- [ ] Archive/unarchive toggle
+
+### Quality Requirements
+- [ ] Fast loading with eager loading
+- [ ] Bilingual support
+- [ ] Tests for filtering/sorting
+
+## Technical Notes
+
+### Volt Component
+```php
+resetPage();
+ }
+
+ public function sort(string $column): void
+ {
+ if ($this->sortBy === $column) {
+ $this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
+ } else {
+ $this->sortBy = $column;
+ $this->sortDir = 'asc';
+ }
+ }
+
+ public function with(): array
+ {
+ return [
+ 'timelines' => Timeline::query()
+ ->with(['user', 'updates' => fn($q) => $q->latest()->limit(1)])
+ ->withCount('updates')
+ ->when($this->search, fn($q) => $q->where(function($q) {
+ $q->where('case_name', 'like', "%{$this->search}%")
+ ->orWhere('case_reference', 'like', "%{$this->search}%");
+ }))
+ ->when($this->clientFilter, fn($q) => $q->where('user_id', $this->clientFilter))
+ ->when($this->statusFilter, fn($q) => $q->where('status', $this->statusFilter))
+ ->when($this->dateFrom, fn($q) => $q->where('created_at', '>=', $this->dateFrom))
+ ->when($this->dateTo, fn($q) => $q->where('created_at', '<=', $this->dateTo))
+ ->orderBy($this->sortBy, $this->sortDir)
+ ->paginate($this->perPage),
+ ];
+ }
+};
+```
+
+### Template Structure
+```blade
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | {{ __('admin.case_name') }} |
+ {{ __('admin.client') }} |
+ {{ __('admin.status') }} |
+ {{ __('admin.last_update') }} |
+ {{ __('admin.actions') }} |
+
+
+
+ @foreach($timelines as $timeline)
+
+ | {{ $timeline->case_name }} |
+ {{ $timeline->user->name }} |
+
+
+ {{ __('admin.' . $timeline->status) }}
+
+ |
+ {{ $timeline->updated_at->diffForHumans() }} |
+
+
+ {{ __('admin.actions') }}
+
+
+ {{ __('admin.view') }}
+
+
+ {{ $timeline->status === 'active' ? __('admin.archive') : __('admin.unarchive') }}
+
+
+
+ |
+
+ @endforeach
+
+
+
+ {{ $timelines->links() }}
+
+```
+
+## Definition of Done
+
+- [ ] List displays all timelines
+- [ ] All filters working
+- [ ] All sorts working
+- [ ] Quick actions functional
+- [ ] Pagination working
+- [ ] No N+1 queries
+- [ ] Bilingual support
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 4.1:** Timeline creation
+- **Story 4.3:** Archive functionality
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-4.5-client-timeline-view.md b/docs/stories/story-4.5-client-timeline-view.md
new file mode 100644
index 0000000..d009aed
--- /dev/null
+++ b/docs/stories/story-4.5-client-timeline-view.md
@@ -0,0 +1,174 @@
+# Story 4.5: Client Timeline View
+
+## Epic Reference
+**Epic 4:** Case Timeline System
+
+## User Story
+As a **client**,
+I want **to view my case timelines and updates**,
+So that **I can track the progress of my legal matters**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** timelines, timeline_updates tables
+- **Technology:** Livewire Volt (read-only)
+- **Follows pattern:** Client dashboard pattern
+- **Touch points:** Client portal navigation
+
+## Acceptance Criteria
+
+### Timeline List
+- [ ] Display all client's timelines
+- [ ] Active timelines prominently displayed
+- [ ] Archived timelines clearly separated
+- [ ] Visual distinction (color/icon) for status
+- [ ] Show for each:
+ - Case name and reference
+ - Status indicator
+ - Last update date
+ - Update count
+
+### Individual Timeline View
+- [ ] Case name and reference
+- [ ] Status indicator
+- [ ] All updates in chronological order
+- [ ] Each update shows:
+ - Date and time
+ - Update content (formatted)
+
+### Restrictions
+- [ ] Read-only (no edit/comment)
+- [ ] No ability to archive/delete
+- [ ] Only see own timelines
+
+### UX Features
+- [ ] Recent updates indicator (new since last view, optional)
+- [ ] Responsive design for mobile
+- [ ] Bilingual labels and dates
+
+## Technical Notes
+
+### Volt Component for List
+```php
+ auth()->user()
+ ->timelines()
+ ->active()
+ ->withCount('updates')
+ ->with(['updates' => fn($q) => $q->latest()->limit(1)])
+ ->latest('updated_at')
+ ->get(),
+
+ 'archivedTimelines' => auth()->user()
+ ->timelines()
+ ->archived()
+ ->withCount('updates')
+ ->latest('updated_at')
+ ->get(),
+ ];
+ }
+};
+```
+
+### Timeline Detail View
+```php
+user_id === auth()->id(), 403);
+
+ $this->timeline = $timeline->load(['updates' => fn($q) => $q->oldest()]);
+ }
+};
+```
+
+### Template
+```blade
+
+
+
+
+
{{ $timeline->case_name }}
+ @if($timeline->case_reference)
+
{{ __('client.reference') }}: {{ $timeline->case_reference }}
+ @endif
+
+
+ {{ __('client.' . $timeline->status) }}
+
+
+
+
+
+
+
+
+
+ @forelse($timeline->updates as $update)
+
+
+
+
+
+
+ {{ $update->created_at->translatedFormat('l, d M Y - g:i A') }}
+
+
+ {!! $update->update_text !!}
+
+
+
+ @empty
+
+ {{ __('client.no_updates_yet') }}
+
+ @endforelse
+
+
+
+
+
+ {{ __('client.back_to_cases') }}
+
+
+
+```
+
+## Definition of Done
+
+- [ ] Client can view list of their timelines
+- [ ] Active/archived clearly separated
+- [ ] Can view individual timeline details
+- [ ] All updates displayed chronologically
+- [ ] Read-only (no edit capabilities)
+- [ ] Cannot view other clients' timelines
+- [ ] Mobile responsive
+- [ ] RTL support
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 4.1-4.3:** Timeline management
+- **Epic 7:** Client dashboard structure
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-4.6-timeline-update-notifications.md b/docs/stories/story-4.6-timeline-update-notifications.md
new file mode 100644
index 0000000..9112255
--- /dev/null
+++ b/docs/stories/story-4.6-timeline-update-notifications.md
@@ -0,0 +1,220 @@
+# Story 4.6: Timeline Update Notifications
+
+## Epic Reference
+**Epic 4:** Case Timeline System
+
+## User Story
+As a **client**,
+I want **to receive email notifications when my timeline is updated**,
+So that **I stay informed about my case progress without checking the portal**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** timeline_updates creation, email system
+- **Technology:** Laravel Notifications, queued emails
+- **Follows pattern:** Event-driven notification pattern
+- **Touch points:** Timeline update creation
+
+## Acceptance Criteria
+
+### Notification Trigger
+- [ ] Email sent when new update added to timeline
+- [ ] Triggered automatically on TimelineUpdate creation
+- [ ] Queued for performance
+
+### Email Content
+- [ ] Case name and reference
+- [ ] Update summary or full text
+- [ ] Date of update
+- [ ] Link to view timeline
+- [ ] Libra branding
+
+### Language Support
+- [ ] Email in client's preferred language
+- [ ] Arabic template
+- [ ] English template
+
+### Exclusions
+- [ ] No email for archived timeline reactivation
+- [ ] No email if client deactivated
+
+### Quality Requirements
+- [ ] Professional email template
+- [ ] Tests for notification sending
+- [ ] Error handling for failed sends
+
+## Technical Notes
+
+### Notification Class
+```php
+preferred_language ?? 'ar';
+ $timeline = $this->update->timeline;
+
+ return (new MailMessage)
+ ->subject($this->getSubject($locale, $timeline->case_name))
+ ->markdown('emails.timeline.update.' . $locale, [
+ 'update' => $this->update,
+ 'timeline' => $timeline,
+ 'user' => $notifiable,
+ ]);
+ }
+
+ private function getSubject(string $locale, string $caseName): string
+ {
+ return $locale === 'ar'
+ ? "تحديث جديد على قضيتك: {$caseName}"
+ : "New update on your case: {$caseName}";
+ }
+}
+```
+
+### Email Templates
+
+#### Arabic
+```blade
+
+# تحديث جديد على قضيتك
+
+عزيزي {{ $user->name }}،
+
+تم إضافة تحديث جديد على قضيتك:
+
+**القضية:** {{ $timeline->case_name }}
+@if($timeline->case_reference)
+**الرقم المرجعي:** {{ $timeline->case_reference }}
+@endif
+
+**تاريخ التحديث:** {{ $update->created_at->translatedFormat('d M Y - g:i A') }}
+
+---
+
+{!! $update->update_text !!}
+
+---
+
+
+عرض التفاصيل الكاملة
+
+
+مع أطيب التحيات،
+مكتب ليبرا للمحاماة
+
+```
+
+#### English
+```blade
+
+# New Update on Your Case
+
+Dear {{ $user->name }},
+
+A new update has been added to your case:
+
+**Case:** {{ $timeline->case_name }}
+@if($timeline->case_reference)
+**Reference:** {{ $timeline->case_reference }}
+@endif
+
+**Update Date:** {{ $update->created_at->format('M d, Y - g:i A') }}
+
+---
+
+{!! $update->update_text !!}
+
+---
+
+
+View Full Details
+
+
+Best regards,
+Libra Law Firm
+
+```
+
+### Trigger in Update Creation
+```php
+// In Story 4.2 addUpdate method
+$update = $this->timeline->updates()->create([...]);
+
+// Check if user is active before notifying
+if ($this->timeline->user->isActive()) {
+ $this->timeline->user->notify(new TimelineUpdateNotification($update));
+}
+```
+
+### Testing
+```php
+use App\Notifications\TimelineUpdateNotification;
+use Illuminate\Support\Facades\Notification;
+
+it('sends notification when timeline update created', function () {
+ Notification::fake();
+
+ $timeline = Timeline::factory()->create();
+ $update = TimelineUpdate::factory()->create(['timeline_id' => $timeline->id]);
+
+ $timeline->user->notify(new TimelineUpdateNotification($update));
+
+ Notification::assertSentTo($timeline->user, TimelineUpdateNotification::class);
+});
+
+it('does not send notification to deactivated user', function () {
+ Notification::fake();
+
+ $user = User::factory()->create(['status' => 'deactivated']);
+ $timeline = Timeline::factory()->for($user)->create();
+
+ // Add update (should check user status)
+ // ...
+
+ Notification::assertNotSentTo($user, TimelineUpdateNotification::class);
+});
+```
+
+## Definition of Done
+
+- [ ] Email sent on new update
+- [ ] Arabic template works
+- [ ] English template works
+- [ ] Uses client's preferred language
+- [ ] Link to timeline works
+- [ ] Queued for performance
+- [ ] No email to deactivated users
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 4.2:** Timeline updates management
+- **Epic 8:** Email infrastructure
+
+## Estimation
+
+**Complexity:** Low-Medium
+**Estimated Effort:** 2-3 hours
diff --git a/docs/stories/story-5.1-post-creation-editing.md b/docs/stories/story-5.1-post-creation-editing.md
new file mode 100644
index 0000000..64d9631
--- /dev/null
+++ b/docs/stories/story-5.1-post-creation-editing.md
@@ -0,0 +1,268 @@
+# Story 5.1: Post Creation & Editing
+
+## Epic Reference
+**Epic 5:** Posts/Blog System
+
+## User Story
+As an **admin**,
+I want **to create and edit blog posts with rich text formatting**,
+So that **I can publish professional legal content for website visitors**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** posts table
+- **Technology:** Livewire Volt, TinyMCE or similar rich text editor
+- **Follows pattern:** Admin CRUD pattern
+- **Touch points:** Public posts display
+
+## Acceptance Criteria
+
+### Post Creation Form
+- [ ] Title (required, bilingual: Arabic and English)
+- [ ] Body content (required, bilingual)
+- [ ] Status (draft/published)
+
+### Rich Text Editor
+- [ ] Bold, italic, underline
+- [ ] Headings (H2, H3)
+- [ ] Bullet and numbered lists
+- [ ] Links
+- [ ] Blockquotes
+
+### Saving Features
+- [ ] Save as draft functionality
+- [ ] Preview post before publishing
+- [ ] Edit published posts
+- [ ] Auto-save draft periodically (every 60 seconds)
+- [ ] Immediate publishing (no scheduling)
+
+### Timestamps
+- [ ] created_at recorded on creation
+- [ ] updated_at updated on edit
+
+### Quality Requirements
+- [ ] HTML sanitization for XSS prevention
+- [ ] Bilingual form labels
+- [ ] Audit log for create/edit
+- [ ] Tests for CRUD operations
+
+## Technical Notes
+
+### Database Schema
+```php
+Schema::create('posts', function (Blueprint $table) {
+ $table->id();
+ $table->string('title_ar');
+ $table->string('title_en');
+ $table->text('body_ar');
+ $table->text('body_en');
+ $table->enum('status', ['draft', 'published'])->default('draft');
+ $table->timestamps();
+});
+```
+
+### Post Model
+```php
+class Post extends Model
+{
+ protected $fillable = [
+ 'title_ar', 'title_en', 'body_ar', 'body_en', 'status',
+ ];
+
+ public function getTitleAttribute(): string
+ {
+ $locale = app()->getLocale();
+ return $this->{"title_{$locale}"} ?? $this->title_en;
+ }
+
+ public function getBodyAttribute(): string
+ {
+ $locale = app()->getLocale();
+ return $this->{"body_{$locale}"} ?? $this->body_en;
+ }
+
+ public function getExcerptAttribute(): string
+ {
+ return Str::limit(strip_tags($this->body), 150);
+ }
+
+ public function scopePublished($query)
+ {
+ return $query->where('status', 'published');
+ }
+
+ public function scopeDraft($query)
+ {
+ return $query->where('status', 'draft');
+ }
+}
+```
+
+### Volt Component
+```php
+exists) {
+ $this->post = $post;
+ $this->fill($post->only([
+ 'title_ar', 'title_en', 'body_ar', 'body_en', 'status'
+ ]));
+ }
+ }
+
+ public function save(): void
+ {
+ $validated = $this->validate([
+ 'title_ar' => ['required', 'string', 'max:255'],
+ 'title_en' => ['required', 'string', 'max:255'],
+ 'body_ar' => ['required', 'string'],
+ 'body_en' => ['required', 'string'],
+ 'status' => ['required', 'in:draft,published'],
+ ]);
+
+ // Sanitize HTML
+ $validated['body_ar'] = clean($validated['body_ar']);
+ $validated['body_en'] = clean($validated['body_en']);
+
+ if ($this->post) {
+ $this->post->update($validated);
+ $action = 'update';
+ } else {
+ $this->post = Post::create($validated);
+ $action = 'create';
+ }
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => $action,
+ 'target_type' => 'post',
+ 'target_id' => $this->post->id,
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.post_saved'));
+ }
+
+ public function saveDraft(): void
+ {
+ $this->status = 'draft';
+ $this->save();
+ }
+
+ public function publish(): void
+ {
+ $this->status = 'published';
+ $this->save();
+ }
+
+ public function autoSave(): void
+ {
+ if ($this->post && $this->status === 'draft') {
+ $this->post->update([
+ 'title_ar' => $this->title_ar,
+ 'title_en' => $this->title_en,
+ 'body_ar' => clean($this->body_ar),
+ 'body_en' => clean($this->body_en),
+ ]);
+ }
+ }
+};
+```
+
+### Template with Rich Text Editor
+```blade
+
+
+
+
+
{{ __('admin.arabic_content') }}
+
+
+ {{ __('admin.title') }} (عربي) *
+
+
+
+
+
+ {{ __('admin.body') }} (عربي) *
+
+ {!! $body_ar !!}
+
+
+
+
+
+
+
+
{{ __('admin.english_content') }}
+
+
+ {{ __('admin.title') }} (English) *
+
+
+
+
+
+ {{ __('admin.body') }} (English) *
+
+ {!! $body_en !!}
+
+
+
+
+
+
+
+
+ {{ __('admin.save_draft') }}
+
+
+ {{ __('admin.publish') }}
+
+
+
+```
+
+## Definition of Done
+
+- [ ] Can create post with bilingual content
+- [ ] Rich text editor works
+- [ ] Can save as draft
+- [ ] Can publish directly
+- [ ] Can edit existing posts
+- [ ] Auto-save works for drafts
+- [ ] HTML properly sanitized
+- [ ] Audit log created
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Epic 1:** Database schema, admin authentication
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 4-5 hours
diff --git a/docs/stories/story-5.2-post-management-dashboard.md b/docs/stories/story-5.2-post-management-dashboard.md
new file mode 100644
index 0000000..7581dd5
--- /dev/null
+++ b/docs/stories/story-5.2-post-management-dashboard.md
@@ -0,0 +1,243 @@
+# Story 5.2: Post Management Dashboard
+
+## Epic Reference
+**Epic 5:** Posts/Blog System
+
+## User Story
+As an **admin**,
+I want **a dashboard to manage all blog posts**,
+So that **I can organize, publish, and maintain content efficiently**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** posts table
+- **Technology:** Livewire Volt with pagination
+- **Follows pattern:** Admin list pattern
+- **Touch points:** Post CRUD operations
+
+## Acceptance Criteria
+
+### List View
+- [ ] Display all posts with:
+ - Title (in current admin language)
+ - Status (draft/published)
+ - Created date
+ - Last updated date
+- [ ] Pagination (10/25/50 per page)
+
+### Filtering & Search
+- [ ] Filter by status (draft/published/all)
+- [ ] Search by title or body content
+- [ ] Sort by date (newest/oldest)
+
+### Quick Actions
+- [ ] Edit post
+- [ ] Delete (with confirmation)
+- [ ] Publish/unpublish toggle
+- [ ] Bulk delete option (optional)
+
+### Quality Requirements
+- [ ] Bilingual labels
+- [ ] Audit log for delete actions
+- [ ] Tests for list operations
+
+## Technical Notes
+
+### Volt Component
+```php
+resetPage();
+ }
+
+ public function sort(string $column): void
+ {
+ if ($this->sortBy === $column) {
+ $this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
+ } else {
+ $this->sortBy = $column;
+ $this->sortDir = 'desc';
+ }
+ }
+
+ public function togglePublish(int $id): void
+ {
+ $post = Post::findOrFail($id);
+ $newStatus = $post->status === 'published' ? 'draft' : 'published';
+
+ $post->update(['status' => $newStatus]);
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'status_change',
+ 'target_type' => 'post',
+ 'target_id' => $post->id,
+ 'old_values' => ['status' => $post->getOriginal('status')],
+ 'new_values' => ['status' => $newStatus],
+ 'ip_address' => request()->ip(),
+ ]);
+
+ session()->flash('success', __('messages.post_status_updated'));
+ }
+
+ public function delete(int $id): void
+ {
+ $post = Post::findOrFail($id);
+
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'delete',
+ 'target_type' => 'post',
+ 'target_id' => $post->id,
+ 'old_values' => $post->toArray(),
+ 'ip_address' => request()->ip(),
+ ]);
+
+ $post->delete();
+
+ session()->flash('success', __('messages.post_deleted'));
+ }
+
+ public function with(): array
+ {
+ $locale = app()->getLocale();
+
+ return [
+ 'posts' => Post::query()
+ ->when($this->search, fn($q) => $q->where(function($q) use ($locale) {
+ $q->where("title_{$locale}", 'like', "%{$this->search}%")
+ ->orWhere("body_{$locale}", 'like', "%{$this->search}%");
+ }))
+ ->when($this->statusFilter, fn($q) => $q->where('status', $this->statusFilter))
+ ->orderBy($this->sortBy, $this->sortDir)
+ ->paginate($this->perPage),
+ ];
+ }
+};
+```
+
+### Template
+```blade
+
+
+ {{ __('admin.posts') }}
+
+ {{ __('admin.create_post') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ {{ __('admin.title') }}
+ |
+ {{ __('admin.status') }} |
+
+ {{ __('admin.created') }}
+ |
+
+ {{ __('admin.updated') }}
+ |
+ {{ __('admin.actions') }} |
+
+
+
+ @forelse($posts as $post)
+
+ | {{ $post->title }} |
+
+
+ {{ __('admin.' . $post->status) }}
+
+ |
+ {{ $post->created_at->format('d/m/Y') }} |
+ {{ $post->updated_at->diffForHumans() }} |
+
+
+
+ {{ __('admin.edit') }}
+
+
+ {{ $post->status === 'published' ? __('admin.unpublish') : __('admin.publish') }}
+
+
+ {{ __('admin.delete') }}
+
+
+ |
+
+ @empty
+
+ |
+ {{ __('admin.no_posts') }}
+ |
+
+ @endforelse
+
+
+
+ {{ $posts->links() }}
+
+```
+
+## Definition of Done
+
+- [ ] List displays all posts
+- [ ] Filter by status works
+- [ ] Search by title/body works
+- [ ] Sort by date works
+- [ ] Quick publish/unpublish toggle works
+- [ ] Delete with confirmation works
+- [ ] Pagination works
+- [ ] Audit log for actions
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 5.1:** Post creation
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-5.3-post-deletion.md b/docs/stories/story-5.3-post-deletion.md
new file mode 100644
index 0000000..0144bf8
--- /dev/null
+++ b/docs/stories/story-5.3-post-deletion.md
@@ -0,0 +1,146 @@
+# Story 5.3: Post Deletion
+
+## Epic Reference
+**Epic 5:** Posts/Blog System
+
+## User Story
+As an **admin**,
+I want **to permanently delete posts**,
+So that **I can remove outdated or incorrect content from the website**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** posts table
+- **Technology:** Livewire Volt
+- **Follows pattern:** Permanent delete pattern
+- **Touch points:** Post management dashboard
+
+## Acceptance Criteria
+
+### Delete Functionality
+- [ ] Delete button on post list and edit page
+- [ ] Confirmation dialog before deletion
+- [ ] Permanent deletion (no soft delete per PRD)
+- [ ] Success message after deletion
+
+### Restrictions
+- [ ] Cannot delete while post is being viewed publicly (edge case)
+- [ ] Admin-only access
+
+### Audit Trail
+- [ ] Audit log entry preserved
+- [ ] Old values stored in log
+
+### Quality Requirements
+- [ ] Clear warning in confirmation
+- [ ] Bilingual messages
+- [ ] Tests for deletion
+
+## Technical Notes
+
+### Delete Confirmation Modal
+```blade
+
+ {{ __('admin.delete_post') }}
+
+
+ {{ __('admin.delete_post_warning') }}
+
+
+
+ {{ __('admin.deleting_post', ['title' => $postToDelete?->title]) }}
+
+
+
+
+ {{ __('common.cancel') }}
+
+
+ {{ __('admin.delete_permanently') }}
+
+
+
+```
+
+### Delete Logic
+```php
+public ?Post $postToDelete = null;
+public bool $showDeleteModal = false;
+
+public function delete(int $id): void
+{
+ $this->postToDelete = Post::findOrFail($id);
+ $this->showDeleteModal = true;
+}
+
+public function confirmDelete(): void
+{
+ if (!$this->postToDelete) {
+ return;
+ }
+
+ // Create audit log BEFORE deletion
+ AdminLog::create([
+ 'admin_id' => auth()->id(),
+ 'action_type' => 'delete',
+ 'target_type' => 'post',
+ 'target_id' => $this->postToDelete->id,
+ 'old_values' => $this->postToDelete->toArray(),
+ 'ip_address' => request()->ip(),
+ ]);
+
+ // Permanently delete
+ $this->postToDelete->delete();
+
+ $this->showDeleteModal = false;
+ $this->postToDelete = null;
+
+ session()->flash('success', __('messages.post_deleted'));
+}
+```
+
+### Testing
+```php
+it('permanently deletes post', function () {
+ $post = Post::factory()->create();
+
+ $this->actingAs($admin)
+ ->delete(route('admin.posts.destroy', $post));
+
+ expect(Post::find($post->id))->toBeNull();
+});
+
+it('creates audit log on deletion', function () {
+ $post = Post::factory()->create();
+
+ $this->actingAs($admin)
+ ->delete(route('admin.posts.destroy', $post));
+
+ expect(AdminLog::where('target_type', 'post')
+ ->where('target_id', $post->id)
+ ->where('action_type', 'delete')
+ ->exists()
+ )->toBeTrue();
+});
+```
+
+## Definition of Done
+
+- [ ] Delete button shows confirmation
+- [ ] Confirmation explains permanence
+- [ ] Post deleted from database
+- [ ] Audit log created with old values
+- [ ] Success message displayed
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 5.1:** Post creation
+- **Story 5.2:** Post management dashboard
+
+## Estimation
+
+**Complexity:** Low
+**Estimated Effort:** 1-2 hours
diff --git a/docs/stories/story-5.4-public-posts-display.md b/docs/stories/story-5.4-public-posts-display.md
new file mode 100644
index 0000000..4934de9
--- /dev/null
+++ b/docs/stories/story-5.4-public-posts-display.md
@@ -0,0 +1,198 @@
+# Story 5.4: Public Posts Display
+
+## Epic Reference
+**Epic 5:** Posts/Blog System
+
+## User Story
+As a **website visitor**,
+I want **to view published blog posts**,
+So that **I can read legal updates and articles from the firm**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** posts table (published only)
+- **Technology:** Livewire Volt (public routes)
+- **Follows pattern:** Public content display
+- **Touch points:** Navigation, homepage
+
+## Acceptance Criteria
+
+### Posts Listing Page
+- [ ] Public access (no login required)
+- [ ] Display in reverse chronological order
+- [ ] Each post card shows:
+ - Title
+ - Publication date
+ - Excerpt (first ~150 characters)
+ - Read more link
+- [ ] Pagination for many posts
+
+### Individual Post Page
+- [ ] Full post content displayed
+- [ ] Clean, readable typography
+- [ ] Publication date shown
+- [ ] Back to posts list link
+
+### Language Support
+- [ ] Content displayed based on current site language
+- [ ] Title and body in selected language
+- [ ] Date formatting per locale
+
+### Design Requirements
+- [ ] Responsive design (mobile-friendly)
+- [ ] Professional typography
+- [ ] Consistent with brand colors
+
+### Quality Requirements
+- [ ] Only published posts visible
+- [ ] Fast loading
+- [ ] SEO-friendly URLs (optional per PRD)
+- [ ] Tests for display logic
+
+## Technical Notes
+
+### Routes
+```php
+// routes/web.php (public)
+Route::get('/posts', \App\Livewire\Posts\Index::class)->name('posts.index');
+Route::get('/posts/{post}', \App\Livewire\Posts\Show::class)->name('posts.show');
+```
+
+### Posts Index Component
+```php
+ Post::published()
+ ->latest()
+ ->paginate(10),
+ ];
+ }
+}; ?>
+
+
+
{{ __('posts.title') }}
+
+
+ @forelse($posts as $post)
+
+
+
+
+
+
+ {{ $post->excerpt }}
+
+
+
+ {{ __('posts.read_more') }} →
+
+
+ @empty
+
+ {{ __('posts.no_posts') }}
+
+ @endforelse
+
+
+ {{ $posts->links() }}
+
+```
+
+### Post Show Component
+```php
+status === 'published', 404);
+
+ $this->post = $post;
+ }
+}; ?>
+
+
+
+ {{ $post->title }}
+
+
+
+
+
+ {!! $post->body !!}
+
+
+
+
+```
+
+### Prose Styling
+```css
+/* In app.css */
+.prose-navy {
+ --tw-prose-headings: theme('colors.navy');
+ --tw-prose-links: theme('colors.gold');
+ --tw-prose-bold: theme('colors.navy');
+}
+
+.prose-navy a {
+ text-decoration: underline;
+}
+
+.prose-navy a:hover {
+ color: theme('colors.gold-light');
+}
+```
+
+## Definition of Done
+
+- [ ] Posts listing page works
+- [ ] Individual post page works
+- [ ] Only published posts visible
+- [ ] Reverse chronological order
+- [ ] Excerpts display correctly
+- [ ] Full content renders properly
+- [ ] Language-appropriate content shown
+- [ ] Mobile responsive
+- [ ] RTL support
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 5.1:** Post creation
+- **Epic 1:** Base UI, navigation
+
+## Estimation
+
+**Complexity:** Medium
+**Estimated Effort:** 3-4 hours
diff --git a/docs/stories/story-5.5-post-search.md b/docs/stories/story-5.5-post-search.md
new file mode 100644
index 0000000..76868f9
--- /dev/null
+++ b/docs/stories/story-5.5-post-search.md
@@ -0,0 +1,275 @@
+# Story 5.5: Post Search
+
+## Epic Reference
+**Epic 5:** Posts/Blog System
+
+## User Story
+As a **website visitor**,
+I want **to search through blog posts**,
+So that **I can find relevant legal articles and information**.
+
+## Story Context
+
+### Existing System Integration
+- **Integrates with:** posts table, public posts listing
+- **Technology:** Livewire Volt with real-time search
+- **Follows pattern:** Search component pattern
+- **Touch points:** Posts listing page
+
+## Acceptance Criteria
+
+### Search Functionality
+- [ ] Search input on posts listing page
+- [ ] Search by title (both languages)
+- [ ] Search by body content (both languages)
+- [ ] Real-time search results (debounced)
+
+### User Experience
+- [ ] "No results found" message when empty
+- [ ] Clear search button
+- [ ] Search works in both Arabic and English
+- [ ] Only searches published posts
+
+### Optional Enhancements
+- [ ] Search highlights in results
+- [ ] Search suggestions
+
+### Quality Requirements
+- [ ] Debounce to reduce server load (300ms)
+- [ ] Case-insensitive search
+- [ ] Tests for search functionality
+
+## Technical Notes
+
+### Updated Posts Index Component
+```php
+resetPage();
+ }
+
+ public function clearSearch(): void
+ {
+ $this->search = '';
+ $this->resetPage();
+ }
+
+ public function with(): array
+ {
+ return [
+ 'posts' => Post::published()
+ ->when($this->search, function ($query) {
+ $search = $this->search;
+ $query->where(function ($q) use ($search) {
+ $q->where('title_ar', 'like', "%{$search}%")
+ ->orWhere('title_en', 'like', "%{$search}%")
+ ->orWhere('body_ar', 'like', "%{$search}%")
+ ->orWhere('body_en', 'like', "%{$search}%");
+ });
+ })
+ ->latest()
+ ->paginate(10),
+ ];
+ }
+}; ?>
+
+
+
{{ __('posts.title') }}
+
+
+
+
+
+
+
+
+
+ @if($search)
+
+ @endif
+
+
+
+ @if($search)
+
+ @if($posts->total() > 0)
+ {{ __('posts.search_results', ['count' => $posts->total(), 'query' => $search]) }}
+ @else
+ {{ __('posts.no_results', ['query' => $search]) }}
+ @endif
+
+ @endif
+
+
+
+ @forelse($posts as $post)
+
+
+
+
+
+
+ @if($search)
+ {!! $this->highlightSearch($post->excerpt, $search) !!}
+ @else
+ {{ $post->excerpt }}
+ @endif
+
+
+
+ {{ __('posts.read_more') }} →
+
+
+ @empty
+
+ @if($search)
+
+
{{ __('posts.no_results', ['query' => $search]) }}
+
+ {{ __('posts.clear_search') }}
+
+ @else
+
{{ __('posts.no_posts') }}
+ @endif
+
+ @endforelse
+
+
+ {{ $posts->links() }}
+
+```
+
+### Highlight Helper Method
+```php
+public function highlightSearch(string $text, string $search): string
+{
+ if (empty($search)) {
+ return e($text);
+ }
+
+ $escapedText = e($text);
+ $escapedSearch = e($search);
+
+ return preg_replace(
+ '/(' . preg_quote($escapedSearch, '/') . ')/iu',
+ '$1',
+ $escapedText
+ );
+}
+```
+
+### Translation Strings
+```php
+// resources/lang/en/posts.php
+return [
+ 'search_placeholder' => 'Search articles...',
+ 'search_results' => 'Found :count results for ":query"',
+ 'no_results' => 'No results found for ":query"',
+ 'clear_search' => 'Clear search',
+];
+
+// resources/lang/ar/posts.php
+return [
+ 'search_placeholder' => 'البحث في المقالات...',
+ 'search_results' => 'تم العثور على :count نتيجة لـ ":query"',
+ 'no_results' => 'لم يتم العثور على نتائج لـ ":query"',
+ 'clear_search' => 'مسح البحث',
+];
+```
+
+### Testing
+```php
+it('searches posts by title', function () {
+ Post::factory()->published()->create(['title_en' => 'Legal Rights Guide']);
+ Post::factory()->published()->create(['title_en' => 'Tax Information']);
+
+ Volt::test('posts.index')
+ ->set('search', 'Legal')
+ ->assertSee('Legal Rights Guide')
+ ->assertDontSee('Tax Information');
+});
+
+it('searches posts by body content', function () {
+ Post::factory()->published()->create([
+ 'title_en' => 'Post 1',
+ 'body_en' => 'This discusses property law topics.',
+ ]);
+ Post::factory()->published()->create([
+ 'title_en' => 'Post 2',
+ 'body_en' => 'This is about family matters.',
+ ]);
+
+ Volt::test('posts.index')
+ ->set('search', 'property')
+ ->assertSee('Post 1')
+ ->assertDontSee('Post 2');
+});
+
+it('shows no results message', function () {
+ Volt::test('posts.index')
+ ->set('search', 'nonexistent')
+ ->assertSee('No results found');
+});
+
+it('only searches published posts', function () {
+ Post::factory()->create(['status' => 'draft', 'title_en' => 'Draft Post']);
+ Post::factory()->published()->create(['title_en' => 'Published Post']);
+
+ Volt::test('posts.index')
+ ->set('search', 'Post')
+ ->assertSee('Published Post')
+ ->assertDontSee('Draft Post');
+});
+```
+
+## Definition of Done
+
+- [ ] Search input on posts page
+- [ ] Searches title in both languages
+- [ ] Searches body in both languages
+- [ ] Real-time results with debounce
+- [ ] Clear search button works
+- [ ] "No results" message shows
+- [ ] Only published posts searched
+- [ ] Search highlighting works
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 5.4:** Public posts display
+
+## Estimation
+
+**Complexity:** Low-Medium
+**Estimated Effort:** 2-3 hours
diff --git a/docs/stories/story-6.1-dashboard-overview-statistics.md b/docs/stories/story-6.1-dashboard-overview-statistics.md
new file mode 100644
index 0000000..1238dac
--- /dev/null
+++ b/docs/stories/story-6.1-dashboard-overview-statistics.md
@@ -0,0 +1,75 @@
+# Story 6.1: Dashboard Overview & Statistics
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to see real-time metrics and key statistics at a glance**,
+So that **I can understand the current state of my practice**.
+
+## Acceptance Criteria
+
+### User Metrics Card
+- [ ] Total active clients
+- [ ] Individual vs company breakdown
+- [ ] Deactivated clients count
+- [ ] New clients this month
+
+### Booking Metrics Card
+- [ ] Pending requests count (highlighted)
+- [ ] Today's consultations
+- [ ] This week's consultations
+- [ ] This month's consultations
+- [ ] Free vs paid breakdown
+- [ ] No-show rate percentage
+
+### Timeline Metrics Card
+- [ ] Active case timelines
+- [ ] Archived timelines
+- [ ] Updates added this week
+
+### Posts Metrics Card
+- [ ] Total published posts
+- [ ] Posts published this month
+
+### Design
+- [ ] Clean card-based layout
+- [ ] Color-coded status indicators
+- [ ] Responsive grid
+
+## Technical Notes
+
+```php
+new class extends Component {
+ public function with(): array
+ {
+ return [
+ 'userMetrics' => $this->getUserMetrics(),
+ 'bookingMetrics' => $this->getBookingMetrics(),
+ 'timelineMetrics' => $this->getTimelineMetrics(),
+ 'postMetrics' => $this->getPostMetrics(),
+ ];
+ }
+
+ private function getUserMetrics(): array
+ {
+ return Cache::remember('admin.metrics.users', 300, fn() => [
+ 'total_active' => User::where('status', 'active')->whereIn('user_type', ['individual', 'company'])->count(),
+ 'individual' => User::where('user_type', 'individual')->where('status', 'active')->count(),
+ 'company' => User::where('user_type', 'company')->where('status', 'active')->count(),
+ 'deactivated' => User::where('status', 'deactivated')->count(),
+ 'new_this_month' => User::whereMonth('created_at', now()->month)->count(),
+ ]);
+ }
+};
+```
+
+## Definition of Done
+- [ ] All metric cards display correctly
+- [ ] Data is accurate and cached
+- [ ] Responsive layout
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 4-5 hours
diff --git a/docs/stories/story-6.10-audit-log-viewer.md b/docs/stories/story-6.10-audit-log-viewer.md
new file mode 100644
index 0000000..6e1c939
--- /dev/null
+++ b/docs/stories/story-6.10-audit-log-viewer.md
@@ -0,0 +1,98 @@
+# Story 6.10: Audit Log Viewer
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to view admin action history**,
+So that **I can maintain accountability and track changes**.
+
+## Acceptance Criteria
+
+### Display
+- [ ] Action type (create, update, delete)
+- [ ] Target (user, consultation, timeline, etc.)
+- [ ] Old and new values (for updates)
+- [ ] Timestamp
+- [ ] IP address
+
+### Filtering
+- [ ] Filter by action type
+- [ ] Filter by target type
+- [ ] Filter by date range
+
+### Search
+- [ ] Search by target name/ID
+
+### Features
+- [ ] Pagination
+- [ ] Export audit log (CSV)
+
+## Technical Notes
+
+```php
+new class extends Component {
+ use WithPagination;
+
+ public string $actionFilter = '';
+ public string $targetFilter = '';
+ public string $dateFrom = '';
+ public string $dateTo = '';
+ public string $search = '';
+
+ public function with(): array
+ {
+ return [
+ 'logs' => AdminLog::query()
+ ->with('admin')
+ ->when($this->actionFilter, fn($q) => $q->where('action_type', $this->actionFilter))
+ ->when($this->targetFilter, fn($q) => $q->where('target_type', $this->targetFilter))
+ ->when($this->dateFrom, fn($q) => $q->whereDate('created_at', '>=', $this->dateFrom))
+ ->when($this->dateTo, fn($q) => $q->whereDate('created_at', '<=', $this->dateTo))
+ ->when($this->search, fn($q) => $q->where('target_id', $this->search))
+ ->latest()
+ ->paginate(25),
+ 'actionTypes' => AdminLog::distinct()->pluck('action_type'),
+ 'targetTypes' => AdminLog::distinct()->pluck('target_type'),
+ ];
+ }
+
+ public function exportCsv()
+ {
+ // Export filtered logs to CSV
+ }
+};
+```
+
+### Template
+```blade
+@foreach($logs as $log)
+
+ | {{ $log->created_at->format('d/m/Y H:i') }} |
+ {{ $log->admin?->name ?? __('admin.system') }} |
+
+ {{ $log->action_type }}
+ |
+ {{ $log->target_type }} #{{ $log->target_id }} |
+ {{ $log->ip_address }} |
+
+
+ {{ __('admin.details') }}
+
+ |
+
+@endforeach
+```
+
+## Definition of Done
+- [ ] Logs display correctly
+- [ ] All filters work
+- [ ] Search works
+- [ ] Pagination works
+- [ ] CSV export works
+- [ ] Old/new values viewable
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-6.2-analytics-charts.md b/docs/stories/story-6.2-analytics-charts.md
new file mode 100644
index 0000000..e5f4ec1
--- /dev/null
+++ b/docs/stories/story-6.2-analytics-charts.md
@@ -0,0 +1,49 @@
+# Story 6.2: Analytics Charts
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **visual charts showing trends and historical data**,
+So that **I can analyze business patterns over time**.
+
+## Acceptance Criteria
+
+### Charts Required
+- [ ] Monthly Trends (Line chart): New clients and consultations per month
+- [ ] Consultation Breakdown (Pie/Donut): Free vs paid ratio
+- [ ] No-show Rate (Line chart): Monthly no-show trend
+
+### Features
+- [ ] Date range selector (6 months, 12 months, custom)
+- [ ] Chart tooltips with exact values
+- [ ] Responsive charts
+- [ ] Bilingual labels
+
+## Technical Notes
+
+Use Chart.js for visualizations. Aggregate data server-side.
+
+```php
+public function getChartData(string $period = '6m'): array
+{
+ $months = $period === '6m' ? 6 : 12;
+
+ return [
+ 'labels' => collect(range($months - 1, 0))->map(fn($i) => now()->subMonths($i)->format('M Y'))->toArray(),
+ 'clients' => $this->getMonthlyClients($months),
+ 'consultations' => $this->getMonthlyConsultations($months),
+ ];
+}
+```
+
+## Definition of Done
+- [ ] All charts render correctly
+- [ ] Date range selector works
+- [ ] Tooltips functional
+- [ ] Mobile responsive
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium-High | **Effort:** 4-5 hours
diff --git a/docs/stories/story-6.3-quick-actions-panel.md b/docs/stories/story-6.3-quick-actions-panel.md
new file mode 100644
index 0000000..d985321
--- /dev/null
+++ b/docs/stories/story-6.3-quick-actions-panel.md
@@ -0,0 +1,55 @@
+# Story 6.3: Quick Actions Panel
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **quick access to pending items and common tasks**,
+So that **I can efficiently manage my daily workflow**.
+
+## Acceptance Criteria
+
+### Pending Bookings Widget
+- [ ] Count badge with urgent indicator
+- [ ] Link to booking management
+- [ ] Mini list of recent pending (3-5)
+
+### Today's Schedule Widget
+- [ ] List of today's consultations
+- [ ] Time and client name
+- [ ] Quick status update buttons
+
+### Recent Timeline Updates Widget
+- [ ] Last 5 updates made
+- [ ] Quick link to timeline
+
+### Quick Action Buttons
+- [ ] Create user
+- [ ] Create post
+- [ ] Block time slot
+
+### Notification Bell
+- [ ] Pending items count
+
+## Technical Notes
+
+```php
+public function with(): array
+{
+ return [
+ 'pendingBookings' => Consultation::pending()->latest()->take(5)->get(),
+ 'todaySchedule' => Consultation::approved()->whereDate('scheduled_date', today())->orderBy('scheduled_time')->get(),
+ 'recentUpdates' => TimelineUpdate::latest()->take(5)->with('timeline.user')->get(),
+ ];
+}
+```
+
+## Definition of Done
+- [ ] All widgets display correctly
+- [ ] Quick actions work
+- [ ] Real-time updates with polling
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-6.4-data-export-user-lists.md b/docs/stories/story-6.4-data-export-user-lists.md
new file mode 100644
index 0000000..1854a1c
--- /dev/null
+++ b/docs/stories/story-6.4-data-export-user-lists.md
@@ -0,0 +1,68 @@
+# Story 6.4: Data Export - User Lists
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to export user data in CSV and PDF formats**,
+So that **I can generate reports and maintain offline records**.
+
+## Acceptance Criteria
+
+### Export Options
+- [ ] Export all users
+- [ ] Export individual clients only
+- [ ] Export company clients only
+
+### Filters
+- [ ] Date range (created)
+- [ ] Status (active/deactivated)
+
+### CSV Export Includes
+- [ ] Name, email, phone
+- [ ] User type
+- [ ] National ID / Company registration
+- [ ] Status
+- [ ] Created date
+
+### PDF Export Includes
+- [ ] Same data with professional formatting
+- [ ] Libra branding header
+- [ ] Generation timestamp
+
+### Bilingual
+- [ ] Column headers based on admin language
+
+## Technical Notes
+
+Use league/csv for CSV and barryvdh/laravel-dompdf for PDF.
+
+```php
+public function exportCsv(): StreamedResponse
+{
+ return response()->streamDownload(function () {
+ $csv = Writer::createFromString();
+ $csv->insertOne([__('export.name'), __('export.email'), ...]);
+
+ User::whereIn('user_type', ['individual', 'company'])
+ ->cursor()
+ ->each(fn($user) => $csv->insertOne([
+ $user->name,
+ $user->email,
+ // ...
+ ]));
+
+ echo $csv->toString();
+ }, 'users-export.csv');
+}
+```
+
+## Definition of Done
+- [ ] CSV export works with all filters
+- [ ] PDF export works with branding
+- [ ] Large datasets handled efficiently
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-6.5-data-export-consultation-records.md b/docs/stories/story-6.5-data-export-consultation-records.md
new file mode 100644
index 0000000..53c382a
--- /dev/null
+++ b/docs/stories/story-6.5-data-export-consultation-records.md
@@ -0,0 +1,59 @@
+# Story 6.5: Data Export - Consultation Records
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to export consultation/booking data**,
+So that **I can analyze and report on consultation history**.
+
+## Acceptance Criteria
+
+### Export Options
+- [ ] Export all consultations
+
+### Filters
+- [ ] Date range
+- [ ] Consultation type (free/paid)
+- [ ] Status (approved/completed/no-show/cancelled)
+- [ ] Payment status
+
+### Export Includes
+- [ ] Client name
+- [ ] Date and time
+- [ ] Consultation type
+- [ ] Status
+- [ ] Payment status
+- [ ] Problem summary
+
+### Formats
+- [ ] CSV format
+- [ ] PDF format with professional layout and branding
+
+## Technical Notes
+
+```php
+public function exportConsultationsPdf(Request $request)
+{
+ $consultations = Consultation::query()
+ ->with('user')
+ ->when($request->date_from, fn($q) => $q->where('scheduled_date', '>=', $request->date_from))
+ ->when($request->status, fn($q) => $q->where('status', $request->status))
+ ->get();
+
+ $pdf = Pdf::loadView('exports.consultations', compact('consultations'));
+
+ return $pdf->download('consultations-export.pdf');
+}
+```
+
+## Definition of Done
+- [ ] All filters work correctly
+- [ ] CSV export accurate
+- [ ] PDF professionally formatted
+- [ ] Large summaries handled in PDF
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-6.6-data-export-timeline-reports.md b/docs/stories/story-6.6-data-export-timeline-reports.md
new file mode 100644
index 0000000..02f287f
--- /dev/null
+++ b/docs/stories/story-6.6-data-export-timeline-reports.md
@@ -0,0 +1,65 @@
+# Story 6.6: Data Export - Timeline Reports
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to export timeline and case data**,
+So that **I can maintain records and generate case reports**.
+
+## Acceptance Criteria
+
+### Export Options
+- [ ] Export all timelines (across all clients)
+- [ ] Export timelines for specific client
+
+### Filters
+- [ ] Status (active/archived)
+- [ ] Date range
+
+### Export Includes
+- [ ] Case name and reference
+- [ ] Client name
+- [ ] Status
+- [ ] Created date
+- [ ] Number of updates
+- [ ] Last update date
+
+### Formats
+- [ ] CSV format
+- [ ] PDF format
+
+### Optional
+- [ ] Include update content or summary only toggle
+
+## Technical Notes
+
+```php
+public function exportTimelinesPdf(Request $request)
+{
+ $timelines = Timeline::query()
+ ->with(['user', 'updates'])
+ ->withCount('updates')
+ ->when($request->client_id, fn($q) => $q->where('user_id', $request->client_id))
+ ->when($request->status, fn($q) => $q->where('status', $request->status))
+ ->get();
+
+ $pdf = Pdf::loadView('exports.timelines', [
+ 'timelines' => $timelines,
+ 'includeUpdates' => $request->boolean('include_updates'),
+ ]);
+
+ return $pdf->download('timelines-export.pdf');
+}
+```
+
+## Definition of Done
+- [ ] All filters work
+- [ ] CSV export works
+- [ ] PDF with branding works
+- [ ] Optional update content toggle works
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-6.7-monthly-statistics-report.md b/docs/stories/story-6.7-monthly-statistics-report.md
new file mode 100644
index 0000000..c7a4867
--- /dev/null
+++ b/docs/stories/story-6.7-monthly-statistics-report.md
@@ -0,0 +1,70 @@
+# Story 6.7: Monthly Statistics Report
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to generate comprehensive monthly PDF reports**,
+So that **I have professional summaries of business performance**.
+
+## Acceptance Criteria
+
+### Generation
+- [ ] "Generate Monthly Report" button
+- [ ] Select month/year
+
+### PDF Report Includes
+- [ ] Overview of key metrics
+- [ ] Charts (rendered as images)
+- [ ] User statistics
+- [ ] Consultation statistics
+- [ ] Timeline statistics
+- [ ] Post statistics
+
+### Design
+- [ ] Professional layout with branding
+- [ ] Table of contents
+- [ ] Printable format
+- [ ] Bilingual based on admin preference
+
+### UX
+- [ ] Loading indicator during generation
+- [ ] Download on completion
+
+## Technical Notes
+
+Pre-render charts as base64 images for PDF inclusion.
+
+```php
+public function generateMonthlyReport(int $year, int $month)
+{
+ $startDate = Carbon::create($year, $month, 1)->startOfMonth();
+ $endDate = $startDate->copy()->endOfMonth();
+
+ $data = [
+ 'period' => $startDate->format('F Y'),
+ 'userStats' => $this->getUserStatsForPeriod($startDate, $endDate),
+ 'consultationStats' => $this->getConsultationStatsForPeriod($startDate, $endDate),
+ 'timelineStats' => $this->getTimelineStatsForPeriod($startDate, $endDate),
+ 'postStats' => $this->getPostStatsForPeriod($startDate, $endDate),
+ 'charts' => $this->renderChartsAsImages($startDate, $endDate),
+ ];
+
+ $pdf = Pdf::loadView('exports.monthly-report', $data)
+ ->setPaper('a4', 'portrait');
+
+ return $pdf->download("monthly-report-{$year}-{$month}.pdf");
+}
+```
+
+## Definition of Done
+- [ ] Month/year selector works
+- [ ] All statistics accurate
+- [ ] Charts rendered in PDF
+- [ ] Professional branding
+- [ ] Bilingual support
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** High | **Effort:** 5-6 hours
diff --git a/docs/stories/story-6.8-system-settings.md b/docs/stories/story-6.8-system-settings.md
new file mode 100644
index 0000000..afd7a6c
--- /dev/null
+++ b/docs/stories/story-6.8-system-settings.md
@@ -0,0 +1,99 @@
+# Story 6.8: System Settings
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to configure system-wide settings**,
+So that **I can customize the platform to my needs**.
+
+## Acceptance Criteria
+
+### Profile Settings
+- [ ] Admin name
+- [ ] Email
+- [ ] Password change
+- [ ] Preferred language
+
+### Email Settings
+- [ ] View current sender email
+- [ ] Test email functionality
+
+### Notification Preferences (Optional)
+- [ ] Toggle admin notifications
+- [ ] Summary email frequency
+
+### Behavior
+- [ ] Settings saved and applied immediately
+- [ ] Validation for all inputs
+
+## Technical Notes
+
+```php
+new class extends Component {
+ public string $name = '';
+ public string $email = '';
+ public string $current_password = '';
+ public string $password = '';
+ public string $password_confirmation = '';
+ public string $preferred_language = 'ar';
+
+ public function mount(): void
+ {
+ $user = auth()->user();
+ $this->name = $user->name;
+ $this->email = $user->email;
+ $this->preferred_language = $user->preferred_language;
+ }
+
+ public function updateProfile(): void
+ {
+ $this->validate([
+ 'name' => ['required', 'string', 'max:255'],
+ 'email' => ['required', 'email', Rule::unique('users')->ignore(auth()->id())],
+ 'preferred_language' => ['required', 'in:ar,en'],
+ ]);
+
+ auth()->user()->update([
+ 'name' => $this->name,
+ 'email' => $this->email,
+ 'preferred_language' => $this->preferred_language,
+ ]);
+
+ session()->flash('success', __('messages.profile_updated'));
+ }
+
+ public function updatePassword(): void
+ {
+ $this->validate([
+ 'current_password' => ['required', 'current_password'],
+ 'password' => ['required', 'string', 'min:8', 'confirmed'],
+ ]);
+
+ auth()->user()->update([
+ 'password' => Hash::make($this->password),
+ ]);
+
+ $this->reset(['current_password', 'password', 'password_confirmation']);
+ session()->flash('success', __('messages.password_updated'));
+ }
+
+ public function sendTestEmail(): void
+ {
+ Mail::to(auth()->user())->send(new TestEmail());
+ session()->flash('success', __('messages.test_email_sent'));
+ }
+};
+```
+
+## Definition of Done
+- [ ] Profile updates work
+- [ ] Password change works
+- [ ] Language preference persists
+- [ ] Test email sends
+- [ ] Validation complete
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-6.9-legal-pages-editor.md b/docs/stories/story-6.9-legal-pages-editor.md
new file mode 100644
index 0000000..3309eb7
--- /dev/null
+++ b/docs/stories/story-6.9-legal-pages-editor.md
@@ -0,0 +1,79 @@
+# Story 6.9: Legal Pages Editor
+
+## Epic Reference
+**Epic 6:** Admin Dashboard
+
+## User Story
+As an **admin**,
+I want **to edit Terms of Service and Privacy Policy pages**,
+So that **I can maintain legal compliance and update policies**.
+
+## Acceptance Criteria
+
+### Pages to Edit
+- [ ] Terms of Service
+- [ ] Privacy Policy
+
+### Editor Features
+- [ ] Rich text editor
+- [ ] Bilingual content (Arabic/English)
+- [ ] Save and publish
+- [ ] Preview before publishing
+
+### Public Display
+- [ ] Pages accessible from footer (public)
+- [ ] Last updated timestamp displayed
+
+## Technical Notes
+
+Store in database settings table or dedicated pages table.
+
+```php
+// Migration
+Schema::create('pages', function (Blueprint $table) {
+ $table->id();
+ $table->string('slug')->unique();
+ $table->string('title_ar');
+ $table->string('title_en');
+ $table->text('content_ar');
+ $table->text('content_en');
+ $table->timestamps();
+});
+
+// Seeder
+Page::create([
+ 'slug' => 'terms',
+ 'title_ar' => 'شروط الخدمة',
+ 'title_en' => 'Terms of Service',
+ 'content_ar' => '',
+ 'content_en' => '',
+]);
+
+Page::create([
+ 'slug' => 'privacy',
+ 'title_ar' => 'سياسة الخصوصية',
+ 'title_en' => 'Privacy Policy',
+ 'content_ar' => '',
+ 'content_en' => '',
+]);
+```
+
+### Public Route
+```php
+Route::get('/page/{slug}', function (string $slug) {
+ $page = Page::where('slug', $slug)->firstOrFail();
+ return view('pages.show', compact('page'));
+})->name('page.show');
+```
+
+## Definition of Done
+- [ ] Can edit Terms of Service
+- [ ] Can edit Privacy Policy
+- [ ] Bilingual content works
+- [ ] Preview works
+- [ ] Public pages accessible
+- [ ] Last updated shows
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-7.1-client-dashboard-overview.md b/docs/stories/story-7.1-client-dashboard-overview.md
new file mode 100644
index 0000000..336ad21
--- /dev/null
+++ b/docs/stories/story-7.1-client-dashboard-overview.md
@@ -0,0 +1,79 @@
+# Story 7.1: Client Dashboard Overview
+
+## Epic Reference
+**Epic 7:** Client Dashboard
+
+## User Story
+As a **client**,
+I want **a dashboard showing my key information at a glance**,
+So that **I can quickly see upcoming consultations and case updates**.
+
+## Acceptance Criteria
+
+### Welcome Section
+- [ ] Welcome message with client name
+
+### Upcoming Consultations Widget
+- [ ] Next consultation date/time
+- [ ] Type (free/paid)
+- [ ] Status
+- [ ] Quick link to details
+
+### Active Cases Widget
+- [ ] Count of active timelines
+- [ ] Latest update preview
+- [ ] Link to full list
+
+### Recent Updates Widget
+- [ ] Last 3 timeline updates (across all cases)
+- [ ] Case name and date
+- [ ] Link to full timeline
+
+### Booking Status Widget
+- [ ] Pending booking requests
+- [ ] Daily booking limit indicator
+- [ ] Quick book button
+
+### Design
+- [ ] Clean, card-based layout
+- [ ] Mobile-first responsive
+- [ ] Bilingual content
+
+## Technical Notes
+
+```php
+new class extends Component {
+ public function with(): array
+ {
+ $user = auth()->user();
+
+ return [
+ 'upcomingConsultation' => $user->consultations()
+ ->approved()
+ ->upcoming()
+ ->first(),
+ 'activeTimelinesCount' => $user->timelines()->active()->count(),
+ 'recentUpdates' => TimelineUpdate::whereHas('timeline', fn($q) => $q->where('user_id', $user->id))
+ ->latest()
+ ->take(3)
+ ->with('timeline')
+ ->get(),
+ 'pendingBookings' => $user->consultations()->pending()->count(),
+ 'canBookToday' => !$user->consultations()
+ ->whereDate('scheduled_date', today())
+ ->whereIn('status', ['pending', 'approved'])
+ ->exists(),
+ ];
+ }
+};
+```
+
+## Definition of Done
+- [ ] All widgets display correctly
+- [ ] Data scoped to logged-in user
+- [ ] Mobile responsive
+- [ ] Bilingual
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 4-5 hours
diff --git a/docs/stories/story-7.2-my-consultations-view.md b/docs/stories/story-7.2-my-consultations-view.md
new file mode 100644
index 0000000..72480cb
--- /dev/null
+++ b/docs/stories/story-7.2-my-consultations-view.md
@@ -0,0 +1,77 @@
+# Story 7.2: My Consultations View
+
+## Epic Reference
+**Epic 7:** Client Dashboard
+
+## User Story
+As a **client**,
+I want **to view all my consultations**,
+So that **I can track upcoming appointments and review past sessions**.
+
+## Acceptance Criteria
+
+### Upcoming Consultations Section
+- [ ] Date and time
+- [ ] Consultation type (free/paid)
+- [ ] Status (approved/pending)
+- [ ] Payment status (for paid)
+- [ ] Download .ics calendar file button
+
+### Pending Requests Section
+- [ ] Submitted bookings awaiting approval
+- [ ] Submission date
+- [ ] Problem summary preview
+- [ ] Status: "Pending Review"
+
+### Past Consultations Section
+- [ ] Historical consultations
+- [ ] Status (completed/cancelled/no-show)
+- [ ] Date and type
+
+### Features
+- [ ] Visual status indicators
+- [ ] Sort by date (default: newest first for past)
+- [ ] Pagination if many consultations
+- [ ] No edit/cancel capabilities (read-only)
+
+## Technical Notes
+
+```php
+new class extends Component {
+ use WithPagination;
+
+ public function with(): array
+ {
+ $user = auth()->user();
+
+ return [
+ 'upcoming' => $user->consultations()
+ ->approved()
+ ->where('scheduled_date', '>=', today())
+ ->orderBy('scheduled_date')
+ ->orderBy('scheduled_time')
+ ->get(),
+ 'pending' => $user->consultations()
+ ->pending()
+ ->latest()
+ ->get(),
+ 'past' => $user->consultations()
+ ->whereIn('status', ['completed', 'cancelled', 'no_show'])
+ ->orWhere(fn($q) => $q->approved()->where('scheduled_date', '<', today()))
+ ->latest('scheduled_date')
+ ->paginate(10),
+ ];
+ }
+};
+```
+
+## Definition of Done
+- [ ] All sections display correctly
+- [ ] Calendar download works
+- [ ] Status indicators clear
+- [ ] Read-only (no actions)
+- [ ] Pagination works
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-7.3-my-cases-timelines-view.md b/docs/stories/story-7.3-my-cases-timelines-view.md
new file mode 100644
index 0000000..77b15db
--- /dev/null
+++ b/docs/stories/story-7.3-my-cases-timelines-view.md
@@ -0,0 +1,73 @@
+# Story 7.3: My Cases/Timelines View
+
+## Epic Reference
+**Epic 7:** Client Dashboard
+
+## User Story
+As a **client**,
+I want **to view my case timelines and their updates**,
+So that **I can track the progress of my legal matters**.
+
+## Acceptance Criteria
+
+### Active Cases Section
+- [ ] List of active timelines
+- [ ] Case name and reference
+- [ ] Last update date
+- [ ] Update count
+- [ ] "View" button
+
+### Archived Cases Section
+- [ ] Clearly separated from active
+- [ ] Different visual styling (muted)
+- [ ] Still accessible for viewing
+
+### Individual Timeline View
+- [ ] Case name and reference
+- [ ] Status badge (active/archived)
+- [ ] All updates in chronological order
+- [ ] Each update shows:
+ - Date and time
+ - Update content
+- [ ] Read-only (no interactions)
+
+### Navigation
+- [ ] Back to cases list
+- [ ] Responsive layout
+
+## Technical Notes
+
+Reuse components from Story 4.5.
+
+```php
+new class extends Component {
+ public function with(): array
+ {
+ return [
+ 'activeTimelines' => auth()->user()
+ ->timelines()
+ ->active()
+ ->withCount('updates')
+ ->latest('updated_at')
+ ->get(),
+ 'archivedTimelines' => auth()->user()
+ ->timelines()
+ ->archived()
+ ->withCount('updates')
+ ->latest('updated_at')
+ ->get(),
+ ];
+ }
+};
+```
+
+## Definition of Done
+- [ ] Active cases display correctly
+- [ ] Archived cases separated
+- [ ] Timeline detail view works
+- [ ] Updates display chronologically
+- [ ] Read-only enforced
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-7.4-my-profile-view.md b/docs/stories/story-7.4-my-profile-view.md
new file mode 100644
index 0000000..1185a52
--- /dev/null
+++ b/docs/stories/story-7.4-my-profile-view.md
@@ -0,0 +1,98 @@
+# Story 7.4: My Profile View
+
+## Epic Reference
+**Epic 7:** Client Dashboard
+
+## User Story
+As a **client**,
+I want **to view my profile information**,
+So that **I can verify my account details are correct**.
+
+## Acceptance Criteria
+
+### Individual Client Profile
+- [ ] Full name
+- [ ] National ID
+- [ ] Email address
+- [ ] Phone number
+- [ ] Preferred language
+- [ ] Account created date
+
+### Company Client Profile
+- [ ] Company name
+- [ ] Registration number
+- [ ] Contact person name
+- [ ] Contact person ID
+- [ ] Email address
+- [ ] Phone number
+- [ ] Preferred language
+- [ ] Account created date
+
+### Features
+- [ ] Account type indicator
+- [ ] No edit capabilities (read-only)
+- [ ] Message: "Contact admin to update your information"
+- [ ] Logout button
+
+## Technical Notes
+
+```php
+new class extends Component {
+ public function with(): array
+ {
+ return [
+ 'user' => auth()->user(),
+ ];
+ }
+
+ public function logout(): void
+ {
+ auth()->logout();
+ session()->invalidate();
+ session()->regenerateToken();
+
+ $this->redirect(route('login'));
+ }
+}; ?>
+
+
+
{{ __('client.my_profile') }}
+
+
+ @if($user->user_type === 'individual')
+
+
+
- {{ __('profile.full_name') }}
+ - {{ $user->name }}
+
+
+
- {{ __('profile.national_id') }}
+ - {{ $user->national_id }}
+
+
+
+ @else
+
+ @endif
+
+
+
+ {{ __('client.contact_admin_to_update') }}
+
+
+
+ {{ __('auth.logout') }}
+
+
+```
+
+## Definition of Done
+- [ ] Individual profile displays correctly
+- [ ] Company profile displays correctly
+- [ ] No edit functionality
+- [ ] Contact admin message shown
+- [ ] Logout works
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-7.5-new-booking-interface.md b/docs/stories/story-7.5-new-booking-interface.md
new file mode 100644
index 0000000..fa666d1
--- /dev/null
+++ b/docs/stories/story-7.5-new-booking-interface.md
@@ -0,0 +1,71 @@
+# Story 7.5: New Booking Interface
+
+## Epic Reference
+**Epic 7:** Client Dashboard
+
+## User Story
+As a **client**,
+I want **to submit new consultation booking requests**,
+So that **I can schedule meetings with the lawyer**.
+
+## Acceptance Criteria
+
+### Access
+- [ ] Quick access from dashboard
+- [ ] Book Now button in navigation
+
+### Calendar View
+- [ ] Calendar showing available dates
+- [ ] Available time slots for selected date
+
+### Booking Form
+- [ ] Selected date/time (display)
+- [ ] Problem summary (required, textarea)
+- [ ] Submit button
+
+### Validation
+- [ ] Enforce 1 booking per day limit
+- [ ] Show warning if already booked that day
+- [ ] Prevent booking on unavailable slots
+
+### Submission Flow
+- [ ] Confirmation before submission
+- [ ] Success message with "Pending Review" status
+- [ ] Redirect to consultations list
+
+## Technical Notes
+
+Reuse availability calendar from Story 3.3 and booking submission from Story 3.4.
+
+```php
+new class extends Component {
+ public ?string $selectedDate = null;
+ public ?string $selectedTime = null;
+ public string $problemSummary = '';
+
+ public function submit(): void
+ {
+ // Validation and submission logic from Story 3.4
+ }
+
+ public function canBookDate(string $date): bool
+ {
+ return !auth()->user()->consultations()
+ ->whereDate('scheduled_date', $date)
+ ->whereIn('status', ['pending', 'approved'])
+ ->exists();
+ }
+};
+```
+
+## Definition of Done
+- [ ] Calendar displays correctly
+- [ ] Time slots selectable
+- [ ] 1-per-day limit enforced
+- [ ] Problem summary required
+- [ ] Confirmation shown
+- [ ] Booking submitted successfully
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-7.6-booking-limit-indicator.md b/docs/stories/story-7.6-booking-limit-indicator.md
new file mode 100644
index 0000000..a0d4cad
--- /dev/null
+++ b/docs/stories/story-7.6-booking-limit-indicator.md
@@ -0,0 +1,104 @@
+# Story 7.6: Booking Limit Indicator
+
+## Epic Reference
+**Epic 7:** Client Dashboard
+
+## User Story
+As a **client**,
+I want **to see my booking status and limits clearly**,
+So that **I understand when I can book consultations**.
+
+## Acceptance Criteria
+
+### Display Locations
+- [ ] Dashboard widget
+- [ ] Booking page
+
+### Status Messages
+- [ ] "You can book a consultation today"
+- [ ] "You already have a booking for today"
+- [ ] "You have a pending request for [date]"
+
+### Calendar Integration
+- [ ] Calendar shows booked days as unavailable
+- [ ] Visual indicator for user's booked dates
+
+### Information
+- [ ] Clear messaging about 1-per-day limit
+- [ ] Bilingual messages
+
+## Technical Notes
+
+```php
+new class extends Component {
+ public function getBookingStatus(): array
+ {
+ $user = auth()->user();
+
+ $todayBooking = $user->consultations()
+ ->whereDate('scheduled_date', today())
+ ->whereIn('status', ['pending', 'approved'])
+ ->first();
+
+ $pendingRequests = $user->consultations()
+ ->pending()
+ ->get();
+
+ $upcomingApproved = $user->consultations()
+ ->approved()
+ ->where('scheduled_date', '>=', today())
+ ->get();
+
+ return [
+ 'canBookToday' => is_null($todayBooking),
+ 'todayBooking' => $todayBooking,
+ 'pendingRequests' => $pendingRequests,
+ 'upcomingApproved' => $upcomingApproved,
+ 'bookedDates' => $user->consultations()
+ ->whereIn('status', ['pending', 'approved'])
+ ->where('scheduled_date', '>=', today())
+ ->pluck('scheduled_date')
+ ->map(fn($d) => $d->format('Y-m-d'))
+ ->toArray(),
+ ];
+ }
+};
+```
+
+### Template
+```blade
+
+ @if($canBookToday)
+
+
+ {{ __('booking.can_book_today') }}
+
+ @else
+
+
+ {{ __('booking.already_booked_today') }}
+
+ @endif
+
+ @if($pendingRequests->isNotEmpty())
+
+ {{ __('booking.pending_for_date', ['date' => $pendingRequests->first()->scheduled_date->format('d/m/Y')]) }}
+
+ @endif
+
+
+ {{ __('booking.limit_message') }}
+
+
+```
+
+## Definition of Done
+- [ ] Status displays on dashboard
+- [ ] Status displays on booking page
+- [ ] Calendar highlights booked dates
+- [ ] Messages are accurate
+- [ ] Bilingual support
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-8.1-email-infrastructure-setup.md b/docs/stories/story-8.1-email-infrastructure-setup.md
new file mode 100644
index 0000000..b49785f
--- /dev/null
+++ b/docs/stories/story-8.1-email-infrastructure-setup.md
@@ -0,0 +1,76 @@
+# Story 8.1: Email Infrastructure Setup
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **developer**,
+I want **to configure email sending infrastructure and base templates**,
+So that **all emails have consistent branding and reliable delivery**.
+
+## Acceptance Criteria
+
+### SMTP Configuration
+- [ ] MAIL_MAILER configured via .env
+- [ ] MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
+- [ ] MAIL_ENCRYPTION (TLS)
+- [ ] MAIL_FROM_ADDRESS: no-reply@libra.ps
+- [ ] MAIL_FROM_NAME: Libra Law Firm / مكتب ليبرا للمحاماة
+
+### Base Email Template
+- [ ] Libra logo in header
+- [ ] Navy blue (#0A1F44) and gold (#D4AF37) colors
+- [ ] Professional typography
+- [ ] Footer with firm contact info
+- [ ] Mobile-responsive layout
+
+### Technical Setup
+- [ ] Plain text fallback generation
+- [ ] Queue configuration for async sending
+- [ ] Email logging for debugging
+
+## Technical Notes
+
+```php
+// config/mail.php - from .env
+'from' => [
+ 'address' => env('MAIL_FROM_ADDRESS', 'no-reply@libra.ps'),
+ 'name' => env('MAIL_FROM_NAME', 'Libra Law Firm'),
+],
+
+// resources/views/vendor/mail/html/header.blade.php
+
+
+
+
+// Base Mailable
+abstract class BaseMailable extends Mailable
+{
+ use Queueable, SerializesModels;
+
+ public function build()
+ {
+ return $this->from(config('mail.from.address'), $this->getFromName());
+ }
+
+ protected function getFromName(): string
+ {
+ $locale = $this->getLocale();
+ return $locale === 'ar' ? 'مكتب ليبرا للمحاماة' : 'Libra Law Firm';
+ }
+}
+```
+
+## Definition of Done
+- [ ] SMTP sending works
+- [ ] Base template with branding
+- [ ] Plain text fallback
+- [ ] Queued delivery works
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-8.10-admin-notification-system-events.md b/docs/stories/story-8.10-admin-notification-system-events.md
new file mode 100644
index 0000000..c0dfa06
--- /dev/null
+++ b/docs/stories/story-8.10-admin-notification-system-events.md
@@ -0,0 +1,79 @@
+# Story 8.10: Admin Notification - System Events
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As an **admin**,
+I want **to be notified of critical system events**,
+So that **I can address issues promptly**.
+
+## Acceptance Criteria
+
+### Events to Notify
+- [ ] Email delivery failures
+- [ ] Scheduled job failures
+- [ ] Critical application errors
+
+### Content
+- [ ] Event type and description
+- [ ] Timestamp
+- [ ] Relevant details
+- [ ] Recommended action (if any)
+
+### Delivery
+- [ ] Sent immediately (not queued)
+- [ ] Clear subject line indicating urgency
+
+## Technical Notes
+
+```php
+// In App\Exceptions\Handler or bootstrap/app.php
+->withExceptions(function (Exceptions $exceptions) {
+ $exceptions->reportable(function (Throwable $e) {
+ if ($this->shouldNotifyAdmin($e)) {
+ $admin = User::where('user_type', 'admin')->first();
+ $admin?->notify(new SystemErrorNotification($e));
+ }
+ });
+});
+
+class SystemErrorNotification extends Notification
+{
+ public function __construct(
+ public Throwable $exception
+ ) {}
+
+ public function via(object $notifiable): array
+ {
+ return ['mail'];
+ }
+
+ public function toMail(object $notifiable): MailMessage
+ {
+ return (new MailMessage)
+ ->subject('[URGENT] System Error - Libra Law Firm')
+ ->line('A critical error occurred on the platform.')
+ ->line('Error: ' . $this->exception->getMessage())
+ ->line('Time: ' . now()->format('Y-m-d H:i:s'))
+ ->line('Please check the logs for more details.');
+ }
+}
+
+// Queue failure notification
+Queue::failing(function (JobFailed $event) {
+ $admin = User::where('user_type', 'admin')->first();
+ $admin?->notify(new QueueFailureNotification($event));
+});
+```
+
+## Definition of Done
+- [ ] Email failures notified
+- [ ] Job failures notified
+- [ ] Critical errors notified
+- [ ] Sent immediately
+- [ ] Clear urgency indication
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-8.2-welcome-email.md b/docs/stories/story-8.2-welcome-email.md
new file mode 100644
index 0000000..4ed5e74
--- /dev/null
+++ b/docs/stories/story-8.2-welcome-email.md
@@ -0,0 +1,73 @@
+# Story 8.2: Welcome Email (Account Created)
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **new client**,
+I want **to receive a welcome email with my login credentials**,
+So that **I can access the platform**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Sent automatically on user creation by admin
+- [ ] Queued for performance
+
+### Content
+- [ ] Personalized greeting (name/company)
+- [ ] "Your account has been created" message
+- [ ] Login credentials (email, password)
+- [ ] Login URL link with button
+- [ ] Brief platform introduction
+- [ ] Contact info for questions
+
+### Language
+- [ ] Email in user's preferred_language
+- [ ] Arabic template
+- [ ] English template
+
+### Design
+- [ ] Professional branding
+- [ ] Call-to-action button: "Login Now"
+
+## Technical Notes
+
+```php
+class WelcomeEmail extends Mailable
+{
+ use Queueable, SerializesModels;
+
+ public function __construct(
+ public User $user,
+ public string $password
+ ) {}
+
+ public function envelope(): Envelope
+ {
+ return new Envelope(
+ subject: $this->user->preferred_language === 'ar'
+ ? 'مرحباً بك في مكتب ليبرا للمحاماة'
+ : 'Welcome to Libra Law Firm',
+ );
+ }
+
+ public function content(): Content
+ {
+ return new Content(
+ markdown: 'emails.welcome.' . ($this->user->preferred_language ?? 'ar'),
+ );
+ }
+}
+```
+
+## Definition of Done
+- [ ] Email sent on user creation
+- [ ] Credentials included
+- [ ] Arabic template works
+- [ ] English template works
+- [ ] Login button works
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2-3 hours
diff --git a/docs/stories/story-8.3-booking-submitted-confirmation.md b/docs/stories/story-8.3-booking-submitted-confirmation.md
new file mode 100644
index 0000000..d2b95b8
--- /dev/null
+++ b/docs/stories/story-8.3-booking-submitted-confirmation.md
@@ -0,0 +1,66 @@
+# Story 8.3: Booking Submitted Confirmation
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **client**,
+I want **to receive confirmation when I submit a booking request**,
+So that **I know my request was received**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Sent on booking submission
+- [ ] Status: pending
+
+### Content
+- [ ] "Your consultation request has been submitted"
+- [ ] Requested date and time
+- [ ] Problem summary preview
+- [ ] "Pending Review" status note
+- [ ] Expected response timeframe (general)
+
+### Language
+- [ ] Email in client's preferred language
+
+### Design
+- [ ] No action required message
+- [ ] Professional template
+
+## Technical Notes
+
+```php
+class BookingSubmittedEmail extends Mailable
+{
+ use Queueable, SerializesModels;
+
+ public function __construct(
+ public Consultation $consultation
+ ) {}
+
+ public function content(): Content
+ {
+ $locale = $this->consultation->user->preferred_language ?? 'ar';
+
+ return new Content(
+ markdown: "emails.booking.submitted.{$locale}",
+ with: [
+ 'consultation' => $this->consultation,
+ 'user' => $this->consultation->user,
+ ],
+ );
+ }
+}
+```
+
+## Definition of Done
+- [ ] Email sent on submission
+- [ ] Date/time included
+- [ ] Summary preview shown
+- [ ] Pending status clear
+- [ ] Bilingual templates
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-8.4-booking-approved-email.md b/docs/stories/story-8.4-booking-approved-email.md
new file mode 100644
index 0000000..b03adfe
--- /dev/null
+++ b/docs/stories/story-8.4-booking-approved-email.md
@@ -0,0 +1,77 @@
+# Story 8.4: Booking Approved Email
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **client**,
+I want **to receive notification when my booking is approved**,
+So that **I can confirm the appointment and add it to my calendar**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Sent on booking approval by admin
+
+### Content
+- [ ] "Your consultation has been approved"
+- [ ] Confirmed date and time
+- [ ] Duration (45 minutes)
+- [ ] Consultation type (free/paid)
+- [ ] If paid: amount and payment instructions
+- [ ] .ics calendar file attached
+- [ ] "Add to Calendar" button
+- [ ] Location/contact information
+
+### Language
+- [ ] Email in client's preferred language
+
+### Attachment
+- [ ] Valid .ics calendar file
+
+## Technical Notes
+
+```php
+class BookingApprovedEmail extends Mailable
+{
+ use Queueable, SerializesModels;
+
+ public function __construct(
+ public Consultation $consultation,
+ public string $icsContent,
+ public ?string $paymentInstructions = null
+ ) {}
+
+ public function content(): Content
+ {
+ $locale = $this->consultation->user->preferred_language ?? 'ar';
+
+ return new Content(
+ markdown: "emails.booking.approved.{$locale}",
+ with: [
+ 'consultation' => $this->consultation,
+ 'paymentInstructions' => $this->paymentInstructions,
+ ],
+ );
+ }
+
+ public function attachments(): array
+ {
+ return [
+ Attachment::fromData(fn() => $this->icsContent, 'consultation.ics')
+ ->withMime('text/calendar'),
+ ];
+ }
+}
+```
+
+## Definition of Done
+- [ ] Email sent on approval
+- [ ] All details included
+- [ ] Payment info for paid consultations
+- [ ] .ics file attached
+- [ ] Bilingual templates
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-8.5-booking-rejected-email.md b/docs/stories/story-8.5-booking-rejected-email.md
new file mode 100644
index 0000000..e0461dc
--- /dev/null
+++ b/docs/stories/story-8.5-booking-rejected-email.md
@@ -0,0 +1,64 @@
+# Story 8.5: Booking Rejected Email
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **client**,
+I want **to be notified when my booking is rejected**,
+So that **I can understand why and request a new consultation if needed**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Sent on booking rejection by admin
+
+### Content
+- [ ] "Your consultation request could not be approved"
+- [ ] Original requested date and time
+- [ ] Rejection reason (if provided by admin)
+- [ ] Invitation to request new consultation
+- [ ] Contact info for questions
+
+### Tone
+- [ ] Empathetic, professional
+
+### Language
+- [ ] Email in client's preferred language
+
+## Technical Notes
+
+```php
+class BookingRejectedEmail extends Mailable
+{
+ use Queueable, SerializesModels;
+
+ public function __construct(
+ public Consultation $consultation,
+ public ?string $reason = null
+ ) {}
+
+ public function content(): Content
+ {
+ $locale = $this->consultation->user->preferred_language ?? 'ar';
+
+ return new Content(
+ markdown: "emails.booking.rejected.{$locale}",
+ with: [
+ 'consultation' => $this->consultation,
+ 'reason' => $this->reason,
+ ],
+ );
+ }
+}
+```
+
+## Definition of Done
+- [ ] Email sent on rejection
+- [ ] Reason included if provided
+- [ ] Empathetic tone
+- [ ] Bilingual templates
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-8.6-consultation-reminder-24h.md b/docs/stories/story-8.6-consultation-reminder-24h.md
new file mode 100644
index 0000000..e7a5350
--- /dev/null
+++ b/docs/stories/story-8.6-consultation-reminder-24h.md
@@ -0,0 +1,64 @@
+# Story 8.6: Consultation Reminder (24 Hours)
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **client**,
+I want **to receive a reminder 24 hours before my consultation**,
+So that **I don't forget my appointment**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Scheduled job runs daily
+- [ ] Find consultations 24 hours away
+- [ ] Only for approved consultations
+- [ ] Skip cancelled/no-show
+
+### Content
+- [ ] "Reminder: Your consultation is tomorrow"
+- [ ] Date and time
+- [ ] Consultation type
+- [ ] Payment reminder (if paid and not received)
+- [ ] Calendar file link
+- [ ] Any preparation notes
+
+### Language
+- [ ] Email in client's preferred language
+
+## Technical Notes
+
+```php
+// Command: php artisan reminders:send-24h
+class Send24HourReminders extends Command
+{
+ public function handle(): int
+ {
+ $targetTime = now()->addHours(24);
+
+ Consultation::where('status', 'approved')
+ ->whereNull('reminder_24h_sent_at')
+ ->whereDate('scheduled_date', $targetTime->toDateString())
+ ->each(function ($consultation) {
+ $consultation->user->notify(new ConsultationReminder24h($consultation));
+ $consultation->update(['reminder_24h_sent_at' => now()]);
+ });
+
+ return Command::SUCCESS;
+ }
+}
+
+// Schedule: hourly
+```
+
+## Definition of Done
+- [ ] Command runs successfully
+- [ ] Reminders sent to correct consultations
+- [ ] Payment reminder for unpaid
+- [ ] No duplicate reminders
+- [ ] Bilingual templates
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-8.7-consultation-reminder-2h.md b/docs/stories/story-8.7-consultation-reminder-2h.md
new file mode 100644
index 0000000..d65b7b9
--- /dev/null
+++ b/docs/stories/story-8.7-consultation-reminder-2h.md
@@ -0,0 +1,68 @@
+# Story 8.7: Consultation Reminder (2 Hours)
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **client**,
+I want **to receive a reminder 2 hours before my consultation**,
+So that **I'm prepared and ready**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Scheduled job runs every 15 minutes
+- [ ] Find consultations 2 hours away
+- [ ] Only for approved consultations
+- [ ] Skip cancelled/no-show
+
+### Content
+- [ ] "Your consultation is in 2 hours"
+- [ ] Date and time
+- [ ] Final payment reminder (if applicable)
+- [ ] Contact info for last-minute issues
+
+### Language
+- [ ] Email in client's preferred language
+
+## Technical Notes
+
+```php
+// Command: php artisan reminders:send-2h
+// Schedule: everyFifteenMinutes
+class Send2HourReminders extends Command
+{
+ public function handle(): int
+ {
+ $targetTime = now()->addHours(2);
+ $windowStart = $targetTime->copy()->subMinutes(7);
+ $windowEnd = $targetTime->copy()->addMinutes(7);
+
+ Consultation::where('status', 'approved')
+ ->whereNull('reminder_2h_sent_at')
+ ->whereDate('scheduled_date', today())
+ ->get()
+ ->filter(function ($c) use ($windowStart, $windowEnd) {
+ $time = Carbon::parse($c->scheduled_date->format('Y-m-d') . ' ' . $c->scheduled_time);
+ return $time->between($windowStart, $windowEnd);
+ })
+ ->each(function ($consultation) {
+ $consultation->user->notify(new ConsultationReminder2h($consultation));
+ $consultation->update(['reminder_2h_sent_at' => now()]);
+ });
+
+ return Command::SUCCESS;
+ }
+}
+```
+
+## Definition of Done
+- [ ] Command runs successfully
+- [ ] Correct timing (2 hours before)
+- [ ] Payment reminder if unpaid
+- [ ] No duplicate reminders
+- [ ] Bilingual templates
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 2-3 hours
diff --git a/docs/stories/story-8.8-timeline-update-notification.md b/docs/stories/story-8.8-timeline-update-notification.md
new file mode 100644
index 0000000..a119be3
--- /dev/null
+++ b/docs/stories/story-8.8-timeline-update-notification.md
@@ -0,0 +1,70 @@
+# Story 8.8: Timeline Update Notification
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As a **client**,
+I want **to be notified when my case timeline is updated**,
+So that **I stay informed about my case progress**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Sent on timeline update creation
+- [ ] Queued for performance
+
+### Content
+- [ ] "Update on your case: [Case Name]"
+- [ ] Case reference number
+- [ ] Update content (full or summary)
+- [ ] Date of update
+- [ ] "View Timeline" button/link
+
+### Language
+- [ ] Email in client's preferred language
+
+### Design
+- [ ] Professional, informative tone
+
+## Technical Notes
+
+```php
+class TimelineUpdateNotification extends Notification
+{
+ use Queueable;
+
+ public function __construct(
+ public TimelineUpdate $update
+ ) {}
+
+ public function via(object $notifiable): array
+ {
+ return ['mail'];
+ }
+
+ public function toMail(object $notifiable): MailMessage
+ {
+ $locale = $notifiable->preferred_language ?? 'ar';
+ $timeline = $this->update->timeline;
+
+ return (new MailMessage)
+ ->subject($this->getSubject($locale, $timeline->case_name))
+ ->markdown("emails.timeline.update.{$locale}", [
+ 'update' => $this->update,
+ 'timeline' => $timeline,
+ ]);
+ }
+}
+```
+
+## Definition of Done
+- [ ] Email sent on update
+- [ ] Case info included
+- [ ] Update content shown
+- [ ] View link works
+- [ ] Bilingual templates
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-8.9-admin-notification-new-booking.md b/docs/stories/story-8.9-admin-notification-new-booking.md
new file mode 100644
index 0000000..b23201d
--- /dev/null
+++ b/docs/stories/story-8.9-admin-notification-new-booking.md
@@ -0,0 +1,74 @@
+# Story 8.9: Admin Notification - New Booking
+
+## Epic Reference
+**Epic 8:** Email Notification System
+
+## User Story
+As an **admin**,
+I want **to be notified when a client submits a booking request**,
+So that **I can review and respond promptly**.
+
+## Acceptance Criteria
+
+### Trigger
+- [ ] Sent on booking submission by client
+
+### Recipient
+- [ ] Admin email address
+
+### Content
+- [ ] "New Consultation Request"
+- [ ] Client name (individual or company)
+- [ ] Requested date and time
+- [ ] Problem summary (full)
+- [ ] Client contact info
+- [ ] "Review Request" button/link
+
+### Priority
+- [ ] Clear indicator in subject line
+
+### Language
+- [ ] Admin language preference (or default)
+
+## Technical Notes
+
+```php
+class NewBookingAdminNotification extends Notification
+{
+ use Queueable;
+
+ public function __construct(
+ public Consultation $consultation
+ ) {}
+
+ public function via(object $notifiable): array
+ {
+ return ['mail'];
+ }
+
+ public function toMail(object $notifiable): MailMessage
+ {
+ return (new MailMessage)
+ ->subject('[Action Required] New Consultation Request')
+ ->markdown('emails.admin.new-booking', [
+ 'consultation' => $this->consultation,
+ 'client' => $this->consultation->user,
+ ]);
+ }
+}
+
+// Trigger in booking submission
+$admin = User::where('user_type', 'admin')->first();
+$admin?->notify(new NewBookingAdminNotification($consultation));
+```
+
+## Definition of Done
+- [ ] Email sent to admin on new booking
+- [ ] All client info included
+- [ ] Problem summary shown
+- [ ] Review link works
+- [ ] Priority clear in subject
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-9.1-color-system-implementation.md b/docs/stories/story-9.1-color-system-implementation.md
new file mode 100644
index 0000000..da22b65
--- /dev/null
+++ b/docs/stories/story-9.1-color-system-implementation.md
@@ -0,0 +1,67 @@
+# Story 9.1: Color System Implementation
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **developer**,
+I want **to implement the complete color palette as Tailwind CSS theme**,
+So that **brand colors are consistently applied throughout the application**.
+
+## Acceptance Criteria
+
+### Primary Colors
+- [ ] Dark Navy Blue: #0A1F44 (backgrounds, headers)
+- [ ] Gold/Brass: #D4AF37 (accents, buttons, links)
+
+### Supporting Colors
+- [ ] Light Gold: #F4E4B8 (hover states)
+- [ ] Off-White/Cream: #F9F7F4 (text, cards)
+- [ ] Charcoal Gray: #2C3E50 (secondary text)
+- [ ] Success Green: #27AE60 (approved status)
+- [ ] Warning Red: #E74C3C (rejected, errors)
+- [ ] Pending Yellow: #F39C12 (pending status)
+
+### Implementation
+- [ ] Custom Tailwind colors configured via @theme
+- [ ] Color variables for easy maintenance
+- [ ] Semantic color aliases (primary, accent, etc.)
+
+## Technical Notes
+
+```css
+/* resources/css/app.css */
+@import "tailwindcss";
+
+@theme {
+ /* Primary */
+ --color-navy: #0A1F44;
+ --color-gold: #D4AF37;
+
+ /* Supporting */
+ --color-gold-light: #F4E4B8;
+ --color-cream: #F9F7F4;
+ --color-charcoal: #2C3E50;
+
+ /* Status */
+ --color-success: #27AE60;
+ --color-danger: #E74C3C;
+ --color-warning: #F39C12;
+
+ /* Semantic aliases */
+ --color-primary: var(--color-navy);
+ --color-accent: var(--color-gold);
+ --color-background: var(--color-cream);
+ --color-text: var(--color-charcoal);
+}
+```
+
+## Definition of Done
+- [ ] All colors defined in Tailwind
+- [ ] Colors work with utility classes (bg-navy, text-gold)
+- [ ] Dark mode consideration documented
+- [ ] Tests pass
+- [ ] Code formatted with Pint
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-9.10-accessibility-compliance.md b/docs/stories/story-9.10-accessibility-compliance.md
new file mode 100644
index 0000000..1f14780
--- /dev/null
+++ b/docs/stories/story-9.10-accessibility-compliance.md
@@ -0,0 +1,99 @@
+# Story 9.10: Accessibility Compliance
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user with disabilities**,
+I want **the platform to be accessible**,
+So that **I can use it regardless of my abilities**.
+
+## Acceptance Criteria
+
+### Color Contrast
+- [ ] Body text: 4.5:1 minimum
+- [ ] Large text: 3:1 minimum
+- [ ] UI elements: 3:1 minimum
+
+### Focus Indicators
+- [ ] Visible focus outline (gold)
+- [ ] Not just color change
+
+### Keyboard Navigation
+- [ ] All interactive elements reachable
+- [ ] Logical tab order
+- [ ] Skip to main content link
+
+### Screen Readers
+- [ ] Proper heading hierarchy (h1 > h2 > h3)
+- [ ] Alt text for images
+- [ ] ARIA labels where needed
+- [ ] Form labels associated
+
+### Motion
+- [ ] Respect prefers-reduced-motion
+- [ ] No auto-playing animations
+
+## Technical Notes
+
+```css
+/* Focus styles */
+:focus-visible {
+ @apply outline-2 outline-offset-2 outline-gold;
+}
+
+/* Skip link */
+.skip-link {
+ @apply sr-only focus:not-sr-only focus:absolute focus:top-4 focus:start-4
+ focus:bg-gold focus:text-navy focus:px-4 focus:py-2 focus:rounded focus:z-50;
+}
+
+/* Reduced motion */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+```
+
+```blade
+
+
+ {{ __('accessibility.skip_to_content') }}
+
+
+
+
+ {{ $slot }}
+
+
+
+
+
+
+
+
+```
+
+### Testing Tools
+- axe DevTools
+- WAVE
+- Lighthouse
+- Screen reader (VoiceOver/NVDA)
+
+## Definition of Done
+- [ ] Color contrast passes
+- [ ] Focus indicators visible
+- [ ] Keyboard navigation works
+- [ ] Screen reader friendly
+- [ ] Reduced motion respected
+- [ ] Skip link works
+- [ ] Lighthouse accessibility > 90
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 4-5 hours
diff --git a/docs/stories/story-9.11-animations-micro-interactions.md b/docs/stories/story-9.11-animations-micro-interactions.md
new file mode 100644
index 0000000..ee0cae9
--- /dev/null
+++ b/docs/stories/story-9.11-animations-micro-interactions.md
@@ -0,0 +1,131 @@
+# Story 9.11: Animations & Micro-interactions
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **subtle, professional animations**,
+So that **the interface feels polished and responsive**.
+
+## Acceptance Criteria
+
+### Transitions
+- [ ] Button hover: 150ms ease
+- [ ] Card hover: 200ms ease
+- [ ] Modal open/close: 200ms
+- [ ] Page transitions (optional)
+
+### Loading States
+- [ ] Skeleton loaders for content
+- [ ] Spinner for actions
+- [ ] Progress indicators
+
+### Feedback Animations
+- [ ] Success checkmark
+- [ ] Error shake
+- [ ] Toast slide-in
+
+### Hover Effects
+- [ ] Links: Color transition
+- [ ] Cards: Lift effect
+- [ ] Buttons: Background transition
+
+### Requirements
+- [ ] All animations subtle, professional
+- [ ] Under 300ms duration
+- [ ] Respect prefers-reduced-motion
+
+## Technical Notes
+
+```css
+/* Base transitions */
+.transition-default {
+ @apply transition-all duration-150 ease-in-out;
+}
+
+.transition-slow {
+ @apply transition-all duration-200 ease-in-out;
+}
+
+/* Button hover */
+.btn {
+ @apply transition-colors duration-150;
+}
+
+/* Card lift */
+.card-hover {
+ @apply transition-all duration-200;
+}
+.card-hover:hover {
+ @apply -translate-y-0.5 shadow-md;
+}
+
+/* Skeleton loader */
+.skeleton {
+ @apply animate-pulse bg-charcoal/10 rounded;
+}
+
+/* Toast animation */
+.toast-enter {
+ @apply transform translate-x-full opacity-0;
+}
+.toast-enter-active {
+ @apply transform translate-x-0 opacity-100 transition-all duration-200;
+}
+
+/* Success checkmark */
+@keyframes checkmark {
+ 0% { stroke-dashoffset: 100; }
+ 100% { stroke-dashoffset: 0; }
+}
+
+.checkmark-animated path {
+ stroke-dasharray: 100;
+ animation: checkmark 0.3s ease-in-out forwards;
+}
+
+/* Error shake */
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-5px); }
+ 75% { transform: translateX(5px); }
+}
+
+.shake {
+ animation: shake 0.3s ease-in-out;
+}
+```
+
+```blade
+
+@props(['lines' => 3])
+
+
+ @for($i = 0; $i < $lines; $i++)
+
+ @endfor
+
+
+
+
+
+
{{ __('common.loading') }}
+
+```
+
+## Definition of Done
+- [ ] Button transitions work
+- [ ] Card hover effects work
+- [ ] Skeleton loaders work
+- [ ] Spinners work
+- [ ] Toast animations work
+- [ ] All animations subtle
+- [ ] Reduced motion respected
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 4 hours
diff --git a/docs/stories/story-9.2-typography-system.md b/docs/stories/story-9.2-typography-system.md
new file mode 100644
index 0000000..74d4bf7
--- /dev/null
+++ b/docs/stories/story-9.2-typography-system.md
@@ -0,0 +1,72 @@
+# Story 9.2: Typography System
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **professional, readable typography**,
+So that **the platform feels polished and content is easy to read**.
+
+## Acceptance Criteria
+
+### Arabic Fonts
+- [ ] Primary: Cairo or Tajawal (Google Fonts)
+- [ ] Weights: Light (300), Regular (400), SemiBold (600), Bold (700)
+
+### English Fonts
+- [ ] Primary: Montserrat or Lato
+- [ ] Weights: Light (300), Regular (400), SemiBold (600), Bold (700)
+
+### Font Hierarchy
+- [ ] H1: Bold, 2.5rem (40px)
+- [ ] H2: SemiBold, 2rem (32px)
+- [ ] H3: SemiBold, 1.5rem (24px)
+- [ ] Body: Regular, 1rem (16px)
+- [ ] Small: Regular, 0.875rem (14px)
+
+### Performance
+- [ ] Line height: 1.6 body, 1.3 headings
+- [ ] font-display: swap
+- [ ] Preload critical fonts
+
+## Technical Notes
+
+```css
+/* Google Fonts import */
+@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&family=Montserrat:wght@300;400;600;700&display=swap');
+
+@theme {
+ --font-arabic: 'Cairo', 'Tajawal', sans-serif;
+ --font-english: 'Montserrat', 'Lato', sans-serif;
+
+ --font-size-xs: 0.75rem;
+ --font-size-sm: 0.875rem;
+ --font-size-base: 1rem;
+ --font-size-lg: 1.125rem;
+ --font-size-xl: 1.25rem;
+ --font-size-2xl: 1.5rem;
+ --font-size-3xl: 2rem;
+ --font-size-4xl: 2.5rem;
+}
+
+/* Dynamic font selection */
+html[lang="ar"] body {
+ font-family: var(--font-arabic);
+}
+
+html[lang="en"] body {
+ font-family: var(--font-english);
+}
+```
+
+## Definition of Done
+- [ ] Fonts load correctly
+- [ ] Arabic fonts work with RTL
+- [ ] English fonts work with LTR
+- [ ] Font hierarchy applied
+- [ ] Performance optimized
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-9.3-logo-integration.md b/docs/stories/story-9.3-logo-integration.md
new file mode 100644
index 0000000..c855dce
--- /dev/null
+++ b/docs/stories/story-9.3-logo-integration.md
@@ -0,0 +1,76 @@
+# Story 9.3: Logo Integration
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **visitor**,
+I want **to see the Libra scales logo prominently displayed**,
+So that **I recognize the firm's branding**.
+
+## Acceptance Criteria
+
+### Logo Placement
+- [ ] Navigation: Top left (desktop), centered (mobile)
+- [ ] Footer: Smaller version
+- [ ] Email templates: Header
+- [ ] PDF exports: Header
+
+### Logo Specifications
+- [ ] Minimum size: 120px (desktop), 80px (mobile)
+- [ ] Clear space: 20px padding minimum
+
+### Format Support
+- [ ] SVG primary (scalable)
+- [ ] PNG fallback
+
+### Color Variations
+- [ ] Full color (gold on navy)
+- [ ] Reversed (navy on light)
+- [ ] Monochrome gold
+
+### Features
+- [ ] Responsive sizing
+- [ ] Accessible alt text
+
+## Technical Notes
+
+```blade
+
+@props([
+ 'size' => 'default',
+ 'variant' => 'full'
+])
+
+@php
+$sizes = [
+ 'small' => 'h-8',
+ 'default' => 'h-12',
+ 'large' => 'h-16',
+];
+
+$variants = [
+ 'full' => 'logo.svg',
+ 'reversed' => 'logo-reversed.svg',
+ 'mono' => 'logo-mono.svg',
+];
+@endphp
+
+
merge(['class' => $sizes[$size]]) }}
+/>
+```
+
+## Definition of Done
+- [ ] Logo displays in navigation
+- [ ] Logo displays in footer
+- [ ] Logo in email templates
+- [ ] Responsive sizing works
+- [ ] All color variants available
+- [ ] Alt text correct
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Low | **Effort:** 2 hours
diff --git a/docs/stories/story-9.4-component-styling-buttons.md b/docs/stories/story-9.4-component-styling-buttons.md
new file mode 100644
index 0000000..9ab8de9
--- /dev/null
+++ b/docs/stories/story-9.4-component-styling-buttons.md
@@ -0,0 +1,79 @@
+# Story 9.4: Component Styling - Buttons
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **consistent, professional button styling**,
+So that **interactive elements are clear and visually appealing**.
+
+## Acceptance Criteria
+
+### Primary Button
+- [ ] Background: Gold (#D4AF37)
+- [ ] Text: Dark Navy Blue
+- [ ] Hover: Light Gold (#F4E4B8)
+- [ ] Border-radius: 6px
+- [ ] Padding: 12px 24px
+
+### Secondary Button
+- [ ] Background: Transparent
+- [ ] Border: 2px solid Gold
+- [ ] Text: Gold
+- [ ] Hover: Gold background, Navy text
+
+### Disabled State
+- [ ] Background: #CCCCCC
+- [ ] Text: #666666
+- [ ] No hover effect
+- [ ] Cursor: not-allowed
+
+### Danger Button
+- [ ] Background: #E74C3C
+- [ ] Text: White
+
+### Features
+- [ ] Loading states with spinner
+- [ ] Focus states for accessibility
+- [ ] Flux UI button integration
+
+## Technical Notes
+
+```css
+/* Extend Flux UI buttons */
+.btn-primary {
+ @apply bg-gold text-navy hover:bg-gold-light rounded-md px-6 py-3 font-semibold transition-colors;
+}
+
+.btn-secondary {
+ @apply bg-transparent border-2 border-gold text-gold hover:bg-gold hover:text-navy rounded-md px-6 py-3 font-semibold transition-colors;
+}
+
+.btn-danger {
+ @apply bg-danger text-white hover:bg-danger/90 rounded-md px-6 py-3 font-semibold transition-colors;
+}
+
+/* Loading state */
+.btn-loading {
+ @apply relative pointer-events-none;
+}
+
+.btn-loading::after {
+ content: '';
+ @apply absolute inset-0 flex items-center justify-center;
+ /* Spinner styles */
+}
+```
+
+## Definition of Done
+- [ ] Primary button styled
+- [ ] Secondary button styled
+- [ ] Danger button styled
+- [ ] Disabled states work
+- [ ] Loading states work
+- [ ] Focus states accessible
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-9.5-component-styling-forms.md b/docs/stories/story-9.5-component-styling-forms.md
new file mode 100644
index 0000000..b0946e4
--- /dev/null
+++ b/docs/stories/story-9.5-component-styling-forms.md
@@ -0,0 +1,86 @@
+# Story 9.5: Component Styling - Forms
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **consistent, accessible form styling**,
+So that **data entry is intuitive and error states are clear**.
+
+## Acceptance Criteria
+
+### Input Fields
+- [ ] Border: Charcoal Gray
+- [ ] Focus: Gold border
+- [ ] Border-radius: 6px
+- [ ] Padding: 12px 16px
+
+### Textareas
+- [ ] Same styling as inputs
+- [ ] Minimum height: 120px
+
+### Select Dropdowns
+- [ ] Custom styled (not native)
+- [ ] Consistent with inputs
+
+### Checkboxes & Radios
+- [ ] Custom styled with gold accent
+- [ ] Clear checked state
+
+### Labels
+- [ ] SemiBold weight
+- [ ] Required indicator (*)
+
+### Error States
+- [ ] Red border
+- [ ] Error message below field
+
+### RTL Support
+- [ ] All fields align correctly in RTL
+
+## Technical Notes
+
+```css
+/* Form field styling */
+.input-field {
+ @apply w-full border border-charcoal/30 rounded-md px-4 py-3
+ focus:border-gold focus:ring-2 focus:ring-gold/20
+ transition-colors outline-none;
+}
+
+.input-error {
+ @apply border-danger focus:border-danger focus:ring-danger/20;
+}
+
+.form-label {
+ @apply block text-sm font-semibold text-charcoal mb-2;
+}
+
+.form-label-required::after {
+ content: ' *';
+ @apply text-danger;
+}
+
+.error-message {
+ @apply text-sm text-danger mt-1;
+}
+
+/* Custom checkbox */
+.checkbox-custom {
+ @apply w-5 h-5 rounded border-charcoal/30 text-gold
+ focus:ring-gold focus:ring-offset-0;
+}
+```
+
+## Definition of Done
+- [ ] Input styling consistent
+- [ ] Textarea styling consistent
+- [ ] Select styling works
+- [ ] Checkbox/radio styled
+- [ ] Error states clear
+- [ ] RTL alignment correct
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3-4 hours
diff --git a/docs/stories/story-9.6-component-styling-cards-containers.md b/docs/stories/story-9.6-component-styling-cards-containers.md
new file mode 100644
index 0000000..cd653f0
--- /dev/null
+++ b/docs/stories/story-9.6-component-styling-cards-containers.md
@@ -0,0 +1,92 @@
+# Story 9.6: Component Styling - Cards & Containers
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **consistent card and container styling**,
+So that **content is well-organized and visually appealing**.
+
+## Acceptance Criteria
+
+### Card Styling
+- [ ] Background: Off-white/cream
+- [ ] Box-shadow: 0 2px 8px rgba(0,0,0,0.1)
+- [ ] Border-radius: 8px
+- [ ] Padding: 24px
+
+### Gold Border Highlight
+- [ ] Optional gold top/left border
+- [ ] For featured/important cards
+
+### Hover States
+- [ ] Subtle lift effect
+- [ ] Shadow increase
+
+### Dashboard Stat Cards
+- [ ] Icon with gold accent
+- [ ] Large number display
+- [ ] Trend indicator
+
+### List Cards
+- [ ] Consistent item spacing
+- [ ] Clear click targets
+
+### Containers
+- [ ] Max-width: 1200px, centered
+
+## Technical Notes
+
+```blade
+
+@props([
+ 'variant' => 'default',
+ 'hover' => false,
+ 'highlight' => false
+])
+
+merge([
+ 'class' => collect([
+ 'bg-cream rounded-lg p-6',
+ 'shadow-sm' => $variant === 'default',
+ 'shadow-md' => $variant === 'elevated',
+ 'hover:shadow-md hover:-translate-y-0.5 transition-all cursor-pointer' => $hover,
+ 'border-s-4 border-gold' => $highlight,
+ ])->filter()->implode(' ')
+]) }}>
+ {{ $slot }}
+
+
+
+@props(['icon', 'value', 'label', 'trend' => null])
+
+
+
+
+
+
+
+
{{ $value }}
+
{{ $label }}
+ @if($trend)
+
+ {{ $trend > 0 ? '+' : '' }}{{ $trend }}%
+
+ @endif
+
+
+
+```
+
+## Definition of Done
+- [ ] Card component created
+- [ ] Shadow and radius consistent
+- [ ] Hover effects work
+- [ ] Stat cards work
+- [ ] Highlight variant works
+- [ ] Container max-width applied
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 3 hours
diff --git a/docs/stories/story-9.7-navigation-footer-styling.md b/docs/stories/story-9.7-navigation-footer-styling.md
new file mode 100644
index 0000000..f50cfd9
--- /dev/null
+++ b/docs/stories/story-9.7-navigation-footer-styling.md
@@ -0,0 +1,104 @@
+# Story 9.7: Navigation & Footer Styling
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **professional navigation and footer styling**,
+So that **I can easily navigate the site and find information**.
+
+## Acceptance Criteria
+
+### Navigation Bar
+- [ ] Fixed top position
+- [ ] Navy blue background
+- [ ] Logo left (desktop), centered (mobile)
+- [ ] Gold text for links
+- [ ] Active link indicator
+- [ ] Language toggle styled
+- [ ] Responsive mobile menu
+
+### Mobile Menu
+- [ ] Full-width dropdown/slide
+- [ ] Navy background
+- [ ] Clear touch targets (44px+)
+- [ ] Smooth animation
+
+### Footer
+- [ ] Navy blue background
+- [ ] Logo and firm info
+- [ ] Contact details
+- [ ] Links to Terms/Privacy
+- [ ] Copyright notice
+- [ ] Sticky footer (always at bottom)
+
+## Technical Notes
+
+```blade
+
+
+
+
+
+```
+
+## Definition of Done
+- [ ] Navigation styled correctly
+- [ ] Mobile menu works
+- [ ] Footer styled correctly
+- [ ] Sticky footer works
+- [ ] Links functional
+- [ ] RTL layout works
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** Medium | **Effort:** 4 hours
diff --git a/docs/stories/story-9.8-rtl-ltr-layout-perfection.md b/docs/stories/story-9.8-rtl-ltr-layout-perfection.md
new file mode 100644
index 0000000..d758575
--- /dev/null
+++ b/docs/stories/story-9.8-rtl-ltr-layout-perfection.md
@@ -0,0 +1,90 @@
+# Story 9.8: RTL/LTR Layout Perfection
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **perfect RTL layout for Arabic and LTR for English**,
+So that **the content is natural to read in my language**.
+
+## Acceptance Criteria
+
+### RTL (Arabic)
+- [ ] Text aligns right
+- [ ] Navigation mirrors (logo right)
+- [ ] Form labels on right
+- [ ] Icons/arrows flip appropriately
+- [ ] Margins/paddings swap
+
+### LTR (English)
+- [ ] Standard left-to-right layout
+- [ ] Proper text alignment
+
+### Transitions
+- [ ] Seamless language toggle
+- [ ] No layout breaks on switch
+
+### Component Support
+- [ ] Calendar RTL support
+- [ ] Tables RTL support
+- [ ] All components tested in both modes
+
+## Technical Notes
+
+```css
+/* Use logical properties */
+.card {
+ margin-inline-start: 1rem; /* margin-left in LTR, margin-right in RTL */
+ padding-inline-end: 1rem; /* padding-right in LTR, padding-left in RTL */
+}
+
+/* RTL-aware utilities */
+[dir="rtl"] .flip-rtl {
+ transform: scaleX(-1);
+}
+
+/* Tailwind RTL plugin configuration */
+@theme {
+ /* Use logical properties by default */
+}
+```
+
+```blade
+
+ app()->getLocale() === 'ar'])
+/>
+
+
+
+
+
+
+
+
+
+
+```
+
+### Testing Checklist
+- [ ] Navigation layout
+- [ ] Form layouts
+- [ ] Card layouts
+- [ ] Table layouts
+- [ ] Modal layouts
+- [ ] Dropdown menus
+- [ ] Pagination
+
+## Definition of Done
+- [ ] RTL renders correctly
+- [ ] LTR renders correctly
+- [ ] Language switch seamless
+- [ ] Icons flip correctly
+- [ ] All components tested
+- [ ] No layout breaks
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** High | **Effort:** 5-6 hours
diff --git a/docs/stories/story-9.9-responsive-design-implementation.md b/docs/stories/story-9.9-responsive-design-implementation.md
new file mode 100644
index 0000000..8759ba1
--- /dev/null
+++ b/docs/stories/story-9.9-responsive-design-implementation.md
@@ -0,0 +1,91 @@
+# Story 9.9: Responsive Design Implementation
+
+## Epic Reference
+**Epic 9:** Design & Branding Implementation
+
+## User Story
+As a **user**,
+I want **the platform to work perfectly on all device sizes**,
+So that **I can use it on my phone, tablet, or desktop**.
+
+## Acceptance Criteria
+
+### Breakpoints
+- [ ] Mobile: < 576px
+- [ ] Tablet: 576px - 991px
+- [ ] Desktop: 992px - 1199px
+- [ ] Large Desktop: >= 1200px
+
+### Mobile Optimizations
+- [ ] Touch-friendly targets (44px+)
+- [ ] Readable font sizes
+- [ ] Single column layouts
+- [ ] Collapsible sections
+
+### Tablet Optimizations
+- [ ] Two-column where appropriate
+- [ ] Sidebar collapsible
+
+### Desktop Optimizations
+- [ ] Full layouts with sidebars
+- [ ] Multi-column grids
+
+### Specific Features
+- [ ] All forms usable on mobile
+- [ ] Calendar usable on mobile
+- [ ] Tables scroll horizontally
+- [ ] No horizontal scroll on any viewport
+
+## Technical Notes
+
+```css
+/* Mobile-first approach */
+.dashboard-grid {
+ @apply grid gap-4;
+ @apply grid-cols-1; /* Mobile */
+ @apply sm:grid-cols-2; /* Tablet */
+ @apply lg:grid-cols-3; /* Desktop */
+ @apply xl:grid-cols-4; /* Large */
+}
+
+/* Touch targets */
+.touch-target {
+ @apply min-h-[44px] min-w-[44px];
+}
+
+/* Responsive table wrapper */
+.table-responsive {
+ @apply overflow-x-auto -mx-4 px-4;
+}
+
+/* Collapsible sidebar */
+@media (max-width: 991px) {
+ .sidebar {
+ @apply fixed inset-y-0 start-0 w-64 transform -translate-x-full transition-transform z-40;
+ }
+ .sidebar.open {
+ @apply translate-x-0;
+ }
+}
+```
+
+### Testing Devices
+- iPhone SE (375px)
+- iPhone 14 (390px)
+- iPad (768px)
+- iPad Pro (1024px)
+- Desktop (1280px)
+- Large Desktop (1920px)
+
+## Definition of Done
+- [ ] Mobile layout works
+- [ ] Tablet layout works
+- [ ] Desktop layout works
+- [ ] No horizontal scroll
+- [ ] Touch targets 44px+
+- [ ] Forms usable on mobile
+- [ ] Calendar usable on mobile
+- [ ] Tests pass
+
+## Estimation
+**Complexity:** High | **Effort:** 5-6 hours