Files
libra/CLAUDE.md
T
Naser Mansour 1d549ac0e6 Update CLAUDE.md: correct production server, mail host, deploy checklist
Server IP was stale (pre-migration host, 69.62.106.94) - production has
been on h1 (163.172.67.77) for a while. Documents the MAIL_HOST incident,
the first-time git linking done today, and that PHP-FPM + queue worker
restarts are needed on every deploy, not just composer/config changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 00:51:56 +03:00

260 lines
11 KiB
Markdown

# 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 system reference (colors, typography, layout, components — retrospective, written 2026-09-27)
- `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
<?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:
```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
## Design Requirements (from PRD)
- **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