delivery slot ui

This commit is contained in:
2026-10-08 15:09:36 +05:30
parent 262b83ffbc
commit 8960fb8ebb
3 changed files with 107 additions and 73 deletions

View File

@@ -1,12 +1,12 @@
import { useState, useMemo } from 'react';
import {
WINDOW_FILTERS,
matchesWindow,
windowHasPassed,
windowHours,
windowLabel,
type WindowFilterKey,
} from '../deliveryWindow';
import { WindowFilterSelect } from '../WindowFilterSelect';
import { useDateScope } from '@/components/shell/DateScope';
import { CheckboxInput } from '@astryxdesign/core/CheckboxInput';
import { Card } from '@astryxdesign/core/Card';
@@ -203,14 +203,17 @@ export function SalesPage() {
matchesStatus(status, orderStage(row, stages).status) &&
matchesWindow(windowFilter, row),
),
[allOrders, status, stages],
// windowFilter belongs here. Left out, this memo never recomputed when the
// dropdown changed, so the control moved and the table did not — a filter
// that silently does nothing.
[allOrders, status, stages, windowFilter],
);
const deliveryRows = useMemo(
() =>
allDeliveries.filter(
(row) => matchesStatus(status, row.orderstatus) && matchesWindow(windowFilter, row),
),
[allDeliveries, status],
[allDeliveries, status, windowFilter],
);
const billRows = useMemo(() => {
@@ -387,12 +390,20 @@ export function SalesPage() {
/* Tabs left, search right, on ONE row — see `isTabsInline`. */
isTabsInline
actions={
<SearchInput
label="Search sales"
value={keyword}
onChange={setKeyword}
placeholder={SEARCH_HINT[tab]}
/>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
{/* Not on the counter tab. Imported till bills are not deliveries
and can never carry a window, so the control would be one that
does nothing on a third of this page. */}
{tab === 'counter' ? null : (
<WindowFilterSelect value={windowFilter} onChange={setWindowFilter} />
)}
<SearchInput
label="Search sales"
value={keyword}
onChange={setKeyword}
placeholder={SEARCH_HINT[tab]}
/>
</div>
}
tabs={
/* THREE views, not four.
@@ -477,10 +488,6 @@ export function SalesPage() {
counts={tabCounts}
onStatus={setStatus}
/>
{/* Beside the status tabs, not inside them. Both filters apply at
once, because "evening orders still pending" is the question
somebody actually has. */}
<WindowFilter value={windowFilter} onChange={setWindowFilter} />
{/* Above the table, not below it and not in a drawer.
Assigning is something you do WHILE reading the day's orders —
glancing between the drop addresses and who is free — so the
@@ -1528,39 +1535,3 @@ function WindowCell({ row }: { row: OrderRow | DeliveryRow }) {
);
}
/**
* Which window to show.
*
* "No window" earns its place: it is how an operator finds orders placed
* without choosing one, which is most of them while this rolls out, and is the
* population worth watching to see the feature being used at all.
*/
function WindowFilter({
value,
onChange,
}: {
value: WindowFilterKey;
onChange: (next: WindowFilterKey) => void;
}) {
return (
<select
value={value}
onChange={(event) => onChange(event.target.value as WindowFilterKey)}
aria-label="Filter by delivery window"
style={{
padding: '6px 10px',
fontSize: 12.5,
border: '1px solid var(--color-line, #d7dce5)',
borderRadius: 7,
background: 'var(--color-surface, #fff)',
color: 'var(--color-ink-1)',
}}
>
{WINDOW_FILTERS.map((option) => (
<option key={option.key} value={option.key}>
{option.label}
</option>
))}
</select>
);
}