Frontend / App

Last generated 2026-07-13 · window 2026-07-12T18:02:00Z–2026-07-13T18:01:03Z

Inertia.js bridges Laravel controllers to React pages under apps/web/resources/js/Pages/. Vite bundles TypeScript + Tailwind.

Structure #

  • Pages/Dashboard/ — KPI dashboard builder
  • Pages/TenantJourney/ — campaigns, experiments, reporting, Editor (click-to-translate), Translations (scope override grid)
  • Pages/RapidStorSettings/ — native RapidStor facility/corp settings (schema-driven tabs, WYSIWYG, per-locale translations editor)
  • Pages/RevenueManagement/Home (portfolio landing), rules, approval centre (activity feed + bulk pricing), Portfolio, revenue map
  • Pages/Admin/ — internal admin including Stats.tsx
  • Pages/Tasks/, Contacts/, Settings/ — operations modules

Admin Stats page #

Route: GET /admin/stats. Tabs:

  1. Application Interest — coming-soon sign-ups
  2. System Alerts — admin notifications
  3. Benchmarks — cross-corp booking funnel benchmarks (BenchmarksTab.tsx)

Benchmarks tab consumes Inertia prop benchmarks from ApplicationInterestController@stats. Date range changes trigger partial reload (only: ['benchmarks']). Five views: by corporation, timezone, fleet baseline, one-vs-rest, top/bottom. Conversion rates are RapidStor-only; move-in volumes span all channels (footnote in UI).

Admin — Data Sync tab #

Component: Pages/Admin/components/DataSyncTab.tsx on the corporations admin page. Shows per-corporation importer schedules with last-run status, manual sync triggers, and enable/disable toggles.

  • Requirement gating — Each importer declares ImporterRequirement cases (SITELINK_API, RAPIDSTOR_TOKEN, GOOGLE_INTEGRATION, MTAS_API, BIGQUERY_CONFIG). ImporterRequirement::unmet() is the single source of truth shared by manual sync dispatch (AdminController::syncData), scheduled command (RunScheduledSyncs), and queued DataImportJob execution.
  • Schedule toggleSyncScheduleRow blocks enabling a schedule when requirements are unmet, but always allows disabling (no backend check on disable).
  • Scheduled sync guardapp:run-scheduled-syncs skips due schedules when the corporation no longer satisfies importer requirements, preventing failing jobs after config is revoked.

Tenant Journey — Editor & translations #

Editor (Pages/TenantJourney/Editor.tsx): embeds RapidStor v2 preview iframe; click-to-translate opens TranslationInspectorPanel for snippet keys. Elements controlled by RapidStor site settings are flagged to avoid false positives. Translations page (Translations.tsx) edits language/corp/location overrides via TranslationGrid + SnippetTranslationsDialog.

RapidStor Settings screen #

Page: Pages/RapidStorSettings/Index.tsx. Schema tabs from server; client helpers handle path get/set, feature gating (customFeatures, multi-site secondary read-only), validation parity with legacy AngularJS, and per-locale TranslationsEditor for translatable fields. Facility dropdown lives in page header via AuthenticatedLayout props; form state resyncs on facility change.

Helpdesk Center panel #

Component: features/helpdesk-center/HelpdeskPanel.tsx. Opened from the site header when the helpdesk Pennant flag is enabled. Tabs: Articles, Tours, legacy Helpdesk index.

Articles tab (ArticleSearch): loads the full article list once via RTK Query useListHelpdeskArticlesQueryGET /api/v1/helpdesk/articles, then filters titles client-side as the user types (no debounced server search). Drill-in fetches rendered content via GET /api/v1/helpdesk/page/{id}. Analytics capture coarse metadata only (query length, never raw search text).

Revenue Management — Home & Approval Centre #

Home (Pages/RevenueManagement/Home.tsx): corp-scoped landing at /revenue-management/home/{facility?}. Sections include HubCards, AttentionStrip (portfolio-wide pending / below-market), KpiRoiStrip, OperationsSection, FacilitiesTable, ActivityFeed, and ValueReportSection. Hydrates from engine portfolio bundle; missing surfaces show not-wired placeholders.

Approval Centre (Pages/RevenueManagement/ApprovalCentre.tsx): three-column layout — bulk-pricing grid (left), alerts filter (centre), activity feed (right).

  • Bulk pricing grid (BulkPricingTable.tsx): rows keyed on RapidStor unit-type _id; columns Unit group, Units/avail, Occ, Base, Competitive, Pricing formula, Unit attributes, Seasonal, Price discovery, Suggested, Override, Action. Live inventory fields from corp-wide bulk-pricing path. Discriminator sub-label (Type · Floor N · Features) when present. Collapsible facility groups with Approve-all. Why modal (WhyModal.tsx): Starting point → factor contributions → Suggested, with Stage-as-override / Approve.
  • Activity feed (ActivityFeed.tsx): wired via useGetActivityQueryGET /api/v1/revenue-management/activity?facility=; client-side groupActivity() groups events by date (Today/Yesterday/date) with relative timestamps in browser TZ. Also reused on Home.
  • Engine-connected gating: UI surfaces disabled states when facility is not engine_connected; manual engine runs and undo are blocked for disconnected facilities.

PostHog analytics #

app.tsx identifies the logged-in user and captures $pageview / feature_viewed only when authenticated. Anonymous pages (Login, ForgotPassword, Invite, Welcome) do not emit pageviews, so DAU/WAU unique-user insights reflect real operators rather than anonymous session noise.

Feature-gated UI #

Pages check Pennant application flags client-side where needed; server middleware enforces access. Disabled apps are hidden from navigation. Sidebar assembly is centralized in ApplicationMenuBuilder (see Architecture).

Admin Application Management tab: corporation rows show a read-only "Resolves to" hint when global ceiling changes effective state (ApplicationStateResolver::effectiveFromValues). RM-family child rows are read-only in corporation mode (entitlement-driven); global mode remains editable. Grid save for a deliberately-gated RM corp (onboarded before, now un-entitled) shows a warning flash instead of silently re-entitling.