{{-- NREMS Sidebar Partial — wireframe v12 markup (CR-2, spec §12). Structure is docs/wireframes-v12/index.html's own: a .sidebar-brand block, then a flat .sidebar-nav of .nav-section headings and .nav-item anchors. There is no nesting, no accordion and no Metronic .menu vocabulary; an active entry is `.nav-item.active` and nothing else. Icons are the wireframe's, entry for entry, with one substitution: v12 draws Dashboard with `fa-grid-2`, which is a FontAwesome *Pro* glyph. We ship the 6.6 Free build (plugins.bundle.css), where that class resolves to nothing — and so does the wireframe page itself, which links the Free CDN. `fa-gauge-high` is carried over from the previous sidebar as the free stand-in. ------------------------------------------------------------------ Everything below this line is unchanged from the CR-1 state. CR-2 is a re-skin: the class names moved, the information architecture did not. ------------------------------------------------------------------ Sections and items mirror wireframe v12 (docs/wireframes-v12/, 6 sections), which is the canonical pack: it dropped the v10 "Resale / Transfer" entry. Every entry here now has a v12 counterpart. Two did not, and both are gone. A Unit Inventory entry was added because unit-inventory.html was a screen v12's menu gave no way into, and the client withdrew it (CR-1, spec §11); a Unit Types entry stood under Administration on the same reasoning until CR-5 §15.1 removed the module outright. The lesson those two leave behind: a built screen is not by itself a reason for an entry — if v12 gives no way into yours, put it on the confirmation list rather than assuming. Dashboard and Projects & Units are wired to routes today. Every other entry renders inert (href="#", disabled styling) so the information architecture is visible while the modules are built. Each task that lands a module swaps its entry for a real route() + request()->routeIs() active state. Two distinct gates, never nested: * Permission-gated entries (@canany / @can) are visible to ANY user who holds the permission — Super Admin has no special standing here. Read that narrowly: it means the *sidebar* adds no bypass of its own. A Super Admin still passes every @can, because AppServiceProvider::defineSuperAdmin() short-circuits Gate::before for the role, so these entries render for them without their holding a single permission. * Preview-only entries — the ones whose modules do not exist yet (Possession, Maintenance and Legal Files & Agreements) — are wrapped in $previewMenu so ordinary users never see dead ends. As each module lands, its entry moves out of $previewMenu into a permission gate; Bookings made that move in CR-8, Cancellation in CR-9, Broker Management in CR-12, User Management in CR-13, Role Management in CR-14 and **Customer Documents in CR-15** — so Sales, Administration and now Document Uploads each have at most one preview entry left, and the THREE that remain are Operations' two and Document Uploads' one. CR-14 §3.1 asked whether $previewMenu itself could go once Role Management landed, it being the last "coming soon" entry under Administration. It cannot, and the three entries above are why: Possession, Maintenance and Legal Files & Agreements are all still preview-only, all still Super-Admin-visible, and all still use `nav.coming_soon`. The machinery stays until the last of THOSE lands. A section *heading* renders when either gate would show something under it: Inventory keys off @can alone, and Sales and Administration each key off a flat @if over the preview flag and their live entries' permissions. --}} @php // The wireframe has no inert entry, so `.is-disabled` is ours; it is styled // in the app-additions tail of nrems-wireframe.css. It stays on .nav-item so // a preview entry is still an entry, in the sidebar's own vocabulary. $inert = 'nav-item is-disabled'; $previewMenu = auth()->user()?->hasRole('Super Admin') ?? false; // The Inventory section's one live entry. It used to cover two route // namespaces — `units.*` was in this list because the unit screens had no // entry of their own and belonged under Projects & Units, which is what // v12 does (new-unit.html and view-unit.html both mark that entry active). // CR-5 §15.1 removed those screens, so `projects.*` is again the whole of // it. Resolved here rather than inline so the anchor below keeps its // one-space class attribute. $inInventory = request()->routeIs('projects.*'); // The Administration section's two live entries (CR-7 §4.5). Same shape as // the Sales pair below: one pattern each, covering the module's whole route // surface because the snake_case naming convention of recipe §8 puts every // one of its routes under the same prefix. Both modules are four routes and // one screen, so the pattern is doing less work here than `inquiries.*` // does — but it is the same pattern, and a member route added later is // covered without anybody remembering to widen it. $inBookingTypes = request()->routeIs('booking_types.*'); $inCustomerDocTypes = request()->routeIs('customer_doc_types.*'); // The Administration section's third live entry (CR-12 §3). Same shape as // the pair above, and doing rather more work: `brokers.*` covers the four // resource screens, the three write routes and the document download // (`brokers.files.download`), because CR-12 named every one of them under // the one prefix. $inBrokers = request()->routeIs('brokers.*'); // The Administration section's fourth live entry, and its first item // (CR-13 §3). `users.*` covers the seven resource screens plus the status // toggle (`users.status`), because CR-13 named every one of them under the // one prefix — the snake_case convention of recipe §8 doing the same work // it does for `brokers.*` above. $inUsers = request()->routeIs('users.*'); // The Administration section's fifth live entry, and the last "coming soon" // one in the whole sidebar to go live (CR-14 §3.1). `roles.*` covers the six // resource screens the module registers — there is no `show` (decision D10), // and no member route — because the snake_case convention of recipe §8 puts // every one of them under the one prefix. $inRoles = request()->routeIs('roles.*'); // The Sales section's two live entries (CR-6 §6, CR-8 §6). `inquiries.*` // covers the resource's four screens AND the module's five member routes, // because their names are `inquiries.follow_up`, `inquiries.hold` and so on // — the snake_case naming convention of recipe §8 is what makes one pattern // enough. The cascade endpoint shares the prefix too (`inquiries.unit_options`) // and never renders a page, so it costs nothing to be inside it. // // `bookings.*` is the same pattern doing more work: it covers the six // resource screens, the cancel member route, the four document routes // (`bookings.documents.*`) and the cascade, because CR-8 named every one of // them under the one prefix. $inSales = request()->routeIs('inquiries.*'); $inBookings = request()->routeIs('bookings.*'); // The Operations section's one live entry (CR-9 §3.5). Same shape as the // pairs above: one pattern covering the module's whole route surface, // because every one of its six routes is named under the `cancellations.` // prefix. It deliberately does NOT overlap `bookings.*` — a cancellation is // a cancelled booking, but it is its own set of screens under its own // section, and two entries lighting up at once is the drift the "only one // active" rule exists to catch. $inCancellations = request()->routeIs('cancellations.*'); // The Document Uploads section's one live entry (CR-15 §4.4, decision D19). // Same shape as every pair above: one pattern covering the module's whole // route surface, because the snake_case convention of recipe §8 puts the // five screens and the two cascade endpoints under the one // `customer_documents.` prefix. // // It deliberately does NOT overlap `bookings.*`, and that is worth saying // out loud here because CR-15 decision D3 makes the two modules share a // permission scheme: the booking Documents tab is a booking screen, gated // by `customer_documents.*` and reached at `bookings.show`, so opening it // must light up Bookings and not this entry. Two entries active at once is // the drift the "only one active" rule exists to catch. $inCustomerDocuments = request()->routeIs('customer_documents.*'); @endphp