Skip to main content
All Docs
FeaturesmyProp (AgentOS People Portal)Updated April 4, 2026

Company-Namespaced Portal Shell

Company-Namespaced Portal Shell

Introduced in v0.1.15, the company-namespaced route shell is the core architectural layer that makes each agency's myProp portal distinct. It handles authentication, company resolution, branding, and navigation — so that every page within /:company is automatically scoped to the correct agency and styled with its brand colours.

How It Works

When a user visits a company URL (e.g., /acme-lettings), the following happens in sequence:

User visits /:company
       │
       ▼
 Middleware checks auth
  └─ Not signed in → redirect to /sign-in?callbackUrl=/:company
       │
       ▼
 Server layout validates slug format
  └─ Empty or >200 chars → 404
       │
       ▼
 AgentOS API availability check
  └─ Not configured → render fallback shell with defaults
       │
       ▼
 verifyCompany(slug)
  └─ Not found or inactive → 404 (Company Not Found page)
       │
       ▼
 Parallel fetch: branding + branches + currency
  └─ Each call has independent error fallback
       │
       ▼
 Client layout renders:
  CompanyProvider → CompanyThemeProvider → CompanyShell → {children}

Authentication

Authentication is checked at two layers:

  1. Middleware — intercepts the request before any rendering; redirects unauthenticated users to /sign-in with a callbackUrl pointing back to the original company URL.
  2. Server layout — re-checks the session server-side as a belt-and-suspenders guard before any API calls are made.

Company Verification

The verifyCompany(slug) function calls the AgentOS API to confirm:

  • The company short name exists
  • The company is currently active

If either check fails, Next.js's notFound() is called, rendering the Company Not Found page.

Company Context

All data fetched during layout initialisation is made available to child components via the useCompany() hook:

import { useCompany } from '@/contexts/company-context';

export function MyComponent() {
  const { company, branding, branches, currency, shortName } = useCompany();

  return <p>Welcome to {company.companyName}</p>;
}

Context Shape

interface CompanyContextValue {
  shortName: string;          // URL slug, e.g. "acme-lettings"
  company: CompanyInfo;       // Name, address, phone, email, website, isActive
  branding: CompanyBranding;  // Hex colours, logo/icon/banner URLs, custom CSS
  branches: CompanyBranch[];  // List of agency branches
  currency: CurrencyInfo;     // { isoCode, symbol, name }
}

useCompany() must only be called inside a component that is a descendant of a /:company route. Calling it outside that context throws.

Runtime Theming

The CompanyThemeProvider converts AgentOS hex brand colours into OKLCH CSS custom properties at runtime, overriding the default Tailwind/shadcn theme:

Colour Mapping

AgentOS fieldOverridden CSS variables
primaryColour--primary, --ring, --sidebar-primary, --chart-1
secondaryColour--secondary, --accent
backgroundColour--background, --card, --popover
textColour--foreground, --card-foreground

In addition, the provider auto-generates:

  • Foreground contrast colours for primary/secondary surfaces
  • Muted variants of background and accent colours
  • Sidebar dark variant derived from primaryColour

Conversion Pipeline

Colours are converted through a three-step pipeline:

Hex string (e.g. #2563EB)
    └─ Parse to RGB (0–255 per channel)
          └─ Normalise to linear light
                └─ Convert to OKLCH (L, C, H)
                      └─ Write as CSS custom property

If a colour field is null or unparseable, that CSS variable is left at its default value — the rest of the theme continues to apply normally.

Navigation Shell

CompanyShell wraps all company page content with a responsive navigation frame:

Desktop

  • Fixed sidebar, always visible
  • Company logo and name at the top
  • Navigation links
  • Company contact info (phone, email) at the bottom
  • User profile dropdown with sign-out

Mobile

  • Hamburger button in the top bar
  • Slide-out drawer containing the same navigation links
  • User profile dropdown with sign-out

Route Files Reference

PathTypePurpose
src/app/[company]/layout.tsxServer ComponentAuth, verification, data fetch, metadata
src/app/[company]/company-layout-client.tsxClient ComponentWires context + theme + shell
src/app/[company]/page.tsxServer ComponentCompany home with quick-access cards
src/app/[company]/not-found.tsxClient ComponentInvalid/inactive company slug
src/app/[company]/loading.tsxServer ComponentSkeleton while data fetches
src/app/[company]/error.tsxClient ComponentRuntime error boundary with retry
src/contexts/company-context.tsxContextCompanyProvider + useCompany() hook
src/components/company-theme-provider.tsxClient ComponentHex → OKLCH CSS variable injection
src/components/company-shell.tsxClient ComponentResponsive nav sidebar/drawer

Company Not Found Page

The not-found page is shown when:

  • The URL slug doesn't match any AgentOS agency
  • The company has been deactivated
  • The slug is malformed (empty or over 200 characters)

It provides users with actionable guidance: verify the URL, contact their agent, or check their invitation email.

Error Boundary

Runtime errors within the company shell are caught by error.tsx. The boundary:

  • Logs the error with captureError(), including the error digest for tracing
  • Displays a recovery UI with a Try Again button (calls reset()) and a Back to Home link
  • Does not expose raw error messages to the user

Graceful Degradation

The layout is designed to never crash due to AgentOS API unavailability:

  • If the API is not configured → renders a fallback shell with default branding
  • If getCompanyBranding fails → falls back to null colours (default theme preserved)
  • If getCompanyBranches fails → falls back to an empty array
  • If getCurrencyInfo fails → falls back to GBP (£)

Each fallback is independent; a failure in one API call does not affect the others.

Route Priority

Next.js resolves static route segments before dynamic ones. The [company] segment will never capture requests to existing static routes such as /dashboard, /sign-in, /privacy, or /api/*. No existing routes are affected by this addition.

Health Check Endpoint

The middleware now fast-paths requests to /api/health, bypassing all auth and redirect logic and returning 200 immediately. This ensures uptime monitors and load balancers always receive a reliable response regardless of session state.