Any unrecognised path redirects to
/.
What the app switcher reads and what the status pip means
The app switcher is backed by the realrealtime.apps.list procedure,
called with your orgId. The query only runs once an orgId is
available, so the switcher has three pre-app states (see below) before
it can show anything.
Once the list loads, the switcher shows the active app — the one you
last selected, or the first app in the list if you have not selected
one. It displays:
- the app’s
name - its
app_id - a coloured pip
status field:
Opening the switcher lists every app in the org with the same
name / app_id / status triple and the same pip per row. Picking a
row changes the active app for the whole shell — every app-scoped tab
re-reads it immediately. The selection is shell state, not a URL
parameter, so switching apps does not change the path you are on.
Next to the switcher, when an app is active, the shell renders a
status pill with a globe icon, the app’s app_id in monospace, and
the raw status string. The pill is absent entirely when there is no
active app. It is the same data as the switcher, surfaced so you can see
which app the current screen is talking to without opening the menu.
Organisation scope versus app scope across the tabs
Two scopes coexist in this console, and the distinction explains why some tabs work before you have an app and others do not. Organisation-scoped. The app list itself is org-scoped: it is fetched byorgId, not by app. Apps & keys, Webhooks and Settings are
management screens that live at the org level.
App-scoped. Stats, Messages and the Event Creator read live engine
telemetry or publish through the realtime.* procedures against the
currently selected app. Without an active app there is nothing for them
to address.
Every screen receives the same props from the shell, so each one can
decide what to render for itself:
Because
appsError and appsLoading are passed down rather than
handled centrally, a failed app list shows up inside the screen you are
on, not as a shell-level banner.
The three pre-app states: session resolving, signed out, no app
The app switcher renders one of three placeholders before it can show a real app. They look similar, so it is worth knowing which is which. “Checking session…” — auth is still resolving. The pip isoff and
the control is inert. This state exists specifically so that a cold load
does not flash the signed-out “Sign in” call to action for a moment
before the session hydrates. If you see it persist, the session lookup
has not settled yet; nothing is wrong with your apps.
“Sign in” — there is no session, or no orgId, so the app list
cannot be fetched at all. The switcher becomes a link to /login on the
portal, with the subtitle “app list is org-scoped · sign in on the
portal” and an external-link icon. It deliberately links out instead of
rendering a disabled button, because the portal session is shared across
subdomains and signing in there resolves this console too.
Separately, on cold load the console fires a portal-session guard before
the shell paints. The guard resolves when a session exists — or when it
has already attempted the check once in this tab — and otherwise
navigates you to the portal. So in practice most signed-out visitors are
redirected rather than landing on the “Sign in” switcher.
“No app yet” — you are signed in and the org list came back empty.
The switcher becomes an internal link to /apps with the subtitle
“create one in Apps & keys”. This is the state to fix before Stats,
Messages or the Event Creator have anything to show.
Signing in, the shared portal session, and the account menu
Authentication is not owned by this console. The session lives in the portal and is shared with the sibling consoles, which is why both the signed-out switcher and the account menu send you to portal URLs rather than rendering a login form here. The account menu is the circular avatar at the right of the top bar. Its letter is the first character of your signed-in email address, upper cased, or an em dash when no email is known. The button’s tooltip is the full email, or “Account” when there is none. The menu subscribes to auth state changes, so the email updates in place if the session changes in another tab without a reload. Opening it shows:- your email, or “Not signed in”
- the active app as
App · <name>, or “No app connected” — the same active app the switcher and status pill reflect - Portal hub — a link to the portal root
- Sign out when signed in, or Sign in (portal
/login) when not
Branding in the console shell
Two parts of the top-left mark behave differently under white-labelling. “Channels” is the product name of this console and does not change. It is the name of the realtime modality’s dashboard, not a ClutchCall brand string. The logo mark and the platform chip do change. The shell asks the shared branding helpers for a logo source and a platform name. If a branded logo image is configured it renders that; otherwise it falls back to a generated broadcast mark. The small monospace chip beside “Channels” reads<platform> realtime, where the platform name comes
from the same branding lookup.
A white-labelled deployment replaces those two through the portal, under
Settings → Branding.
The theme toggle sits between the docs link and the account menu and
flips the shell between dark and light mode. The current mode is also
passed down to every screen as the theme prop, so screens can match
their own rendering to it.
When a screen fails to render
Every routed screen is wrapped in a single top-level error boundary. If a screen throws during render, you get a fallback card in the content area instead of a white screen:Something broke rendering this screen Reload, or check Sentry for the details.with a Reload button that performs a full page reload. Three things follow from how this boundary is wired:
- The shell survives. The top bar, app switcher, status pill and tab bar are outside the boundary, so they keep working. You can still switch apps, open the account menu, or navigate to another tab.
- Navigating clears the error. The outlet wrapper is keyed by pathname, which remounts the boundary on every navigation. Moving to a different tab resets the error state without a reload — so if only one screen is broken, the rest of the console remains usable.
- The error is reported. The boundary captures the exception to
Sentry along with the React component stack. Error monitoring is a
no-op when no Sentry DSN is configured, and events from this console
are tagged with
vertical='realtime'.
apps.list erroring — is not a render
error; it arrives at the screen as the appsError prop and is displayed
by that screen.
