redesign
This commit is contained in:
@@ -30,6 +30,7 @@ import { assignability, assignedFrom, type Assignability } from '../assignDelive
|
||||
import { useCustomers, useDeliveries, useOrders, useRiders, useTenantLocations } from '@/queries/hooks';
|
||||
import { useDateScope } from '@/components/shell/DateScope';
|
||||
import { useBranchScope } from '../BranchScope';
|
||||
import { canAssign, scopeBranches, scopeLabel, scopeQuery, type DispatchScope } from '../dispatchScope';
|
||||
import { count, money, moneyExact } from '../format';
|
||||
import { DELIVERY_STATUS, statusColor } from '../orderStatus';
|
||||
import { ActivePanel } from '../ActivePanel';
|
||||
@@ -65,8 +66,25 @@ type Board = ViewMode | 'timing' | 'active' | 'profit';
|
||||
const GROUPINGS = new Set<Board>(['riders', 'stores', 'customers']);
|
||||
const isGrouping = (board: Board): board is ViewMode => GROUPINGS.has(board);
|
||||
|
||||
/**
|
||||
* The shop's own dispatch — a thin wrapper that reads the branch scope from
|
||||
* context and hands the board a tenant scope.
|
||||
*
|
||||
* The board itself knows nothing about logins. That is what lets the Nearle
|
||||
* Admin render the same component against a partner instead, and why the
|
||||
* platform board cannot reach a shop's own riders: there is no field on the
|
||||
* partner scope to put a tenantid in.
|
||||
*/
|
||||
export function DispatchPage() {
|
||||
const { branches, selected, tenantid } = useBranchScope();
|
||||
return (
|
||||
<DispatchBoard
|
||||
scope={{ kind: 'tenant', tenantid, locationid: selected ?? undefined, branches }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function DispatchBoard({ scope }: { scope: DispatchScope }) {
|
||||
const dates = useDateScope();
|
||||
const [mode, setMode] = useState<Board>('riders');
|
||||
const [focused, setFocused] = useState<string | null>(null);
|
||||
@@ -74,16 +92,23 @@ export function DispatchPage() {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState<'all' | 'in_progress' | 'delivered' | 'unassigned'>('all');
|
||||
|
||||
const query = tenantid
|
||||
? { tenantid, locationid: selected ?? undefined, ...dates.range, pagesize: 200 }
|
||||
: undefined;
|
||||
/* One id or the other, never both — see `DispatchScope`. */
|
||||
const isPartnerBoard = scope.kind === 'partner';
|
||||
const tenantid = scope.kind === 'tenant' ? scope.tenantid : 0;
|
||||
const branches = scopeBranches(scope);
|
||||
|
||||
const query =
|
||||
isPartnerBoard || tenantid ? scopeQuery(scope, dates.range) : undefined;
|
||||
|
||||
const deliveriesQuery = useDeliveries(query);
|
||||
const ordersQuery = useOrders(query);
|
||||
const locationsQuery = useTenantLocations(tenantid || undefined);
|
||||
/* The customer book is a tenant's own list, so a partner board has none —
|
||||
and grouping a partner's rounds by customer would mean reading every
|
||||
merchant's book to name them. The tab is not offered there. */
|
||||
const customers = useCustomers(
|
||||
tenantid && mode === 'customers'
|
||||
? { tenantid, locationid: selected ?? undefined, pagesize: 500 }
|
||||
? { tenantid, locationid: scope.kind === 'tenant' ? scope.locationid : undefined, pagesize: 500 }
|
||||
: undefined,
|
||||
);
|
||||
|
||||
@@ -228,14 +253,19 @@ export function DispatchPage() {
|
||||
<span className="sbt-badge">{branches.length}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`sbt ${mode === 'customers' ? 'active' : ''}`}
|
||||
onClick={() => changeMode('customers')}
|
||||
>
|
||||
<span className="sbt-icon"><Users size={15} /></span>
|
||||
<span>By Customer</span>
|
||||
</button>
|
||||
{/* A shop's customer book is its own. Grouping a partner's rounds by
|
||||
customer would mean reading every merchant's book to put a name to
|
||||
a phone number, so the tab is not offered on a partner board. */}
|
||||
{scope.kind === 'tenant' ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`sbt ${mode === 'customers' ? 'active' : ''}`}
|
||||
onClick={() => changeMode('customers')}
|
||||
>
|
||||
<span className="sbt-icon"><Users size={15} /></span>
|
||||
<span>By Customer</span>
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@@ -395,7 +425,7 @@ export function DispatchPage() {
|
||||
mode={mode}
|
||||
onOpen={setDetail}
|
||||
locations={locationsQuery.data ?? branches}
|
||||
{...(open.id === UNASSIGNED
|
||||
{...(open.id === UNASSIGNED && canAssign(scope)
|
||||
? {
|
||||
selection: picked,
|
||||
verdictOf: (row: OrderRow) => assignability(row, branchOf(row), assigned),
|
||||
@@ -442,8 +472,8 @@ export function DispatchPage() {
|
||||
range is selected, which is usually history. */}
|
||||
<GroupMap
|
||||
stops={stops}
|
||||
groupName={dates.range.fromdate ? `Dispatch ${dates.range.fromdate}` : 'All Rounds'}
|
||||
emptyHint="Select a date range with active deliveries to view live GPS trails."
|
||||
groupName={scopeLabel(scope)}
|
||||
emptyHint="Change the date range in the top bar — the board opens on the current month, and a quiet month has nothing to draw."
|
||||
locations={locationsQuery.data ?? branches}
|
||||
/>
|
||||
|
||||
@@ -509,7 +539,7 @@ export function DispatchPage() {
|
||||
<OrderDetailDrawer row={detail.row} kind={detail.kind} onClose={() => setDetail(null)} />
|
||||
) : null}
|
||||
|
||||
{isAutoOpen ? (
|
||||
{isAutoOpen && canAssign(scope) ? (
|
||||
<AutoAssignDrawer
|
||||
orders={waiting}
|
||||
branches={branches}
|
||||
|
||||
Reference in New Issue
Block a user