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
| Property | Type | Description |
|---|---|---|
label | string | Display label for the setting |
type | "dateRange" | Must be set to "dateRange" |
options | array | Preset keys to offer, in display order. Any of the keys in Presets. Defaults to today, yesterday, last7Days, last30Days, weekToDate, monthToDate |
default | string | Preset 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 |
helperText | string | Help 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.
| Field | Type | Description |
|---|---|---|
preset | string | The preset in effect: the saved one, the default, or the dashboard date filter's |
start | string | ISO 8601 instant in UTC at which the range starts (inclusive) |
end | string | ISO 8601 instant in UTC at which the range ends (exclusive) |
startDay | string | First shop day in the range, as yyyy-MM-dd in the company's time zone (inclusive) |
endDay | string | Last shop day in the range, as yyyy-MM-dd in the company's time zone (inclusive) |
A few things to keep in mind:
startandendare always on shop-day boundaries.endcloses the period, so it can be in the future: for Today,endis the start of the next shop day. If you need a range that stops at the current time, clampendto now yourself.startDayandendDayare 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
undefinedat 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
optionsordefaultreach a saved widget the same way. - There is no label in the value. If your widget shows the period's name, map
presetto 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.
| Key | Label in the dashboard | Range |
|---|---|---|
today | Today | The current shop day |
yesterday | Yesterday | The previous shop day |
last7Days | Last 7 Days | Today plus the previous 6 shop days |
last30Days | Last 30 Days | Today plus the previous 29 shop days |
last90Days | Last 90 Days | Today plus the previous 89 shop days |
lastWeek | Last Week | The previous complete week |
last2Weeks | Last 2 Weeks | Last week plus this week, through the end of this week |
lastMonth | Last Month | The previous complete calendar month |
lastYear | Last Year | The previous complete calendar year |
weekToDate | Week To Date | The start of this week through the end of today's shop day |
monthToDate | Month To Date | The start of this month through the end of today's shop day |
quarterToDate | Quarter To Date | The start of this quarter through the end of today's shop day |
yearToDate | Year To Date | The 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.
presettells 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:
- 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.
- 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.
- 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,startDayandendDay; - find the shop day that contains an instant;
- turn an explicit
startDayandendDayinto exactstartandendinstants.
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.