ui/ux improvement
This commit is contained in:
@@ -28,39 +28,85 @@ import {
|
||||
|
||||
export interface DateScopeValue {
|
||||
preset: RangePreset;
|
||||
/**
|
||||
* What the PAGES read — always a real window.
|
||||
*
|
||||
* When nobody has picked anything this is the default week, not an empty
|
||||
* object. Left empty, every read on the console became unbounded and each
|
||||
* part of a page bounded it differently: the Console's chart drew however
|
||||
* many days the last 500 orders happened to span (nine, in practice) while
|
||||
* the KPI tiles beside it totalled all of them, and the counter figures came
|
||||
* from a POS summary with no window at all. One page, three answers.
|
||||
*/
|
||||
range: DateRange;
|
||||
/**
|
||||
* What the PICKER shows — empty until somebody chooses.
|
||||
*
|
||||
* This is the half that keeps the filter unselected on arrival. The two are
|
||||
* separate on purpose: "no filter has been set" is a fact about the control,
|
||||
* and "which week are we looking at" is a fact about the data, and collapsing
|
||||
* them into one value is what forced a choice between a filter nobody set and
|
||||
* a page with no window.
|
||||
*/
|
||||
chosen: DateRange;
|
||||
set: (preset: RangePreset, range: DateRange) => void;
|
||||
/** Back to no filter at all — what an empty state offers as a way out. */
|
||||
clear: () => void;
|
||||
/** True when a range is actually narrowing the page. */
|
||||
/** True when the USER has narrowed the page, not merely that a window exists. */
|
||||
isFiltered: boolean;
|
||||
}
|
||||
|
||||
/** Where the console starts. Month to date is what most pages want. */
|
||||
export const DEFAULT_RANGE_PRESET: RangePreset = 'month';
|
||||
/**
|
||||
* The window the console shows when nobody has picked one.
|
||||
*
|
||||
* A WEEK — while the picker still shows nothing selected. Those are two
|
||||
* separate statements and both are deliberate.
|
||||
*
|
||||
* The filter opens unselected because the console used to narrow every page
|
||||
* before anyone asked: somebody signing in to see how trade is going was shown
|
||||
* a slice with no sign that it was one, and the range then followed them across
|
||||
* the whole console.
|
||||
*
|
||||
* But unselected must not mean UNBOUNDED. With no dates at all the reads were
|
||||
* capped only by `pagesize`, and each part of a page then bounded itself
|
||||
* differently — the Console's chart drew however many days the last 500 orders
|
||||
* happened to span (nine), the KPI tiles beside it totalled all of those
|
||||
* orders, and the counter figures came from a POS summary with no window
|
||||
* whatsoever. One page, three different answers to "when".
|
||||
*
|
||||
* A default window is not a filter. Nothing is hidden from the reader, the
|
||||
* control is empty, and one choice replaces it.
|
||||
*/
|
||||
const DEFAULT_WINDOW: RangePreset = 'week';
|
||||
|
||||
const DateScopeContext = createContext<DateScopeValue | null>(null);
|
||||
|
||||
export function DateScopeProvider({ children }: { children: ReactNode }) {
|
||||
const [preset, setPreset] = useState<RangePreset>(DEFAULT_RANGE_PRESET);
|
||||
const [range, setRange] = useState<DateRange>(() => presetRange(DEFAULT_RANGE_PRESET));
|
||||
/* What the user picked. Empty until they do — this is what the picker shows. */
|
||||
const [preset, setPreset] = useState<RangePreset>('custom');
|
||||
const [chosen, setChosen] = useState<DateRange>({});
|
||||
|
||||
const value = useMemo<DateScopeValue>(
|
||||
() => ({
|
||||
const value = useMemo<DateScopeValue>(() => {
|
||||
const hasChoice = Boolean(chosen.fromdate || chosen.todate);
|
||||
return {
|
||||
preset,
|
||||
range,
|
||||
chosen,
|
||||
/* The window the pages actually read: what was picked, or the default
|
||||
week. Resolved here, once, so every read on a page shares it — the
|
||||
orders, the POS summaries and the chart cannot end up describing
|
||||
different spans. */
|
||||
range: hasChoice ? chosen : presetRange(DEFAULT_WINDOW),
|
||||
set: (nextPreset, nextRange) => {
|
||||
setPreset(nextPreset);
|
||||
setRange(nextRange);
|
||||
setChosen(nextRange);
|
||||
},
|
||||
clear: () => {
|
||||
setPreset('custom');
|
||||
setRange({});
|
||||
setChosen({});
|
||||
},
|
||||
isFiltered: Boolean(range.fromdate || range.todate),
|
||||
}),
|
||||
[preset, range],
|
||||
);
|
||||
isFiltered: hasChoice,
|
||||
};
|
||||
}, [preset, chosen]);
|
||||
|
||||
return <DateScopeContext.Provider value={value}>{children}</DateScopeContext.Provider>;
|
||||
}
|
||||
@@ -81,5 +127,5 @@ export function useDateScope(): DateScopeValue {
|
||||
/** The control itself. Rendered once, in the top bar. */
|
||||
export function DateScopePicker() {
|
||||
const dates = useDateScope();
|
||||
return <DateRangePicker preset={dates.preset} range={dates.range} onChange={dates.set} />;
|
||||
return <DateRangePicker range={dates.chosen} onChange={dates.set} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user