# 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: ```bash php84 artisan migrate php84 artisan config:cache php84 artisan optimize:clear ``` ### Deployment Checklist ```bash # 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 ```bash # 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--*.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 name = Auth::user()->name; } public function save(): void { // ... } }; ?>
``` ### 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: `Save` ### Testing Pattern Tests use Pest with `Volt::test()` for Livewire components: ```php 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