useDocyrusTenant
Single-line tenant integration that fetches tenant preferences, builds dateUtils + numberUtils, and wires DateFormatProvider + NumberFormatProvider so every UI component (data grids, calendars, filters, value renderers) picks up the tenant's configured date/time/number formats automatically.
Installation
pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-tenantpnpm add @docyrus/app-utils @docyrus/api-client @tanstack/react-queryThis module ships two named exports:
<DocyrusTenantProvider>— React provider that fetches/v1/tenant/preferences, normalizes the response, and wires both<DateFormatProvider>and<NumberFormatProvider>for everything beneath it.useDocyrusTenant()— read-only hook returning{ preferences, dateUtils, numberUtils, isLoading }for ad-hoc use (chart axes, custom headers, side panels, …).normalizeTenantPreferences(envelope)— pure helper that converts the rawGET /v1/tenant/preferencesenvelope into the snake_case shape@docyrus/app-utilsfactories expect. Exported standalone for apps that build their own provider.
Requires an authenticated RestApiClient from @docyrus/api-client and a QueryClientProvider from @tanstack/react-query somewhere above the provider.
Why this exists
@docyrus/app-utils@0.11.x factories (createDateUtils, createNumberUtils) read the inner preferences object keyed in snake_case (date_format, date_time_format, decimal_precision, …) but the /v1/tenant/preferences API returns a { preferences: {...} } envelope where every key is camelCase (dateFormat, dateTimeFormat, decimalPrecision, …).
Without normalization both factories silently fall back to their hard-coded defaults ('Y-m-d', 'Y-m-d H:i:s', en-US locale) — which is the root cause of the "Docyrus Data Grid doesn't honor tenant date/time format" issue. <DocyrusTenantProvider> is the one-line fix: unwrap the envelope, mirror the keys, build the utils, install the contexts.
Usage
Mount the provider once near the root of your app, beneath your auth + query providers:
'use client';
import { useDocyrusAuth } from '@docyrus/signin';
import { DocyrusTenantProvider } from '@docyrus/ui/library/hooks/use-docyrus-tenant';
export function AppShell({ children }: { children: React.ReactNode }) {
const { client, status, user } = useDocyrusAuth();
const userTimezone = (user as { timeZone?: { id?: string } } | null)?.timeZone?.id;
return (
<DocyrusTenantProvider
client={client}
enabled={status === 'authenticated'}
userTimezone={userTimezone}>
{children}
</DocyrusTenantProvider>
);
}From there, every UI component that reads useDateFormat() / useNumberFormat() — including useDocyrusDataGrid, useDocyrusDataTable, useDocyrusDataGallery, calendar event cells, side-filter chips, and the standalone value renderers — picks up the tenant's configured formats automatically. No per-page formatter wiring required.
For ad-hoc formatting (custom chart axes, header labels, etc.) read the same utils via the hook:
import { useDocyrusTenant } from '@docyrus/ui/library/hooks/use-docyrus-tenant';
function ChartHeader({ date }: { date: string }) {
const { dateUtils, isLoading } = useDocyrusTenant();
if (isLoading || !dateUtils) return <span>—</span>;
return <span>{dateUtils.formatDateLong(date)}</span>;
}Provider props
| Prop | Type | Default | Description |
|---|---|---|---|
client | RestApiClient | null | undefined | — | The @docyrus/api-client instance used to fetch /v1/tenant/preferences. |
enabled | boolean | Boolean(client) | Defer the fetch until your auth flow is ready. Set to false (or leave client null) until the user is authenticated. |
userTimezone | string | 'UTC' | IANA timezone id forwarded to createDateUtils. Typically the user profile's timeZone.id. |
staleTime | number | 1_800_000 (30 min) | TanStack Query stale window for the preferences query. |
children | ReactNode | — | App tree to wrap. |
Hook return
useDocyrusTenant() returns:
| Field | Type | Description |
|---|---|---|
preferences | TenantPreferences | null | Normalized tenant preferences (camelCase keys mirrored to snake_case). null until the fetch finishes. |
dateUtils | DateUtils | null | Output of createDateUtils({ preferences, userTimezone }). Exposes formatDate, formatDateTime, formatDateLong, toUserTimezone. |
numberUtils | NumberUtils | null | Output of createNumberUtils({ preferences }). Exposes formatNumber. |
isLoading | boolean | TanStack Query's loading flag for the preferences fetch. |
Auto-wired contexts
Internally <DocyrusTenantProvider> mounts:
DocyrusTenantContext.Provider
└── DateFormatProvider (formatDate / formatDateTime / formatTime)
└── NumberFormatProvider (formatNumber)
└── {children}Every Docyrus UI component reads from these contexts:
| Consumer | Context | Effect |
|---|---|---|
useDocyrusDataGrid / useDocyrusDataTable / useDocyrusDataGallery | useDateFormat() + useNumberFormat() | Date / datetime / number cells pick up tenant formatting unless a formatDate / formatDateTime / formatNumber prop is passed explicitly (props still win). |
| Calendar event cells, side-filter chips | useDateFormat() | Same fallback chain. |
DocyrusDateValue, DocyrusDateTimeValue, DocyrusNumberValue value renderers | useDateFormat() / useNumberFormat() | Standalone display components inherit tenant formatting. |
Backwards compatible: the data-grid / data-table hooks detect whether a real provider is mounted (vs. the no-provider sentinel) before injecting the context formatter. Apps that haven't adopted <DocyrusTenantProvider> yet keep their previous behaviour byte-for-byte — the cells fall back to their internal Intl.NumberFormat / raw-string renderers exactly as before.
Standalone normalizeTenantPreferences
If you maintain your own provider stack (for example you also need to seed a non-Docyrus formatter), you can call the helper directly:
import {
normalizeTenantPreferences,
type DocyrusTenantContextValue
} from '@docyrus/ui/library/hooks/use-docyrus-tenant';
import {
createDateUtils,
createNumberUtils,
getTenantPreferences
} from '@docyrus/app-utils';
const envelope = await getTenantPreferences(client);
const preferences = normalizeTenantPreferences(envelope);
if (preferences) {
const dateUtils = createDateUtils({ preferences, userTimezone });
const numberUtils = createNumberUtils({ preferences });
// …wire them into your own context
}The helper accepts either the full envelope ({ preferences, tenant, product, enums }) or an already inner-shaped object and is safe to call with null / undefined (returns null).
Type Exports
| Type | Description |
|---|---|
DocyrusTenantContextValue | Shape returned by useDocyrusTenant(). |
DocyrusTenantProviderProps | Props accepted by <DocyrusTenantProvider>. |
useDocyrusPivotGrid
Wires the PivotGrid component to a Docyrus data source with optional server-side aggregation via pivot.matrix + calculations.
useDynamicFormView
Backend-agnostic dynamic form renderer — the sibling of useDocyrusFormView with no Docyrus wiring. Supply fields, values, options, and an onSubmit sink to render create/edit/view forms against any backend.