Skip to content

feat(console): real-mode nav/route gating to backend-backed screens#69

Merged
hitakshiA merged 1 commit into
mainfrom
console/68-realmode-gating
Jul 11, 2026
Merged

feat(console): real-mode nav/route gating to backend-backed screens#69
hitakshiA merged 1 commit into
mainfrom
console/68-realmode-gating

Conversation

@hitakshiA

@hitakshiA hitakshiA commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Epic: #58 · Step B (real-mode nav/route gating) — the final step · Closes #68

Hand-coded (not via codex, per request). With the backend-backed features wired (auth #60, onboarding #63, treasury #65, payroll #67), this makes real mode honest: it exposes only what actually talks to the eERC backend, and keeps the full console in demo mode as the product vision.

What changed

  • app/nav.ts (new) — a single source of truth for the nav: each item carries a realBacked flag. visibleNavItems(demoMode) / visibleNavGroups(demoMode) filter to the wired screens in real mode; REAL_HOME = "/treasury".
  • app/Shell.tsx — the sidebar renders from visibleNavItems() (grouped), and the route table is gated on DEMO_MODE: demo keeps every route; real mode registers only /payroll + /treasury and redirects everything else to /treasury. Because the demo-only screens are referenced solely under the eliminated DEMO_MODE branch, a real build tree-shakes them out.
  • app/CommandBar.tsx — the ⌘K palette is filtered the same way.
  • app/nav.test.ts (new) — asserts the real-mode nav set (only Payroll + Treasury), that every no-backend screen is hidden, that groups collapse in order, and that the landing (REAL_HOME) is itself real-backed. Demo mode still shows the full set.

Why Settings/Overview/etc. are hidden, not ported

Those domains have no backend on this platform (no /payments, /invoices, /grants, /policies, /integrations, /dashboard; even the Settings team table read targets a non-existent /members). Per the epic's option-A decision they stay demo-only. Workspace switching remains available in the top bar. Real team management (/orgs/:id/members) is a genuine future feature, not a gate.

Verification (all green)

typecheck · test (76, incl. 6 new nav tests) · build (real) · VITE_DEMO_MODE=1 build · biome lint .


This completes the console→backend reconciliation (epic #58). Remaining: redeploy console.benzo.space (real mode) + the BenzoNet e2e (gated on test-USDC for a treasury deposit).

Summary by CodeRabbit

  • New Features

    • Navigation now adapts to the active mode, showing only available screens in real mode and the full experience in demo mode.
    • Added mode-aware routing, including a dedicated real-mode landing page and fallback handling for unavailable routes.
    • Updated the jump-to menu to hide destinations that are unavailable in real mode.
    • Navigation groups and sidebar items are now consistently organized across modes.
  • Tests

    • Added coverage for navigation visibility, groups, routes, and real-mode landing behavior.

Greptile Summary

This PR gates the console navigation and routes by demo mode. The main changes are:

  • Added a shared nav model with real-backed flags.
  • Updated the sidebar and command palette to hide demo-only destinations in real mode.
  • Split the route table so real mode only exposes payroll and treasury.
  • Added tests for real-mode nav visibility and group ordering.

Confidence Score: 4/5

The real-mode route gate needs a fix before merging.

  • /claim now redirects to treasury for active-org users in real mode.
  • Demo-only screens remain statically imported, so the bundle split does not follow from the route branch alone.
  • The shared nav filtering logic is small and covered by the new tests.

apps/console/src/app/Shell.tsx

Important Files Changed

Filename Overview
apps/console/src/app/Shell.tsx Uses the shared nav model and gates routes by demo mode, but the real-mode branch drops /claim and still statically imports demo-only screens.
apps/console/src/app/nav.ts Adds the shared navigation model, real-mode filtering helpers, and treasury landing constant.
apps/console/src/app/CommandBar.tsx Filters command destinations by demo mode so real mode only exposes backend-backed screens.
apps/console/src/app/nav.test.ts Adds focused tests for real/demo nav filtering, group collapse, and the real-mode landing target.

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/console/src/app/Shell.tsx:257
**Invite Claim Route Redirected**

When a real-mode user with an active org opens `/claim`, this wildcard catches the URL and sends them to `/treasury`. The previous route table registered `InviteClaim`, so invite links can no longer reach the claim screen in the real build.

### Issue 2 of 2
apps/console/src/app/Shell.tsx:235-250
**Demo Screens Still Imported**

This branch makes the demo-only routes unreachable in real mode, but the demo screens are still statically imported at module load. A real build can still include and evaluate those screen modules and their transitive dependencies, so the intended real-mode bundle split does not happen from this route gate alone.

Reviews (1): Last reviewed commit: "feat(console): real-mode nav/route gatin..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Real mode (VITE_DEMO_MODE unset) now exposes only the screens wired to the eERC
backend — Payroll and Treasury — and lands on /treasury. The no-backend screens
(Overview, Contractors, Invoices, one-off Pay, Approvals, Auditor grants, Audit
log, Settings) stay demo-only: hidden from the sidebar + command bar and their
routes redirect to the treasury landing; in a real build they tree-shake out
entirely. Demo mode (VITE_DEMO_MODE=1) still shows the full product. A shared
nav model (app/nav.ts) is the single source of truth for both the sidebar and
the command palette.

Epic #58 step B (final). Closes #68.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Navigation and route gating

Layer / File(s) Summary
Centralized navigation visibility
apps/console/src/app/nav.ts, apps/console/src/app/nav.test.ts
Defines navigation metadata, real-backed flags, the real-mode landing route, visibility helpers, and mode-specific tests.
Sidebar and route integration
apps/console/src/app/Shell.tsx
Builds sidebar sections from visible navigation configuration and restricts routes in real mode, redirecting unsupported paths to the real-mode home.
Command palette filtering
apps/console/src/app/CommandBar.tsx
Filters destinations by their real/demo availability based on DEMO_MODE.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Shell
  participant NavModel
  participant Router
  User->>Shell: Open console
  Shell->>NavModel: Get visibleNavGroups and visibleNavItems
  NavModel-->>Shell: Return mode-filtered navigation
  Shell->>Router: Register mode-specific routes
  Router-->>User: Render screen or redirect to REAL_HOME
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Real-mode gating is implemented, but the summary shows /settings is omitted from real-mode routes, conflicting with #68's requirement to keep Settings available. Add /settings to the real-mode route set and render only its backed org/team sections while keeping demo mode unchanged.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes stay focused on navigation, routing, command-bar filtering, and tests for real/demo mode gating.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Clearly describes the main change: real-mode navigation and route gating to backend-backed screens.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch console/68-realmode-gating

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/console/src/app/CommandBar.tsx`:
- Around line 10-25: Replace the duplicated DESTINATIONS definitions and
DEMO_MODE filtering in CommandBar with the shared visibleNavItems() navigation
model. Map the returned NavItemDef entries to the command palette shape, and add
any command-specific labels or metadata to NavItemDef so both sidebar and
palette use the same routes and real-mode visibility contract.

In `@apps/console/src/app/nav.ts`:
- Around line 5-6: Keep the supported organization and team settings accessible
in real mode: update the Settings navigation metadata and real-mode route
registration in Shell.tsx so /settings is not treated as demo-only, then modify
SettingsScreen to gate only unsupported settings cards or tabs rather than the
entire screen.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e35afec-cdf5-4360-ae4e-fcafc9c8866b

📥 Commits

Reviewing files that changed from the base of the PR and between 977dca9 and be0cfed.

📒 Files selected for processing (4)
  • apps/console/src/app/CommandBar.tsx
  • apps/console/src/app/Shell.tsx
  • apps/console/src/app/nav.test.ts
  • apps/console/src/app/nav.ts

Comment on lines +10 to +25
import { DEMO_MODE } from "../demo/flag";

// Jump-to targets mirror the sidebar: in real mode only the backend-backed
// screens are reachable, so the palette hides the demo-only destinations too.
const DESTINATIONS = [
{ label: "Dashboard", to: "/" },
{ label: "Contractors", to: "/contractors" },
{ label: "Payroll", to: "/payroll" },
{ label: "Invoices to pay", to: "/invoices" },
{ label: "Send & vendor pay", to: "/pay" },
{ label: "Approvals", to: "/approvals" },
{ label: "Treasury", to: "/treasury" },
{ label: "Auditor grants", to: "/grants" },
{ label: "Audit log", to: "/audit" },
{ label: "Settings & team", to: "/settings" },
];
{ label: "Dashboard", to: "/", real: false },
{ label: "Contractors", to: "/contractors", real: false },
{ label: "Payroll", to: "/payroll", real: true },
{ label: "Invoices to pay", to: "/invoices", real: false },
{ label: "Send & vendor pay", to: "/pay", real: false },
{ label: "Approvals", to: "/approvals", real: false },
{ label: "Treasury", to: "/treasury", real: true },
{ label: "Auditor grants", to: "/grants", real: false },
{ label: "Audit log", to: "/audit", real: false },
{ label: "Settings & team", to: "/settings", real: false },
].filter((d) => DEMO_MODE || d.real);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Derive palette destinations from the shared navigation model.

This duplicates both route definitions and real-mode visibility metadata instead of using visibleNavItems(). It can drift from the sidebar and route-gating contract; keep any command-specific labels as metadata in NavItemDef if needed.

Suggested direction
-import { DEMO_MODE } from "../demo/flag";
+import { visibleNavItems } from "./nav";

-const DESTINATIONS = [
-  // duplicated destinations and `real` flags
-].filter((d) => DEMO_MODE || d.real);
+const DESTINATIONS = visibleNavItems().map((item) => ({
+  label: item.label,
+  to: item.to,
+}));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { DEMO_MODE } from "../demo/flag";
// Jump-to targets mirror the sidebar: in real mode only the backend-backed
// screens are reachable, so the palette hides the demo-only destinations too.
const DESTINATIONS = [
{ label: "Dashboard", to: "/" },
{ label: "Contractors", to: "/contractors" },
{ label: "Payroll", to: "/payroll" },
{ label: "Invoices to pay", to: "/invoices" },
{ label: "Send & vendor pay", to: "/pay" },
{ label: "Approvals", to: "/approvals" },
{ label: "Treasury", to: "/treasury" },
{ label: "Auditor grants", to: "/grants" },
{ label: "Audit log", to: "/audit" },
{ label: "Settings & team", to: "/settings" },
];
{ label: "Dashboard", to: "/", real: false },
{ label: "Contractors", to: "/contractors", real: false },
{ label: "Payroll", to: "/payroll", real: true },
{ label: "Invoices to pay", to: "/invoices", real: false },
{ label: "Send & vendor pay", to: "/pay", real: false },
{ label: "Approvals", to: "/approvals", real: false },
{ label: "Treasury", to: "/treasury", real: true },
{ label: "Auditor grants", to: "/grants", real: false },
{ label: "Audit log", to: "/audit", real: false },
{ label: "Settings & team", to: "/settings", real: false },
].filter((d) => DEMO_MODE || d.real);
import { visibleNavItems } from "./nav";
// Jump-to targets mirror the sidebar: in real mode only the backend-backed
// screens are reachable, so the palette hides the demo-only destinations too.
const DESTINATIONS = visibleNavItems().map((item) => ({
label: item.label,
to: item.to,
}));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/app/CommandBar.tsx` around lines 10 - 25, Replace the
duplicated DESTINATIONS definitions and DEMO_MODE filtering in CommandBar with
the shared visibleNavItems() navigation model. Map the returned NavItemDef
entries to the command palette shape, and add any command-specific labels or
metadata to NavItemDef so both sidebar and palette use the same routes and
real-mode visibility contract.

Comment on lines +5 to +6
* the rest (Overview, Contractors, Invoices, one-off Pay, Approvals, Grants, Audit,
* Settings) have no backend on this platform and stay **demo-only**. Demo mode

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep backed organization and team settings reachable in real mode.

/settings is marked demo-only, so it disappears from navigation and the real-mode wildcard in Shell.tsx redirects it to Treasury. This conflicts with the requirement to retain backed organization/team settings. Mark the supported Settings entry real-backed, register it in the real-mode routes, and gate only unsupported Settings cards/tabs within SettingsScreen.

Also applies to: 36-36

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/app/nav.ts` around lines 5 - 6, Keep the supported
organization and team settings accessible in real mode: update the Settings
navigation metadata and real-mode route registration in Shell.tsx so /settings
is not treated as demo-only, then modify SettingsScreen to gate only unsupported
settings cards or tabs rather than the entire screen.

<Route path="/payroll" element={<Payroll />} />
<Route path="/treasury" element={<Treasury />} />
<Route path="*" element={<Navigate to={REAL_HOME} replace />} />
</>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Invite Claim Route Redirected

When a real-mode user with an active org opens /claim, this wildcard catches the URL and sends them to /treasury. The previous route table registered InviteClaim, so invite links can no longer reach the claim screen in the real build.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/console/src/app/Shell.tsx
Line: 257

Comment:
**Invite Claim Route Redirected**

When a real-mode user with an active org opens `/claim`, this wildcard catches the URL and sends them to `/treasury`. The previous route table registered `InviteClaim`, so invite links can no longer reach the claim screen in the real build.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Codex

Comment on lines +235 to +250
{DEMO_MODE ? (
<>
<Route path="/" element={<Dashboard />} />
<Route path="/approvals" element={<Approvals />} />
<Route path="/contractors" element={<Contractors />} />
<Route path="/payroll" element={<Payroll />} />
<Route path="/invoices" element={<Invoices />} />
<Route path="/pay" element={<Pay />} />
<Route path="/treasury" element={<Treasury />} />
<Route path="/grants" element={<Grants />} />
<Route path="/audit" element={<AuditLog />} />
<Route path="/claim" element={<InviteClaim />} />
<Route path="/settings" element={<SettingsScreen />} />
<Route path="*" element={<Dashboard />} />
</>
) : (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Demo Screens Still Imported

This branch makes the demo-only routes unreachable in real mode, but the demo screens are still statically imported at module load. A real build can still include and evaluate those screen modules and their transitive dependencies, so the intended real-mode bundle split does not happen from this route gate alone.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/console/src/app/Shell.tsx
Line: 235-250

Comment:
**Demo Screens Still Imported**

This branch makes the demo-only routes unreachable in real mode, but the demo screens are still statically imported at module load. A real build can still include and evaluate those screen modules and their transitive dependencies, so the intended real-mode bundle split does not happen from this route gate alone.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Cursor Fix in Codex

@hitakshiA
hitakshiA merged commit 667962d into main Jul 11, 2026
3 checks passed
@hitakshiA
hitakshiA deleted the console/68-realmode-gating branch July 11, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(console): real-mode nav/route gating (hide no-backend screens)

1 participant