# NREMS Laravel Boilerplate — Design Spec

**Date:** 2026-08-23 · **Status:** Approved by user (design round) · **Author:** Fable (architect/reviewer), implementation by Opus subagents

## 1. Purpose

Client-approved NREMS (Navratna Real Estate Management System) moves to development. This spec defines the **reusable Laravel boilerplate** that every future NREMS module — and future client projects — will be built on. Scope of this round: project skeleton + **Auth module** (login / logout / forgot / reset) + **first business module: Projects & Units**, proving the full reusable CRUD pattern end-to-end with zero known bugs.

Canonical UI reference: https://nrems-wireframes.virtualcarry.com/v12/ and the local Theme A static pages at `/home/vivek/nrems/nrems-themes/theme-a-wireframe/` (Metronic bundles + wireframe identity: primary `#2563EB`, Inter font). **Theme A HTML is the source of truth for markup/classes.**

## 2. Goals / Non-goals

**Goals**

- Production-grade, human-readable, senior-dev-friendly code; every pattern copy-paste replicable for the next module.
- Security-first: no known OWASP top-10 gaps; all auth flows on Laravel-maintained code (Fortify), not hand-rolled.
- High performance: server-side DataTables, eager loading, indexed queries, no N+1.
- Fully responsive UI including tab navs (user flagged Theme A tab bar overflow on small screens). The fix is a **global rule in `theme-a.css`/the layout component** (horizontal-scroll tab navs with no clipped items), verified this round on a demo tabbed section and inherited by all future modules — no in-scope page currently uses `nav-tabs`.
- Two comparable repos differing ONLY in the form-building layer.

**Non-goals (this round)**

- Inquiry, Booking, Cancellation, Possession, Maintenance, Documents modules (structure leaves room; not built).
- Redis, queues, 2FA, multi-tenancy, bulk unit generator, unit hold auto-release cron. Schema anticipates them; no code.
- Windows/XAMPP serving (XAMPP PHP is 7.4). Dev runs entirely in WSL.

## 3. Environments & Repos

- **Location:** WSL. Primary repo `/home/vivek/nrems-app` (forms = Blade component library). Variant repo `/home/vivek/nrems-app-html` (forms = `spatie/laravel-html`), produced by cloning repo 1 after acceptance and swapping only the form layer — the internals of the `x-form.*` Blade components are rebuilt on spatie/laravel-html while keeping the identical component API; controllers/models/views/tests unchanged except benign form-markup assertions.
- **Runtime:** PHP 8.3 (WSL), Composer 2.8, MySQL 8 (WSL service; DB `nrems_app`, dedicated user), `php artisan serve` for dev. Windows access via `\\wsl$`.
- **Framework:** Laravel 13 (current stable; matches `yajra/laravel-datatables ^13`, PHP ≥ 8.3).

## 4. Packages

| Package | Why |
|---|---|
| laravel/fortify | Headless auth backend: login throttle, password reset, hashing — maintained security core |
| spatie/laravel-permission | Roles/permissions; powers future Role Management module |
| spatie/laravel-activitylog | Audit trail on all models; powers the "History" tab pattern |
| yajra/laravel-datatables ^13 | Server-side listing for all index pages |
| proengsoft/laravel-jsvalidation | FormRequest rules auto-rendered as client-side validation |
| spatie/laravel-html (repo 2 only) | Officially suggested laravelcollective/html replacement |
| Dev: laravel/pint, larastan/larastan (level ≥ 6), pestphp/pest | Style, static analysis, tests — all must pass green before any task is complete |

`laravelcollective/html` is **excluded**: abandoned, supports ≤ Laravel 10 (verified on Packagist 2026-08-23).

## 5. Architecture

Standard Laravel skeleton (no module package system — plain, readable structure):

```
app/
  Http/Controllers/           # thin: validate (FormRequest) → call service/model → respond
  Http/Requests/<Module>/     # StoreProjectRequest, UpdateProjectRequest, ...
  DataTables/                 # yajra DataTable classes, one per index screen
  Models/                     # SoftDeletes + LogsActivity via shared trait/base
  Models/Concerns/            # HasAudit trait (activitylog defaults), etc.
  Policies/                   # one per model, mapped to spatie permissions
  Services/<Module>/          # only where logic exceeds controller triviality (e.g. UnitPriceCalculator)
  Support/                    # small shared helpers (Breadcrumbs, Flash)
resources/views/
  layouts/app.blade.php       # Theme A shell (sidebar, topbar, footer)
  components/                 # x-form.*, x-card, x-modal, x-datatable, x-breadcrumbs, x-status-badge
  auth/                       # login, forgot, reset (Theme A styled)
  projects/, units/           # module views mirroring wireframe pages
public/assets/                # Theme A assets copied verbatim (plugins.bundle, style.bundle, theme-a.css)
config/nrems.php              # app-level constants (statuses, formats)
docs/adding-a-module.md       # the replication guide (deliverable)
```

**Conventions (binding):**

- Permissions named `<module>.<action>`. This round's full matrix: `projects.view|create|edit|delete`, `units.view|create|edit|delete`, `unit_types.view|create|edit|delete`. **Wings and floor plans are sub-resources of a project and are governed by `projects.edit`** (their policies delegate to the parent project's permission) — they get no permission rows of their own. Policies check these; sidebar items and buttons hidden without permission; routes also protected by `can:` middleware (defense in depth).
- Every model: `SoftDeletes`, `LogsActivity` (log fillable, log only dirty), `$fillable` explicit, casts declared. No mass-assignment holes.
- Every list page: DataTable class with server-side search/order/paginate, indexed columns, explicit `select()` + eager loads.
- Every form: FormRequest (authorize via policy) + jsvalidation rendering of the same request; select2 on all FK dropdowns; old-input restore; errors inline per Theme A markup.
- All user-visible strings via `__()` lang files (future i18n-ready, single `en` locale now).
- Money: store paise-free `DECIMAL(14,2)`; areas `DECIMAL(10,2)`; display via a `format_inr()` helper (Indian digit grouping).
- Dates: store `DATE`/`DATETIME` UTC; display `d-m-Y` per wireframe.

## 6. Auth module

Fortify (views enabled, features: reset passwords; registration OFF, email verification OFF for now, 2FA OFF).

- **Pages:** login, forgot-password, reset-password. Theme A has **no** auth HTML — these pages adapt Metronic's stock auth layout restyled to Theme A identity (`#2563EB`, Inter, `theme-a.css`); jsvalidation active, generic failure messages (no user enumeration), rate-limit 5/min per email+IP (Fortify default throttle), password rule: min 8 with mixed requirements via `Password::defaults()` set in AppServiceProvider.
- **Sessions:** database driver, regenerate on login, invalidate + regenerate CSRF on logout. `SESSION_SECURE_COOKIE` honored, http-only, same-site lax.
- **Mail:** password reset via configured mailer; dev uses `log` mailer; `.env.example` documents SMTP keys.
- **Seeds:** roles `Super Admin`, `Admin`, `Sales`, `Operations` (Super Admin bypass via `Gate::before`), the full permission matrix from §5 (`projects`/`units`/`unit_types`), one Super Admin user with env-provided password (never hard-coded; seeder reads `SEED_ADMIN_PASSWORD`, fails loudly if missing in production).
- **Activity log:** login/logout/failed-login events recorded (listener on Fortify events).

## 7. Projects & Units module

**Schema — the approved 5-table inventory design** (see `/home/vivek/nrems/docs/NREMS-Inventory-Architecture-v1.0.pdf`):

`projects → project_wings → floor_plans → units`, plus `unit_types` templates. All five tables migrated with FKs + indexes exactly per that design (statuses: units.status = `available|held|booked|registered|possessed|blocked|management_quota`, single source of truth; denormalized `units.project_id`; `held_until` column present, auto-release cron deferred). Amenities stored as JSON array on `projects` (checkbox set from wireframe).

**UI in scope (per wireframe pages):**

- `projects` index = `project-management.html`: DataTable with the columns exactly as ordered in the HTML (Project ID, Project Name, Type, Location, Developer, Towers, Floors, Total Units, RERA No., Status, Actions — the HTML governs where this spec and HTML differ on presentation).
- `add-project.html` / `edit-project.html`: sections Basic (name, type, code, launch/possession dates, status), Location (address, city, state, pincode, lat/lng), Structure (towers, floors, total units, plot/built-up area, open space %, parking, configurations), Amenities checkboxes.
- `view-project.html`: project summary + **wings and units grid** with status badges.
- Wings & floor plans: managed inside project view (simple CRUD modals; markup extrapolated from Theme A's modal pattern in `inquiry.html` — the only modal example in the theme).
- Units index = `unit-inventory.html`: standalone units DataTable page (all projects; filters by project/status/type per the HTML, plus a wing filter as a deliberate addition; status badges) — in scope, follows the same DataTable conventions. The HTML's "Sold" badge has no matching status in the canonical enum: badge labels derive strictly from the enum, and seed rows use `booked`/`registered` where the wireframe showed "Sold".
- `view-unit.html`: read-only unit detail page — **in scope** (completes the units view set implied by §5). `unit_types/` views join `projects/`, `units/` in the §5 tree.
- `edit-unit.html` (+ add): cascading selects Project → Wing → Floor, unit number, unit type/configuration, areas (carpet/built-up/super/balcony/terrace, loading %), pricing (base rate, floor rise, corner %, facing %, discount %, **auto-calculated total** — computed by `UnitPriceCalculator` service server-side; JS mirrors it live client-side; server value is authoritative), facing, status.
- Unit types: minimal CRUD (settings-style page) so units can reference templates.
- Client constraint (Mar 25 revision): seed exactly the 2 real projects — **Navratna Gulmohar Park (PRJ-009)** and **Kalhaar Sky Residency (PRJ-010)** — with wings/floors/units/dummy rows taken from the Theme A/wireframe HTML (`project-management.html` rows and related pages), so the app boots looking like the client-approved wireframe. Where the HTML dummy rows carry different project codes (PRJ-001/002), **this spec's codes (PRJ-009/010, per the Mar 25 client revision) win**.

**Unit price formula (authoritative default — confirm with client before production; implemented in exactly one place, `UnitPriceCalculator`):**

```
area           = super_built_up_area (sq ft)
effective_rate = base_rate + floor_rise            (both ₹/sq ft, per the form fields)
base_amount    = effective_rate × area
premiums       = base_amount × (corner_pct + facing_pct) / 100   (additive, non-compounding)
gross          = base_amount + premiums
total_price    = round( gross × (1 − discount_pct / 100), 0 )    (nearest rupee)
```

All percentages accept 0–100 with 2 decimals; negative inputs rejected by validation. Unit tests cover every term in isolation and combined, plus zero-area and 100%-discount edges.

**Dashboard (this round):** static Theme A `index.html` shell wired into the Blade layout with placeholder stats — real widgets are deferred to later modules. It exists so login lands somewhere real and the layout/sidebar/permission-hiding are provable.

**Derived counts** (towers/units on project list) come from `withCount()`/subselects — never loops.

## 8. Security requirements (binding)

CSRF on all forms; policies + `can:` middleware on every route; FormRequest validation on every write; no raw SQL (query builder/Eloquent only; any `whereRaw` requires bindings + review); XSS-safe Blade (`{{ }}` only; any `{!! !!}` requires reviewer sign-off); signed/throttled reset links (Fortify); `APP_DEBUG=false` guidance in README; `.env` never committed; file permissions sane; security headers middleware (X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy) — CSP deferred (Metronic inline scripts). DataTables: whitelist orderable/searchable columns (yajra defaults + explicit column config) to block SQL-injection-via-order. Custom **403/404/500 error pages** rendered in a minimal Theme A-styled standalone layout (no sidebar/session dependency, so 500s render even when app state is broken).

## 9. Testing & QA gates

- **Pest feature tests:** all auth flows (success, failure, throttle, reset token expiry/invalid), every module route (index/create/store/edit/update/delete) for authorized, unauthorized (403), and guest (redirect); validation error paths; DataTable JSON endpoint (search/order); UnitPriceCalculator unit tests (each premium/discount permutation); cascading-dropdown AJAX endpoints; policy matrix per role.
- **Static:** Larastan level ≥ 6 clean; Pint clean. CI-ready composer scripts: `composer test`, `composer analyse`, `composer lint`.
- **Definition of done per task:** tests green + analyse + lint + Fable code review pass. Verified evidence (command output) required before completion claims.
- **Manual gate:** responsive smoke on 390px/768px/1280px widths for every page (headless Chrome screenshots), including the flagged tab-nav overflow fix.

## 10. Delivery plan (high level)

1. Skeleton: Laravel 13 project, packages, Theme A asset integration, layout components, auth module, seeds. — repo 1
2. Blade form component library + docs.
3. Projects & Units module (schema → CRUD → DataTables → tests).
4. `docs/adding-a-module.md` + README (setup, conventions, deploy notes).
5. Clone → repo 2 (`nrems-app-html`), swap form layer to spatie/laravel-html, re-run full test suite.

Each step implemented by Opus subagents against task specs written by Fable; Fable reviews every diff before it lands. Detailed task breakdown lives in the implementation plan (separate doc, written after this spec is approved).

## 11. Acceptance criteria

- `composer test` / `analyse` / `lint` all green in both repos.
- Login → dashboard → Projects list → create/edit/view project → manage wings/units → edit unit with live price calc — all functional against seeded data, matching Theme A visuals, responsive at the three checkpoints.
- Forgot/reset password works end-to-end via log mailer.
- A permission-stripped user sees no Projects sidebar entry and gets 403 on direct URL.
- Repo 2 renders identical pages via spatie/laravel-html with the same test suite green.
