@get-asset/react

Profit & Loss Card

Styled card showing month-by-month revenue and expense pairs for the range on screen. Bars share a scale derived from the largest value in the set. The chart covers every calendar month the range overlaps — a whole year gives the familiar twelve — and follows the AssetProvider's range unless dateFrom / dateTo pin it. Pass insights(shown in the preview below) to render the AI insights panel inside the card; it answers the listed questions from the insights endpoint automatically for the range on screen. An “Ask something else” input lets people type their own question, which returns the same structured answer.

Preview

Loading preview…

Required scopes

The access token used by AssetProvider must include these scopes for this primitive to fetch data successfully:

  • report:read
  • tag:read
  • business:read

API reference

Root props

PropTypeDefaultDescription
dateFromCalendarDateStringInclusive start (YYYY-MM-DD) of a fixed reporting range. Detaches the card from the AssetProvider's shared range entirely; must be paired with dateTo (passing one side alone throws). Use periodDateRange to derive the pair from a month, quarter or year. Omit both to follow the provider's DatePicker-driven range.
dateToCalendarDateStringInclusive end (YYYY-MM-DD) of the fixed reporting range. Must be paired with dateFrom.
monthsProfitLossMonthOverride[]Override fetched data — one entry per month on screen. Each entry: { month, revenue, expense }. Skips the API call.
tagFieldsTagFilterField[]Override the fetched tag filter fields — primarily for testing/storybook. When the business has tag values, a searchable Select Tags control renders in the card header (the same grouped multi-select the transactions Tag column uses, without value creation); selecting tags refetches the chart and scopes the insights panel.
state"loading" | "error" | "empty"Force a specific state for testing.
insightsboolean | { questions?, ask? }Render the AI insights panel inside the card. Pass `true` and it fetches answers from the profit & loss insights endpoint automatically for the range on screen. Alongside the starter questions, an “Ask something else” input lets people type their own — it runs through the same endpoint and returns the same structured answer. Optionally override the starter `questions`, or inject `ask` to mock answers in docs / tests (this preview uses it).
lockedbooleanGate the report for a viewer who isn't entitled to it. Activates the SDK's Locked state: the chart renders blurred and inert as a teaser behind a lock and the upgrade CTA; the insights panel is omitted. No live fetch runs while locked — the teaser shows built-in placeholder data (a `months` override still wins).
upsell{ title?, description?, ctaLabel?, onUpgrade? }Customize the locked state's copy and action. `ctaLabel` and `onUpgrade` are the tenant's upgrade wording and handler; `title` / `description` default per report.
onViewDetails(ctx: { range, period, basis }) => voidCallback fired when the viewer asks to see more details (e.g. to navigate to the full report). Receives the card's resolved range, the exact period it represents (or null for a custom range) and the accounting basis, so the report can open on the same window the card shows.
basis"accrual" | "modified_cash"Report on a specific accounting basis. Omit to inherit the AssetProvider's basis, which itself falls back to the Business's country — Canadian Businesses report on Accrual, everyone else on Cash (see Accounting basis).
basisTogglebooleanShow the Cash/Accrual select in the card header. Off by default. Flipping it moves every component that inherits the provider's basis, so it can't leave a screen half cash and half accrual (see Accounting basis).
classNamestringForwarded to the outer card element.

States

Loading

Loading preview…

Success

Loading preview…

EmptyNo P&L data for the range.

Loading preview…

Error

Loading preview…

View detailsPass `onViewDetails` to handle a request for more details (e.g. navigate to the full report). The callback receives the card's own range, period and basis — here it logs them to the console.

Loading preview…

LockedViewer isn't entitled to the report — a built-in placeholder teaser renders blurred behind the upgrade prompt. Pass `upsell` to customize the copy and wire `onUpgrade`.

Loading preview…

Accounting basis

AssetProfitLossCard reports on one of two accounting bases: Accrual (accrual on the wire), which counts invoices and bills when they are issued, or Cash (modified_cash), which counts them when the money moves. Cashflow components take no basis — cashflow is a cash concept.

The basis is resolved per component, first match winning:

  • the card's own basis prop;
  • the provider's basis — defaultBasis on AssetProvider, or whatever the toggle was last flipped to;
  • the Business's country — Canadian Businesses report on Accrual, everyone else on Cash.

Only the last step costs a request — a Business read, which is why every basis-aware component's scopes includes business:read — and it is serial: the card holds its own fetch until the Business answers. One request is shared across every basis-aware component on the screen, but pass defaultBasis on AssetProvider when you already know the basis and skip it entirely. If the lookup fails the card falls back to Cash and stays there — the Retry button re-runs the report, not the basis lookup.

basisToggle is off by default: no control renders in the header and the card reports on its resolved basis. Turn it on and flipping the control moves every basis-aware component under the provider with it, so the toggle can never leave a screen half cash and half accrual. A component pinned with its own basis prop is the exception — it keeps the basis you gave it and renders no toggle of its own, which is how you show both bases side by side on purpose.