import React, { useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import dayjs from 'dayjs'; import { Ban, Bell, Bike, Car, CheckCircle2, CircleSlash, Pencil, Plus, Radio, Users, } from 'lucide-react'; import { Avatar, Button, ConfirmModal, DataTable, Drawer, EmptyState, Field, Grid, IconButton, Input, KpiCard, Modal, PageHeader, SearchInput, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Stack, StatusBadge, Surface, Tabs, Textarea, ZoneSelector, } from '@/components/ds'; import { useAssignMilerVehicle, useBlockMiler, useMilerLogs, useMilers, useNotifyMiler, useRiderSummaryCounts, useVehicles, } from '@/lib/doormileHooks'; import { useZone } from '@/lib/ZoneContext'; import { formatDoormileTimestamp } from '@/lib/doormileTimestamp'; import { currency, matchesQuery, orDash, useDebouncedValue } from '@/lib/doormileFormat'; /** * Riders — the milers who actually carry the parcels. * * Two identifiers matter here and they are not interchangeable: `userid` is * what a booking's assignment records and what `assign-miler` wants, while * `milerprofileid` is the primary key of this resource and what block, notify, * assign-vehicle and logs all key on. Passing one where the other belongs 404s. * * The rider-substitution feature from the source console is deliberately absent: * it has no backend endpoint at all, so there is nothing here to build against. */ /** "Active" means neither offline nor blocked — the same rule the counts use. */ const isActive = (rider) => !['offline', 'blocked'].includes(String(rider?.availabilitystatus || '').toLowerCase()); const riderName = (rider) => rider?.displayname || rider?.authname || '—'; /** A bare `HH:mm:ss` from the shift record, rendered as a clock time. */ const shiftTime = (value) => value ? dayjs(`${dayjs().format('YYYY-MM-DD')} ${value}`).format('hh:mm A') : '—'; const TABS = [ { value: 'all', label: 'All riders' }, { value: 'active', label: 'Active' }, { value: 'blocked', label: 'Blocked' }, ]; export default function Riders() { const navigate = useNavigate(); const { data: riders = [], isLoading, isFetching } = useMilers(); const { data: summary } = useRiderSummaryCounts(); const { data: vehicles = [] } = useVehicles(); const [search, setSearch] = useState(''); const debouncedSearch = useDebouncedValue(search); const [tab, setTab] = useState('all'); const [blockRow, setBlockRow] = useState(null); const [vehicleRow, setVehicleRow] = useState(null); const [selectedVehicle, setSelectedVehicle] = useState(''); const [notifyRow, setNotifyRow] = useState(null); const [notice, setNotice] = useState({ title: 'DoormileXpress', message: '' }); const [logsRow, setLogsRow] = useState(null); const blockMiler = useBlockMiler(); const assignVehicle = useAssignMilerVehicle(); const notifyMiler = useNotifyMiler(); const { selectedZone, isAllZones, matchesZone } = useZone(); const zoneRiders = useMemo(() => { return (riders || []).filter(matchesZone); }, [riders, matchesZone]); const counts = useMemo(() => { const list = zoneRiders; return { all: list.length, active: list.filter(isActive).length, blocked: list.filter((rider) => String(rider.availabilitystatus || '').toLowerCase() === 'blocked').length, }; }, [zoneRiders]); const rows = useMemo(() => { let list = zoneRiders; if (tab === 'active') list = list.filter(isActive); else if (tab === 'blocked') { list = list.filter((rider) => String(rider.availabilitystatus || '').toLowerCase() === 'blocked'); } return list.filter((rider) => matchesQuery(rider, [riderName, 'phone', 'userid', 'city', 'suburb', 'defaultvehicletype'], debouncedSearch) ); }, [zoneRiders, tab, debouncedSearch]); const columns = useMemo( () => [ { key: 'rider', header: 'Miler', sortable: true, accessor: riderName, cell: (row) => (
{riderName(row)}
{orDash(row.phone)} · #{row.userid}
{row.suburb || row.address || '—'}
{row.city ?{row.city}
: null}{rows.length === (counts[tab] ?? counts.all) ? `${counts[tab] ?? counts.all} milers` : `${rows.length} of ${counts[tab] ?? counts.all} milers`}
Loading logs…
) : !logs || logs.length === 0 ? ({orDash(log.status ?? log.availabilitystatus ?? log.event)}
{log.latitude ?? log.currentlatitude ?? '—'}, {log.longitude ?? log.currentlongitude ?? '—'}
{formatDoormileTimestamp(log.logdate ?? log.lastpingat ?? log.createdat, 'DD MMM, hh:mm A')}