# Scout search (platform)

## Purpose

Async search boxes on invoice, quote, job, and provider payment forms use [Laravel Scout](https://laravel.com/docs/scout) instead of loading full `<select>` lists. Search is team-scoped and backed by Meilisearch in local/production; tests use the `collection` driver.

## Indexed models

| Index | Model | Domain | Filterable attributes |
|-------|-------|--------|------------------------|
| `customers` | `Customer` | Customers | `team_id` |
| `customer_service_sites` | `CustomerServiceSite` | Customers | `team_id`, `customer_id` |
| `service_jobs` | `Job` | Jobs | `team_id` |
| `job_appointments` | `JobAppointment` | Jobs | `team_id`, `job_id` |

Searchable concerns live under each domain’s `Concerns/` folder. Team scoping is enforced in `App\Support\Scout\TeamScopedSearch`.

## Web API

Authenticated team members only. All routes return `{ "results": [ { "id", "label", … } ] }`.

| Route name | URI | Extra params |
|------------|-----|--------------|
| `search.customers` | `GET search/customers` | `q` |
| `search.customer-service-sites` | `GET search/customer-service-sites` | `q`, `customer_id` |
| `search.jobs` | `GET search/jobs` | `q` |
| `search.job-appointments` | `GET search/job-appointments` | `q`, `job_id` |

## Frontend

`resources/js/Components/AsyncSearchSelect.vue` debounces requests (250 ms) and accepts `search-route`, `search-params`, and `initial-option` for edit forms.

Used on:

- Invoices create/edit — customer, job, appointment
- Quotes create/edit — customer, job, service site (create)
- Jobs create/edit — customer, service site
- Provider payments create — job, appointment

## Configuration

`.env`:

```env
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://meilisearch:7700
MEILISEARCH_KEY=
```

Sail includes Meilisearch. Filterable attributes are declared in `config/scout.php` under `meilisearch.index-settings`.

PHPUnit sets `SCOUT_DRIVER=collection` in `phpunit.xml`.

## Initial import

After enabling Meilisearch on an existing database:

```bash
php artisan scout:import "App\Domains\Customers\Models\Customer"
php artisan scout:import "App\Domains\Customers\Models\CustomerServiceSite"
php artisan scout:import "App\Domains\Jobs\Models\Job"
php artisan scout:import "App\Domains\Jobs\Models\JobAppointment"
```

New records sync automatically via Scout model observers.

## Tests

`tests/Feature/Support/ScoutSearchTest.php` — team scoping and job/customer filters.

## Boundaries

- **Owns:** Scout config, `TeamScopedSearch`, search HTTP endpoints, `AsyncSearchSelect`
- **Does not own:** CRM / job business logic (domains index their own models)
- **Depends on:** Customers and Jobs domains (indexed models and routes)
