# CLAUDE.md

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

## Repository layout

The repo root **is** the Laravel application root (this matches how it's actually deployed — see `plesk_deployment_guide.md`, which clones straight into the web root with document root `.../public`). There is no `app/` wrapper directory; `app/` here is Laravel's own `App\` namespace folder (Filament resources, models, services, etc.), same as any Laravel project.

- `app-modules/` — feature modules (nwidart/laravel-modules), each a self-contained mini-Laravel-app
- `dist/school-edition/` — **generated build artifact**, produced by `scripts/build-school-edition.sh`, and git-ignored. Never hand-edit files here; edit the corresponding source file and rebuild.
- `*.md` PRD/handbook files at the repo root (`TemseeEdu_Master_PRD.md`, `ONBOARDING_HANDBOOK.md`, `FILAMENT_PLUGIN_INTEGRATION.md`, `deployment_guide.md`, `plesk_deployment_guide.md`) — product/ops reference docs, not code.

## Commands

```bash
# Install
composer install
npm install

# Local dev (serves app, queue listener, log tailer, and Vite together)
composer run dev

# Tests (clears config cache first)
composer run test
# or a single test
php artisan test --filter=TestName
php artisan test tests/Feature/SomeTest.php

# Lint / format (Laravel Pint)
vendor/bin/pint
vendor/bin/pint --dirty

# Frontend build
npm run build
npm run dev
```

Tests run against a real MySQL database (`temseeedu_testing`, see `phpunit.xml` / `.env.testing`), not SQLite — a test DB must exist and be migrated.

The School Edition distributable is built with `scripts/build-school-edition.sh [version]`. Set `SKIP_ENCODING=1` for a local/demo QA build (plain rsync copy); without it, the script requires SourceGuardian's `sgenc` to encode the PHP for production release builds.

## Architecture

**TemseeEdu** is a multi-tenant school management SaaS (Filament 4 / Laravel 12) that also ships as a self-hosted **School Edition**. The active mode is read from `config/temseeedu.php`'s `edition` key (`cloud` vs `school_edition`) and `hosting_mode` (`standard` vs `cpanel`, which affects whether tenant databases are auto-created and how queues/cache run).

### Multi-tenancy (stancl/tenancy)

- Tenants are resolved by domain (`InitializeTenancyByDomain`). Central domains (`127.0.0.1`, `localhost` in dev) serve marketing/billing/registration; everything else is routed to a tenant's isolated database.
- `app/Providers/TenancyServiceProvider.php` wires tenancy events: on `TenantCreated` it runs `MigrateDatabase` → `SeedDatabase` → `CreateTenantStorageDirectories` → `MigrateTenantModules` (module migrations are handled separately from the core migration set). On `TenancyBootstrapped`/`RevertedToCentralContext` it swaps the Spatie permission cache key, the `public` filesystem disk URL, and the Filament theme-mode setting per tenant.
- `routes/web.php` = central-domain routes (public registration, Paystack checkout/webhooks, license server activation). `routes/tenant.php` = per-school routes, loaded only when a tenant is resolved.
- Tenant-facing panels are additionally gated by `App\Http\Middleware\VerifyLicense` (School Edition licensing) and `RequireTwoFactor`.

### Filament panels

Five panels, each its own `PanelProvider` under `app/Providers/Filament/`, registered in `bootstrap/providers.php`:

| Panel | Path | Guard | Notes |
|---|---|---|---|
| Admin | `/admin` | `admin` | Default panel; school back-office. Discovers resources/pages/widgets from both `app/Filament/**` and every `app-modules/*/app/Filament/**` |
| Temsee (Super) | `/temsee` | `admin` | Platform owner console — resources under `App\Filament\Super\*` |
| Teacher | | | `App\Filament\Teacher\*` |
| Parent | | | `App\Filament\Parent\*` |
| Student | | | `App\Filament\Student\*` |

Panels declare their tenancy middleware explicitly (`InitializeTenancyByDomain`, `PreventAccessFromCentralDomains`) alongside the standard Filament stack. See `FILAMENT_PLUGIN_INTEGRATION.md` for which third-party Filament plugins are approved/deferred and why (e.g. plugins requiring Filament 5, or anything that would expose server tooling inside a tenant panel, are deliberately excluded).

### Feature modules (`app-modules/`)

Modules (Attendance, Payments, Results, Exams, Library, Transport, Hostel, Inventory, Payroll, Accounting, Alumni — toggled in `modules_statuses.json`) are managed by `nwidart/laravel-modules` and merged into the root Composer autoload via `wikimedia/composer-merge-plugin` (`app-modules/*/composer.json`). Each module follows the standard Laravel-modules skeleton (`app/`, `config/`, `database/migrations`, `routes/`, `resources/views`, its own `tests/`). Filament UI for a module is discovered explicitly per-panel in the relevant `PanelProvider` (`->discoverResources(in: base_path('app-modules/X/app/Filament/Resources'), ...)`), so adding a new module resource/cluster requires that discovery call to exist in every panel it should appear in.

### Licensing / editions

`config/temseeedu.php` centralizes edition/licensing config: `cloud` edition relies on `stancl/tenancy` + subdomain routing with no external license check; `school_edition` is single-tenant and license-key verified (locally, via `license_signing_secret`, not a live license server — see the "Local License Controls" note in that file). Per-module plugin license keys (`plugins.*`) let a School Edition installation enable/disable individual modules.

### Other conventions worth knowing

- Rich text editing uses an in-house `App\Filament\Forms\Components\CmsRichEditor` (wraps Filament 4's native editor) instead of a third-party TinyEditor plugin.
- Audit logging and database backup/restore are in-house (`AuditLogResource`, `DatabaseBackup` page + `BackupManager` service) rather than third-party plugins, specifically to keep them tenant-isolated.
- SMS goes through Arkesel (Ghana); email supports SMTP or Resend/Amazon SES.
