Files
libra/CLAUDE.md
T

12 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Libra Law Firm is a bilingual (Arabic/English) web platform for managing client consultations, appointments, and case tracking. It serves as both a public-facing website and an internal management tool.

Domain: libra.adv.ps (production), libra.test (local)

Production Environment

  • Server: h1 (h1.elenoslabs.com, 163.172.67.77) — Rocky Linux 9, ElenosLabs-owned. Not 69.62.106.94 — that stale IP is left over from before the migration to h1 and does not serve this app; a past .env even had MAIL_HOST pointed at it by mistake (see Mail below). Full server details: wiki/prod_servers/elenoslabs/h1server.md (Jarvis workspace), § libra.adv.ps.
  • App user: libra — app root /home/libra/public_html/, logs /home/libra/logs/
  • PHP: 8.4 (use php84 command — there is no bare php binary on this box; even Composer needs php84 /usr/local/bin/composer ...)
  • Database: MariaDB, Redis (sessions/cache/queue)
  • Queue worker: libra-queue.service (queue:work redis) — a long-running process, see the queue note below.
  • Git: production was file-copied at initial migration and only properly linked to git for the first time on 2026-09-28 (git checkout -f -B main origin/main after backing up the prior state) — before that date, git pull on production did nothing (empty local branch, no commits). It is now a normal git-tracked checkout; a plain git pull works going forward.

Mail

MAIL_HOST must be mx1.elenoslabs.com (h1's own Postfix/Dovecot stack, MX for adv.ps) — not mail.adv.ps, which resolves to the stale 69.62.106.94 host and times out. This caused a real incident (2026-09-28): the workshop-registration form's emails silently never arrived because of this wrong value, and a separate real bug in App\Services\AdminNotificationService (a Carbon TypeError on a string throttle_minutes config value — fixed, see git history around 2026-09-28) masked the actual SMTP error in the logs the whole time it was broken. If mail seems to silently fail again, check MAIL_HOST first, then storage/logs/laravel.log for a TransportException.

Production Commands

All artisan commands on production require php84 prefix:

php84 artisan migrate
php84 artisan config:cache
php84 artisan optimize:clear

Deployment Checklist

# On production server (as the libra user, in /home/libra/public_html)
git pull
php84 /usr/local/bin/composer install --no-dev --optimize-autoloader   # only if composer.json/lock changed
npm install && npm run build                                           # only if frontend/Tailwind classes changed
php84 artisan optimize:clear
php84 artisan config:cache
php84 artisan route:cache
php84 artisan view:cache

# IMPORTANT: always restart PHP-FPM after ANY deploy, even a content/lang-only change.
# 2026-09-28 incident: opcache.validate_timestamps is On with revalidate_freq=2, which should
# have picked up a changed lang/*.php file within 2 seconds on its own — it didn't, and only a
# full php84-php-fpm restart made the change visible. Root cause not fully diagnosed; until it
# is, treat the restart as mandatory on every deploy, not just composer/config changes.
sudo systemctl restart php84-php-fpm

# IMPORTANT: always restart the queue worker too — it's a long-running process that holds old
# code and old .env/config values in memory until restarted, independent of the php-fpm restart.
php84 artisan queue:restart

Note: The server has both PHP 8.3 and 8.4. Always use php84 for artisan commands. Failing to restart PHP-FPM can cause cached config issues.

Development Commands

# Initial setup (install deps, create .env, migrate, build assets)
composer run setup

# Start development (server, queue, logs, vite in parallel)
composer run dev

# Run all tests
composer run test

# Run specific test file
php artisan test tests/Feature/Auth/AuthenticationTest.php

# Run tests matching a filter
php artisan test --filter=testName

# Build frontend assets
npm run build

# Format PHP code
vendor/bin/pint --dirty

# Create new Volt component with test
php artisan make:volt pages/component-name --pest

# Create model with migration and factory
php artisan make:model ModelName -mf

Architecture

Technology Stack

  • Backend: Laravel 12, PHP 8.4, Laravel Fortify (auth)
  • Frontend: Livewire 3, Volt (single-file components), Flux UI Free, Tailwind CSS 4
  • Database: SQLite (dev), MariaDB (production)
  • Testing: Pest 4

Key Directories

  • resources/views/livewire/ - Volt single-file components (class-based pattern)
  • resources/views/components/ - Reusable Blade components
  • app/Actions/Fortify/ - Authentication business logic
  • app/Providers/FortifyServiceProvider.php - Custom auth views
  • app/Enums/ - UserType, UserStatus, ConsultationType, ConsultationStatus, PaymentStatus, TimelineStatus, PostStatus
  • docs/prd.md - Full product requirements document
  • docs/architecture.md - Complete architecture document
  • docs/ux-design.md - Design spec. Part A = the public-site redesign (approved by the client 2026-10-31, mockup docs/ux-mockup.html); Part B = the pre-redesign system, which still governs the dashboards, emails and the Nablus form
  • docs/tasks/ - Current task/status tracker (statuses.md is the status source of truth). One row per epic (parent) and per story (subtask), e.g. 3.md / 3-4.md.
  • docs/stories/, docs/epics/, docs/qa/gates/ - Legacy. Original BMAD-agent build records (98→102 stories across 16 epics). Kept for implementation history and traceability, no longer the status source — docs/tasks/statuses.md superseded them 2026-09-27. .bmad-core/ and .claude/commands/BMad/ are the retired BMAD toolchain, also kept but unused.

Pipeline takeover (2026-09-27)

This project was built by BMAD agents and has been handed to a different pipeline (Jarvis/ElenosLabs tooling) for any further work. docs/tasks/statuses.md is now authoritative for what's open — most subtasks are QA Review (BMAD's "Ready for Review" with no final close-out), a couple are Draft (never started: 3-9 guest booking request, 13-5 auth-page visual QA). Cross-check a task's own docs/stories/story-<id>-*.md file for full history/QA notes before starting new work on it.

Services

  • app/Services/AvailabilityService.php - Calculates available time slots for bookings
  • app/Services/CalendarService.php - Generates iCalendar files for consultations
  • app/Services/CaptchaService.php - Math-based captcha for guest booking spam protection

Domain Model

Core models in app/Models/:

  • User - Three types: admin, individual (client), company (client)
  • Consultation - Booking requests between clients and admin
  • Timeline - Case tracking for clients
  • TimelineUpdate - Updates within a timeline
  • WorkingHour - Admin's available hours for booking
  • BlockedTime - Time slots blocked from booking
  • Post - Blog/legal content articles
  • Notification - User notifications
  • AdminLog - Audit logging for admin actions

Route Structure

/                     - Public home
/booking              - Consultation booking
/posts                - Public posts/blog
/language/{locale}    - Language switch (ar/en)

/admin/*              - Admin dashboard (requires admin middleware)
/client/*             - Client dashboard
/settings/*           - User settings (profile, password, appearance; 2FA route only registers if the feature is re-enabled, see below)

Middleware

  • admin - Ensures user is admin (EnsureUserIsAdmin)
  • client - Ensures user is a client (EnsureUserIsClient)
  • active - Ensures user account is active (EnsureUserIsActive)
  • SetLocale - Sets language from session/user preference

Authentication (Fortify)

All auth is handled via Fortify with custom Volt views. Features enabled:

  • Email verification, Password reset, Profile/password updates
  • Two-factor authentication (QR codes + recovery codes) is disabled in production — commented out in config/fortify.php at client request (2026-01-17). routes/web.php only registers settings/two-factor when Features::canManageTwoFactorAuthentication() is true, so the code path still exists but is currently dormant. Don't re-enable without confirming with Naser first.
  • Note: Public registration is disabled; admin creates all client accounts

Volt Component Pattern

This project uses class-based Volt components:

<?php
use Livewire\Volt\Component;

new class extends Component {
    public string $name = '';

    public function mount(): void
    {
        $this->name = Auth::user()->name;
    }

    public function save(): void
    {
        // ...
    }
}; ?>

<div>
    <!-- Blade template -->
</div>

Flux UI Components

Use Flux components when available (free edition): avatar, badge, brand, breadcrumbs, button, callout, checkbox, dropdown, field, heading, icon, input, modal, navbar, otp-input, profile, radio, select, separator, skeleton, switch, text, textarea, tooltip

Example: <flux:button variant="primary">Save</flux:button>

Testing Pattern

Tests use Pest with Volt::test() for Livewire components:

use Livewire\Volt\Volt;

test('profile can be updated', function () {
    $user = User::factory()->create();

    Volt::test('settings.profile')
        ->actingAs($user)
        ->set('name', 'New Name')
        ->call('updateProfileInformation')
        ->assertHasNoErrors();
});

User Factory States

When testing, use these factory states:

  • ->admin() - Creates admin user
  • ->individual() - Creates individual client
  • ->company() - Creates company client
  • ->client() - Randomly creates individual or company
  • ->deactivated() - Creates deactivated user
  • ->unverified() - Creates user without email verification
  • ->withTwoFactor() / ->withoutTwoFactor() - 2FA states

Notifications

Located in app/Notifications/:

  • Booking: BookingApproved, BookingRejected
  • Consultation: ConsultationCancelled, ConsultationRescheduled, ConsultationReminder24h, ConsultationReminder2h
  • Account: WelcomeAccountNotification, PasswordResetByAdminNotification, AccountReactivatedNotification, AccountTypeChangedNotification
  • Timeline: TimelineUpdateNotification
  • Guest: GuestBookingSubmitted, GuestBookingApproved, GuestBookingRejected

Business Rules

  • No self-registration: Admin creates all client accounts
  • Single admin: One lawyer manages the platform
  • No online payments: All payments handled offline
  • Cascade deletes: User deletion cascades to consultations, timelines, notifications
  • Bilingual required: All user-facing text needs Arabic and English

Public-site redesign (PRD 1.4, tasks 24-x)

The public pages (home, about, posts, booking, legal) and the auth pages' type/controls follow docs/ux-design.md Part A; the map is docs/architecture.md §6.2. Boundaries that keep the dashboards safe:

  • Redesign styles live in resources/css/public.css, every selector under body.site (auth: body.site-auth); its script is resources/js/public.js + resources/js/site/*, initialised on livewire:navigated. Both load only from the public (and, CSS only, auth) layouts.
  • Never edit resources/css/app.css @theme/.btn-*, components/language-toggle.blade.php, or lang/*/navigation.php for the redesign — the dashboards share them. New public copy goes in lang/{ar,en}/site.php.
  • availability-calendar defaults to variant="month" (the client dashboard uses it); the home page uses variant="strip". pages.booking takes compact for the home card; booking rules are shared and unchanged.

Design Requirements (from PRD)

Below: the dashboards' palette and rules. Public pages: Part A of docs/ux-design.md.

  • Color scheme:
    • Dark Forest Green (#2D3624) - Header/Footer backgrounds
    • Warm Gold (#A68966) - CTA buttons, links, accents
    • Warm Cream (#F4F1EA) - Main page background
    • Forest Green (#2D322A) - Headings, body text
    • Pure White (#FFFFFF) - Card backgrounds
    • Olive Green (#4A5D23) - Active states
  • Bilingual: Arabic (primary, RTL) / English (LTR)
  • Professional, elegant aesthetic with botanical/wheat Libra logo
  • See docs/brand.md for complete brand guidelines