Hooks

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-tenant
Required Packages(3 packages)
pnpm add @docyrus/app-utils @docyrus/api-client @tanstack/react-query

This 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 raw GET /v1/tenant/preferences envelope into the snake_case shape @docyrus/app-utils factories 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

PropTypeDefaultDescription
clientRestApiClient | null | undefined—The @docyrus/api-client instance used to fetch /v1/tenant/preferences.
enabledbooleanBoolean(client)Defer the fetch until your auth flow is ready. Set to false (or leave client null) until the user is authenticated.
userTimezonestring'UTC'IANA timezone id forwarded to createDateUtils. Typically the user profile's timeZone.id.
staleTimenumber1_800_000 (30 min)TanStack Query stale window for the preferences query.
childrenReactNode—App tree to wrap.

Hook return

useDocyrusTenant() returns:

FieldTypeDescription
preferencesTenantPreferences | nullNormalized tenant preferences (camelCase keys mirrored to snake_case). null until the fetch finishes.
dateUtilsDateUtils | nullOutput of createDateUtils({ preferences, userTimezone }). Exposes formatDate, formatDateTime, formatDateLong, toUserTimezone.
numberUtilsNumberUtils | nullOutput of createNumberUtils({ preferences }). Exposes formatNumber.
isLoadingbooleanTanStack 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:

ConsumerContextEffect
useDocyrusDataGrid / useDocyrusDataTable / useDocyrusDataGalleryuseDateFormat() + 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 chipsuseDateFormat()Same fallback chain.
DocyrusDateValue, DocyrusDateTimeValue, DocyrusNumberValue value renderersuseDateFormat() / 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

TypeDescription
DocyrusTenantContextValueShape returned by useDocyrusTenant().
DocyrusTenantProviderPropsProps accepted by <DocyrusTenantProvider>.

On this page