# AGENTS.md — maxtuero_backend

## Quick start

```bash
cp .env.example .env
composer install
npm install
php artisan key:generate
php artisan migrate
npm run build
```

## Dev server

```bash
composer dev
# Runs concurrently: php artisan serve + queue:listen + pail (log viewer) + npm run dev
```

## Testing

```bash
php artisan test
# Single test: php artisan test --filter=TestName
# Tests use SQLite in-memory by default (phpunit.xml: DB_CONNECTION is commented out — uncomment to enable)
```

## Project structure

- **Real estate marketplace**: agents, properties (multi-step creation), raffles/tickets, chat, wishlists
- **Three user roles**: `none` / `stuff` / `manager` + `admin` (stored as enum `StoreRoleEnum`)
- **Two auth systems**: web (session, Laravel Breeze) and API (JWT via `tymon/jwt-auth`)
- **Route organization**: feature routes are split across `routes/rony/`, `routes/backend.php`, `routes/api.php`, `routes/web.php` — always check all route files for the full picture

## Key middleware

| Middleware | Purpose |
|---|---|
| `auth:api` | JWT-required guard |
| `jwt.setter` (JwtMiddleware) | Non-blocking — sets user if token valid, never returns 401 |
| `AdminMiddleware` | Accepts web session OR JWT |
| `check.role:agent` | Agent-only routes |

## Notable conventions

- Property media — `PropertyMediaServiceInterface` bound in `AppServiceProvider`; swaps between `LocalPropertyMediaService` and `S3PropertyMediaService` based on `filesystems.default`
- Property filter cache uses `properties_filter_version` key to bust caches on save/delete
- API responses use `ApiResponse` trait (standardized format)
- OTP auth helper in `OtpAuthHelperTrait`
- Video uploads use chunked upload (`uploadVideoChunk`) + replace (`replaceVid`)
- Stripe Connect redirect configured via `STRIPE_CONNECT_REDIRECT_URL` and `FRONTEND_URL` env vars (defaults to Vercel frontend)
- PDF generation: dompdf for QR codes (`QrPdfService`) and tickets (`TicketPdfService`)
- Broadcasting: Laravel Reverb (default) or Pusher — config in `config/reverb.php`
- Default queue driver: `database` (SQLite in dev)
- Dev commands: `php artisan pail` for log tailing, `php artisan queue:listen --tries=1`

## Env quirks

- `RELAXED_ACCESS=true`, `VERBOSE_EXCEPTIONS=true` used throughout for dev
- `STRIPE_KEY` must be set (even to dummy value) for boot
- `JWT_SECRET` required — generate with `php artisan jwt:secret`

## Site Representative module — implementation plan

A platform representative (`role = 'representative'`) who onboards affiliate stores — creates stores, assigns store managers, sets up Stripe Connect, and downloads QR codes. This is a **top-level role**, separate from `store_stuff` / `manager`.

### ✅ Completed: Initial setup (Steps 1–5)

| Area | Files | Status |
|---|---|---|
| **Enum** | `app/Enums/StoreRoleEnum.php` — added `REPRESENTATIVE` case | ✅ |
| **Migration (users)** | `database/migrations/0001_01_01_000000_create_users_table.php` — added `'representative'` to role enum | ✅ |
| **Migration (stores)** | `database/migrations/2026_04_18_222302_create_stores_table.php` — added nullable `created_by` FK → `users.id` | ✅ |
| **Middleware** | `app/Http/Middleware/RepresentativeMiddleware.php` — checks `$user->role === 'representative'` | ✅ |
| **Middleware alias** | `bootstrap/app.php` — registered `'representative'` | ✅ |
| **Login redirect** | `AuthenticatedSessionController@store` — routes reps to `portal-representative.dashboard` | ✅ |
| **RedirectIfAuthenticated** | `AppServiceProvider` — redirects reps to `portal-representative.dashboard` | ✅ |
| **AdminMiddleware** | Redirects reps to `portal-representative.dashboard` instead of 403 | ✅ |
| **Routes** | `routes/store-only/representative-web.php` — prefix `portal-representative/`, middleware `['auth', 'representative']`, name `portal-representative.` | ✅ |
| **Route registration** | `routes/web.php` — requires the new route file | ✅ |
| **Controller** | `app/Http/Controllers/Web/Representative/HomeController.php` — dashboard | ✅ |
| **Layout** | `resources/views/backend/representative/layouts/representative-app.blade.php` | ✅ |
| **Sidebar** | `resources/views/backend/representative/partials/sidebar.blade.php` | ✅ |
| **Dashboard view** | `resources/views/backend/representative/dashboard/index.blade.php` | ✅ |

### ✅ Completed: Remaining features

| Area | Files | Status |
|---|---|---|
| **Store CRUD** | `RepresentativeStoreController` + `store/index`, `create`, `edit` views | ✅ |
| **Manager creation** | `RepresentativeManagerController` + `managers/index`, `create` views | ✅ |
| **Stripe Connect** | `RepresentativeStripeConnectController` + `stripe-connect/index` view (store selector + per-store connect) | ✅ |
| **Profile settings** | `HomeController@edit`/`update` + `profile/edit` view | ✅ |
| **Relationship** | `Store.php` — added `createdBy()` relationship | ✅ |

## Install prerequisites

- PHP 8.2+, Node 25 (.nvmrc), Composer
- No Docker — use `php artisan serve` or Laravel Herd/Valet
