# 01 · Development Setup

Getting Rusukh running on a developer machine, from an empty folder to a seeded application you
can sign into. Allow about 30 minutes the first time, most of it waiting for `composer install`.

---

## 1. What you need installed

| Requirement | Version | Notes |
|---|---|---|
| **PHP** | **8.2.x** | The project is pinned to run *as* PHP 8.2. It will install under 8.3, but 8.2 is what every test result and every deployment target assumes. |
| **PostgreSQL** | 16 or newer (18 recommended) | Not MySQL. The schema uses native enum types, materialised views, partial indexes and database-level check constraints that have no MySQL equivalent. |
| **Redis** | 6 or newer | Cache, sessions and queues. |
| **Node.js** | 18 or newer | Front-end build only; not needed at runtime. |
| **Composer** | 2.x | |

### Required PHP extensions

```
pdo_pgsql  pgsql  mbstring  openssl  fileinfo  gd  intl  zip  bcmath
```

Verify with `php -m`. `gd` (or `imagick`) is genuinely required — the system re-encodes uploaded
images to strip EXIF data, because delivery proof photographs would otherwise carry the rider's
GPS coordinates.

> **On Windows:** `ext-pcntl` does not exist, so Laravel Horizon cannot run. This is expected and
> accounted for — the project ships its own queue-monitor screen at
> `/admin/core/queue-monitor` that replaces it.

---

## 2. Database

Create the two databases. The test database is separate and is wiped constantly — never point it
at anything you care about.

```sql
CREATE DATABASE rusukh;
CREATE DATABASE rusukh_testing;
```

That is all you need to do by hand. The three Postgres extensions the schema requires are installed
by the first migration (`2024_01_01_000000_create_required_extensions`):

| Extension    | What depends on it                                                        |
|--------------|---------------------------------------------------------------------------|
| `citext`     | Case-insensitive `users.email` / `customers.email` by column type          |
| `pg_trgm`    | 14 trigram indexes behind fuzzy name, phone and order-number search        |
| `btree_gist` | The `appointments` constraint that makes double-booking a tailor impossible |

All three are *trusted* extensions on PostgreSQL 13 and above, so the database owner can install
them without superuser rights. If you are on a managed host that withholds even trusted extensions,
run those three `CREATE EXTENSION IF NOT EXISTS` statements as an administrator before migrating —
that is the only case where this is a manual step.

> **Not** required: `pgcrypto` and `unaccent`. Earlier revisions of this guide listed both, and
> attributed CNIC and bank-detail encryption to `pgcrypto`. That was wrong and worth correcting
> plainly: those fields use Laravel's `encrypted` cast, so the ciphertext is produced and read by
> the **application** using `APP_KEY`, and the database only ever stores an opaque string. The
> practical consequence is that **losing `APP_KEY` loses the plaintext** — no database-side
> recovery exists. Back it up as carefully as you back up the data.

---

## 3. Install and configure

```bash
composer install
npm install

cp .env.example .env
php artisan key:generate
```

Now open `.env` and set at minimum:

```ini
DB_DATABASE=rusukh
DB_USERNAME=postgres
DB_PASSWORD=your_password_here

REDIS_HOST=127.0.0.1
```

Everything else in `.env.example` has a working default for local development. Two worth knowing:

- `MAIL_MAILER=log` — emails are written to `storage/logs/laravel.log` rather than sent. To see
  them rendered properly, run Mailpit or Mailhog on port 1025 and leave `MAIL_HOST=127.0.0.1`.
- `PAYFAST_MODE=fake` — the payment gateway is simulated. No real money moves, no credentials
  needed, and the fake driver deliberately exercises both success and failure paths.

---

## 4. Build, link, seed

```bash
# Front-end assets. NOT optional — there is no CDN fallback anywhere in this
# application, so skipping this leaves every page unstyled.
npm run build

# Make uploaded files (delivery photos, deposit slips, bundle photographs)
# reachable from the browser.
php artisan storage:link

# Schema, then the six-month demo dataset.
php artisan migrate --seed
```

The seed builds a realistic working business: roughly 120 orders spanning every order status
including the failure paths, a full staff roster across all 12 roles, customers with measurement
histories, stock, production jobs, payments, ledger entries and matured commissions. It takes a
couple of minutes.

---

## 5. Run it

Three processes. The first is obvious; the second is the one people forget.

```bash
# 1. The application.
php artisan serve

# 2. The queue worker. Every email, report and commission maturation runs
#    through here. Without it the app looks fine and silently stops
#    communicating with customers.
php artisan queue:work redis --tries=1 --timeout=60

# 3. The scheduler, for nightly jobs. Optional on a laptop; required on a
#    server (see 03-PRODUCTION-DEPLOYMENT.md).
php artisan schedule:work
```

Open **http://127.0.0.1:8000**.

### Signing in

Full credential tables are in [`../RUNBOOK.md`](../RUNBOOK.md) §1. To get moving:

| Role | Email | Password |
|---|---|---|
| Super root (everything) | `root@rusukh.pk` | `Rusukh#Staff2026` |
| A customer | `ahmed.raza@gmail.com` | `Rusukh#Client2026` |

Staff sign in at `/login`; customers sign in at `/customer/login`. They are separate
authentication guards — a staff account cannot sign into the customer portal and vice versa.

> **2FA:** super-root, management and finance accounts are required to enrol a code generator
> before they can reach their screens (`TWO_FACTOR_REQUIRED_ROLES` in `.env`). On a development
> machine you can widen or empty that list to skip enrolment.

---

## 6. Day-to-day commands

```bash
# After changing a config file, a route, or anything in .env
php artisan optimize:clear

# Front-end, with hot reload
npm run dev

# Re-seed from scratch. DESTRUCTIVE: drops every table.
php artisan migrate:fresh --seed

# Code style and static analysis
./vendor/bin/pint            # fix
./vendor/bin/pint --test     # check only
./vendor/bin/phpstan analyse

# Tests. The browser suite needs a build first — Tailwind only emits the
# classes it finds in the templates, so a stale stylesheet gives you unstyled
# markup and overflow failures that are nothing to do with your change.
php artisan test --parallel --exclude-group=concurrency
php artisan test --group=concurrency
npm run build && npx playwright test --workers=1

# Regenerate the illustrated client handbook after a UI change.
# Runs its own server on 8001 against rusukh_demo; never touches port 8000.
npx playwright test -c e2e/evidence/evidence.config.ts
DB_DATABASE=rusukh_demo php artisan handbook:export-reference
node e2e/evidence/build-handbook.mjs
```

---

## 7. When something is wrong

| Symptom | Cause and fix |
|---|---|
| Every page loads unstyled | `npm run build` was not run, or `public/build` is missing |
| `Vite manifest not found` | Same. Note `vite.config.js` must keep `manifest: 'manifest.json'` — Vite 5's default path is incompatible with Laravel 10 |
| `relation "..." does not exist` | Migrations have not run, or you are pointed at the test database |
| `could not find driver` | `pdo_pgsql` is not enabled in `php.ini` |
| Migration fails on `gist`/`trgm`/`citext` | A required PostgreSQL extension is missing — see §2 |
| Emails never arrive | The queue worker is not running |
| Uploaded images 404 | `php artisan storage:link` was not run |
| A settings change appears to do nothing | Config is cached — `php artisan optimize:clear` |
| Signing in as root/management/finance always lands on the two-factor screen | Working as designed — `EnsureTwoFactor` guards *every* `/admin` request for those three roles, not just the first. Enrol an authenticator, or use one of the other eleven roles |
| A browser test fails as one of those three roles | The suite has to pass a real TOTP challenge. `php artisan identity:seed-browser-two-factor` enrols them; `e2e/auth.setup.ts` calls it automatically, so this usually means that step did not run |
| A browser test passes but the screenshot shows the wrong page | A redirect. `page.goto()` follows it and reports the final 200 — assert the landing path. See [`04-TESTING-AND-VERIFICATION.md`](04-TESTING-AND-VERIFICATION.md) §3 |

More, including production-grade troubleshooting, in [`../RUNBOOK.md`](../RUNBOOK.md) §12.
