Skip to main content

Date Range

Date range settings let users pick a period such as Today or Last 7 Days. Your app lists the presets it supports, and the dashboard turns the chosen preset into exact dates using the company's time zone, shop-day start and first day of the week. Your app doesn't need any date code of its own, and every app's presets mean the same thing.

The dashboard gets these dates from the Shop Days API, the same rules that any other MachineMetrics client can call.

Basic Usage​

import {
useMMAppContext,
useMMAppSetting,
} from '@machinemetrics/mm-react-tools';

function MyWidget() {
useMMAppSetting('range', {
label: 'Date range',
type: 'dateRange',
options: ['today', 'yesterday', 'last7Days'],
default: 'today',
acceptGlobalOverride: 'dateRange',
});

const { range } = useMMAppContext();

if (!range) return <div>Loading...</div>;

return <PartCorrections start={range.start} end={range.end} />;
}

In the settings form, the setting is a single dropdown of the offered presets, in the order you list them, with the dashboard's own labels. The dashboard saves the preset key the user picks; if they leave the default, it saves none and the widget follows the default.

Your app reads the resolved range from useMMAppContext under the setting name. Pass start and end to time-based queries, treating end as exclusive:

query PartCorrections($start: timestamptz!, $end: timestamptz!) {
partCountCorrections(where: { effectiveAt: { _gte: $start, _lt: $end } }) {
effectiveAt
value
}
}

For APIs that take shop days, such as the Production API, use startDay and endDay:

import { useProductionQuery } from '@machinemetrics/mm-react-tools';

function PartsByMachine({ range }) {
const { data } = useProductionQuery(
JSON.stringify({
startDay: range.startDay,
endDay: range.endDay,
data: [{ metric: 'totalParts' }],
groupBy: [{ group: 'machine' }],
}),
null, // no refresh interval: query once per range
);
// ...
}

Properties​

PropertyTypeDescription
labelstringDisplay label for the setting
type"dateRange"Must be set to "dateRange"
optionsarrayPreset keys to offer, in display order. Any of the keys in Presets. Defaults to today, yesterday, last7Days, last30Days, weekToDate, monthToDate
defaultstringPreset used when nothing is saved. Must be one of the offered presets. Defaults to the first offered preset
acceptGlobalOverride"dateRange"Follow the dashboard date filter when one is set. See Following the Dashboard Date Filter
helperTextstringHelp text displayed below the setting

options is a list of preset keys, not { label, value } objects. Unknown keys are ignored with a console warning; if none of the keys is known, the default six are offered, so handle any preset your widget may receive. If default isn't one of the offered presets, the first offered preset is used instead, with a console warning once the options are known.

Don't name the setting dateRange: it collides with the dashboard's own widget property of that name. The names src, id, type, title, filter and dateRangeSettings are reserved for every setting type. A widget can declare up to 20 date range settings.

Value​

The app receives an object under the setting name:

const { range } = useMMAppContext();
// {
// preset: 'today',
// start: '2026-03-10T11:00:00.000Z',
// end: '2026-03-11T11:00:00.000Z',
// startDay: '2026-03-10',
// endDay: '2026-03-10',
// }

This example is for a company in America/Chicago whose shop day starts at 6:00 AM.

FieldTypeDescription
presetstringThe preset in effect: the saved one, the default, or the dashboard date filter's
startstringISO 8601 instant in UTC at which the range starts (inclusive)
endstringISO 8601 instant in UTC at which the range ends (exclusive)
startDaystringFirst shop day in the range, as yyyy-MM-dd in the company's time zone (inclusive)
endDaystringLast shop day in the range, as yyyy-MM-dd in the company's time zone (inclusive)

A few things to keep in mind:

  • start and end are always on shop-day boundaries. end closes the period, so it can be in the future: for Today, end is the start of the next shop day. If you need a range that stops at the current time, clamp end to now yourself.
  • startDay and endDay are both inclusive. For Today and Yesterday they are the same day.
  • On a dashboard, your widget receives none of its settings until the dates are resolved, so every setting is undefined at first. In the widget builder's preview, the other settings arrive at once and the range follows. Wait for the range rather than guessing.
  • The dashboard updates the value at each shop-day boundary, so a widget showing Today moves to the new day without a reload.
  • The dashboard resolves the setting from what was saved with the widget. A widget saved before your app declared the setting gets no value until it is edited and saved again, and changes to options or default reach a saved widget the same way.
  • There is no label in the value. If your widget shows the period's name, map preset to your own text.

When Dates Can't Be Loaded​

This applies to every date range setting, whether or not it follows the dashboard date filter. If the dashboard can't load its date ranges, for example while the service is briefly unavailable, it shows a message in place of the widget; in the widget builder's preview the message shows above your app instead. It tries again within a minute after a network error, a server error, or a timeout or rate limit (408, 429), and every 10 minutes after any other error, as well as whenever the page is shown again. Your widget isn't at fault and needs no handling for it.

Presets​

Every boundary is the company's shop-day start, in the company's time zone. Weeks start on the company's first day of the week, which is Monday unless the company is configured otherwise.

Each day uses the shop-day start in force on that day, so a range stays correct across daylight saving changes and across a change to the company's shop-day start. A shop day that starts before midnight, for example at 10:00 PM, is named by the date whose midnight it precedes: the shop day that starts at 10:00 PM on March 9 is 2026-03-10, and Month to date starts on the evening of the last day of the previous month.

KeyLabel in the dashboardRange
todayTodayThe current shop day
yesterdayYesterdayThe previous shop day
last7DaysLast 7 DaysToday plus the previous 6 shop days
last30DaysLast 30 DaysToday plus the previous 29 shop days
last90DaysLast 90 DaysToday plus the previous 89 shop days
lastWeekLast WeekThe previous complete week
last2WeeksLast 2 WeeksLast week plus this week, through the end of this week
lastMonthLast MonthThe previous complete calendar month
lastYearLast YearThe previous complete calendar year
weekToDateWeek To DateThe start of this week through the end of today's shop day
monthToDateMonth To DateThe start of this month through the end of today's shop day
quarterToDateQuarter To DateThe start of this quarter through the end of today's shop day
yearToDateYear To DateThe start of this year through the end of today's shop day

The "Last N days" and "to date" presets include all of today's shop day, so their end is the start of tomorrow's shop day.

Requirements​

The setting needs a dashboard and a playground that support it: the current dashboard, and mmdev-cli 1.1.0 or later for the playground. An older dashboard doesn't know the type: its widget builder can't edit a widget that declares one, and it may deliver the saved preset name instead of a resolved range. Release an app that declares a date range setting only once those hosts are available to your users. The playground can't preview acceptGlobalOverride or the "Not available" refusal, since it has no dashboard date filter.

Following the Dashboard Date Filter​

By default, a date range setting ignores the dashboard date filter and always uses the preset saved with the widget. Set acceptGlobalOverride: 'dateRange' to make the widget follow the filter instead:

  • When the dashboard date filter is set, the widget receives that range in place of its saved preset. preset tells you which one is in effect.
  • When the filter isn't set, the widget uses its saved preset.
  • If the filter's range isn't one of the setting's options, or is a custom range, the dashboard doesn't load the widget. It shows "Not available for <range>" in its place.

Offer every preset your widget can handle if you want it to work with as many filter choices as possible.

Migrating from a Select of Time Frames​

If your app already has a select setting of time frames that it turns into dates itself, move to a date range setting like this:

  1. Declare the date range under a new setting name. Don't reuse the old name: saved dashboards hold the old select's values under it.
  2. Read the new setting first. When it isn't set, fall back to the old setting's value and your existing date code. Existing widgets keep their old value until someone edits and saves them. On a dashboard, a widget that has been saved with the new setting gets no settings at all until its range is resolved, so the fallback doesn't run there first; in the builder's preview it can run briefly before the range arrives.
  3. Always prefer the new setting when it is set. Saved dashboards keep the old setting's value even after they are re-saved, so its presence doesn't mean the widget hasn't been migrated.
function useReportRange() {
const { range, timeRange } = useMMAppContext();

if (range) return { start: range.start, end: range.end };
if (timeRange) return resolveTimeRange(timeRange); // your existing date code
return undefined;
}

Once you're confident your users' dashboards have been re-saved, you can remove the old setting and the fallback.

Resolving Dates Outside a Widget​

If you need shop-day dates somewhere a widget setting can't supply them, such as a page of your app or a range your users type in, call the Shop Days API (POST /shop-days) instead of writing date code. It takes a signed-in user's token, or an API key or app token with the reporting or user scope. It is the same resolver the dashboard uses, and it can:

  • resolve any of the presets to start, end, startDay and endDay;
  • find the shop day that contains an instant;
  • turn an explicit startDay and endDay into exact start and end instants.

Every response also includes nextBoundary, the start of the next shop day, so you can refresh at the right moment, and the company's timezone. A preset name the server doesn't know fails the request with a 400 (UnknownPreset).

To build a range the presets don't cover, such as the last 14 days, look up today's shop day, do the calendar arithmetic on the dates, and resolve the result as an explicit range.

TypeScript​

mm-react-tools 5.3.0 or later includes the MMAppSettingConfig and DateRangeValue types and exports the preset lists DATE_RANGE_PRESETS (all presets) and DEFAULT_DATE_RANGE_PRESETS (offered when options is omitted). The setting itself works with earlier versions; it only needs a dashboard that supports it.