rider partner page

This commit is contained in:
2026-09-09 15:43:23 +05:30
parent 32c612a10d
commit b760c1a078
18 changed files with 1802 additions and 63 deletions

View File

@@ -2,7 +2,7 @@ import { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Button } from '@astryxdesign/core/Button';
import { Selector } from '@astryxdesign/core/Selector';
import { Route, UserCheck } from 'lucide-react';
import { Bike, Route, Truck, UserCheck } from 'lucide-react';
import { errorMessage } from '@/api/client';
import {
RIDER_MESSAGE,
@@ -10,9 +10,10 @@ import {
deliveriesApi,
} from '@/api/deliveries';
import type { OrderRow, TenantLocation } from '@/api/types';
import { useRiders } from '@/queries/hooks';
import { useAllPartners, useOwnTenant, useRiders } from '@/queries/hooks';
import { queryKeys } from '@/queries/keys';
import { buildDeliveries, riderName, riderVehicle } from './assignDelivery';
import { useBranchScope } from './BranchScope';
import { buildDeliveries, riderName, riderScope, riderVehicle } from './assignDelivery';
import { RoutePlanDrawer } from './RoutePlanDrawer';
import './pages/deliveries.css';
@@ -44,12 +45,38 @@ export function AssignBar({ orders, branchOf, assigned, onClear, onDone }: Assig
const [outcome, setOutcome] = useState<string | null>(null);
const [planning, setPlanning] = useState(false);
// Every order in a batch shares a region in practice — they are one branch's
// orders — so the first one carrying a region decides which fleet to ask for.
const region =
orders.map((row) => row.applocationid || branchOf(row)?.applocationid || 0).find(Boolean) ?? 0;
/**
* Which fleet these orders are handed to.
*
* Two real sources, and a merchant can have both: riders they hired
* themselves, and riders from the delivery partner they sit under. Asked as a
* question rather than merged into one list, because they are different
* people with different employers and an operator handing an order over
* should know which.
*
* The partner side only appears when the merchant HAS one — `tenants.partnerid`
* — so a shop delivering with its own riders never sees an empty second tab.
*/
const { tenantid } = useBranchScope();
const shop = useOwnTenant(tenantid || undefined);
const partnerid = Number((shop.data as unknown as Record<string, number>)?.['partnerid'] ?? 0);
const [source, setSource] = useState<'own' | 'partner'>(partnerid > 0 ? 'partner' : 'own');
const riders = useRiders(region || undefined);
// The partner's NAME on the tab, not "Partner riders". An operator handing an
// order to Xpress-Cbe-Main should read that, not a category.
const partners = useAllPartners();
const partnerName =
partners.data.find((entry) => entry.partnerid === partnerid)?.partnername ?? '';
/*
Scoped by owner, never by city.
It asked `getriders` with the region alone, which means "who is on duty in
Coimbatore" — 82 riders, almost none of them this shop's to use. Measured
2026-09-09: 117 of the platform's 118 riders belong to a partner, so region
scope was quietly offering other companies' fleets.
*/
const riders = useRiders(riderScope({ tenantid, partnerid, source }));
const fleet = riders.data ?? [];
const rider = fleet.find((entry) => String(entry.userid) === riderId);
@@ -110,6 +137,38 @@ export function AssignBar({ orders, branchOf, assigned, onClear, onDone }: Assig
{orders.length} selected
</span>
{/* Only when there is a choice to make. A shop with no partner has one
source, and a toggle with one option is a control that asks a question
it already knows the answer to. */}
{partnerid > 0 ? (
<div className="assign-source" role="group" aria-label="Which riders to offer">
<button
type="button"
className="assign-source-btn"
aria-pressed={source === 'own'}
onClick={() => {
setSource('own');
setRiderId('');
}}
>
<Bike size={13} />
Own riders
</button>
<button
type="button"
className="assign-source-btn"
aria-pressed={source === 'partner'}
onClick={() => {
setSource('partner');
setRiderId('');
}}
>
<Truck size={13} />
{partnerName || 'Partner riders'}
</button>
</div>
) : null}
<div className="assign-bar-picker">
<Selector
label="Choose a rider"
@@ -127,14 +186,19 @@ export function AssignBar({ orders, branchOf, assigned, onClear, onDone }: Assig
rather than being flattened to "Select…". "No riders on duty" is a
shift that has not started; the region case is a branch somebody
has to configure. Neither is a fault in the orders. */
/* Four situations, and the placeholder names which one — an operator
looking at an empty list needs to know whether to wait, to hire, or
to ask for a partner. */
placeholder={
region === 0
? 'No delivery region on this branch'
: riders.isLoading
? 'Loading riders…'
: options.length > 0
? 'Select a rider…'
: 'No rider has clocked on today'
riders.isLoading
? 'Loading riders…'
: options.length > 0
? 'Select a rider…'
: source === 'partner'
? `No ${partnerName || 'partner'} rider has clocked on today`
: partnerid > 0
? 'This shop has no riders of its own'
: 'No rider has clocked on today'
}
/>
</div>