Skip to content

React Back-Office App

Fullfinity ships two distinct frontends, and it’s important not to confuse them:

  • The React/Mantine back-office SPA (this page, and the other “React App” pages). This is the authenticated admin application — every List, Kanban, Form, Calendar, Pivot, report, wizard and dashboard you see after logging in. It is a single-page app built with React, Mantine, TanStack Query, and React Router. Its source lives under app/src/.
  • The Preact islands framework (window.fullfinity), documented by the rest of the pages in this section. That framework hydrates server-rendered pages — the portal, website, ecommerce storefront and themes. It is not the admin app, has its own registry/toast/event-bus, and never runs inside the SPA.

If you are building an admin screen — a new field widget, a new view type, an action button — you are working in the React app described here. If you are adding interactivity to a Jinja-rendered public page, you want the islands framework instead.

app/
├── src/
│ ├── App.jsx # Root: theme, router, top-level providers
│ ├── axiosInstance.js # The single axios client (interceptors, re-auth, timing)
│ ├── theme.js # Mantine theme (createTheme) + cssVariablesResolver
│ ├── contexts/ # React contexts (User, Navigation, Filter, Form, UI, …)
│ │ ├── ShellProviders.jsx # Composes the context provider tree
│ │ └── LoadingContext.jsx # The one global loading channel
│ ├── data/ # TanStack Query layer
│ │ ├── DataProvider.jsx # QueryClientProvider wrapper
│ │ ├── queryClient.js # QueryClient config
│ │ ├── keys.js # Cache-key factories
│ │ └── hooks/ # useModelQuery, useExecuteMethod, useListData, …
│ ├── hooks/ # UI hooks (useModalStack, useWizard, …)
│ ├── views/ # View-type components + registry.js (capabilities)
│ ├── components/
│ │ └── ui/
│ │ ├── fields/ # Field widgets + the widget registry
│ │ └── misc/ # EmptyState, AsyncBoundary, loaders, …
│ └── utils/ # actionResultHandler.jsx, etc.

The built bundle is emitted to the repo-level static/ directory and served to the browser; you edit the sources under app/src/.

App.jsx mounts the top-level providers, and ShellProviders (contexts/ShellProviders.jsx) composes the focused state contexts inside the shell. The nesting order is deliberate — ConnectionProvider is outermost so a disconnected overlay can cover the whole UI, and TranslationProvider is mounted at App.jsx level (above the shell) so that modals rendered in portals still resolve t().

// Conceptual tree (see App.jsx + ShellProviders.jsx for the exact wiring)
<DataProvider> {/* TanStack Query client */}
<MantineProvider theme={theme} cssVariablesResolver={cssVariablesResolver}>
<TranslationProvider> {/* i18n — above the shell so modals get t() */}
<LoadingProvider> {/* the ONE loading bar + logo overlay */}
<ShellProviders modelRegistry={...}>
{/* ConnectionProvider > UserProvider > NotificationProvider >
ChatProvider > UIProvider > NavigationProvider >
PaginationProvider > FilterProvider > FormProvider >
AnalyticsProvider > ImportProvider */}
<Shell />
</ShellProviders>
</LoadingProvider>
</TranslationProvider>
</MantineProvider>
</DataProvider>

ShellProviders takes a modelRegistry prop and threads it into FilterProvider.

Each context is single-responsibility, exported from contexts/index.js with a matching hook. Import the hook, never reach into the context object directly.

ContextHookResponsibility
UserContextuseUserCurrent user profile and permissions
NavigationContextuseNavigationURL params, the active action, breadcrumbs, menu
FilterContextuseFilterFilter sets, search terms, grouping, saved filters
PaginationContextusePaginationPage number, rows per page, total count
FormContextuseFormContextEdit mode, form values, submit
UIContextuseUILoading flags, modals, theme, responsive state
AnalyticsContextuseAnalyticsChart / pivot configuration
ImportContextuseImportFile-import state
RowSelectionContextuseRowSelectionTable row selection
TranslationContextuseTranslation / useTi18n + locale formatting
ErrorContextuseErrorError-modal display
PreviewContextusePreviewBuilder preview mode (mock values)

Additional providers wired into ShellProviders but not re-exported from contexts/index.js include ConnectionProvider, NotificationProvider and ChatProvider.

Two cross-cutting concerns are deliberately not in this catalogue because they have their own dedicated pages:

  • Loading / empty state → the single global loading channel. See React Conventions.
  • Data fetching → the TanStack Query hooks and axios client. See Data Layer.

Two registries are the supported extension points for the SPA — both live under app/src/ (this is React code, not islands code):

  • Field widgetscomponents/ui/fields/registry.jsx. Register a widget with createWidget / registerWidget; a widget: name in a backend view arch resolves to it.
  • View typesviews/registry.js. Each view type is declared once as a capability record; a component is dispatched by the ViewRenderer.

Both are covered in detail in Widget & View Registries.

Do not add admin-app widgets through the islands framework’s window.fullfinity.registry — that registry is for server-rendered pages only. The two systems are separate.