updates on the ui changes

This commit is contained in:
2026-09-19 15:50:02 +05:30
parent 3e2e9cad3a
commit 7992208361
18 changed files with 243 additions and 263 deletions

View File

@@ -150,14 +150,6 @@ export default function AppUsers() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New user
</Button>
}
/>
<Grid cols={3}> <Grid cols={3}>
<KpiCard <KpiCard
icon={Users} icon={Users}
@@ -194,6 +186,11 @@ export default function AppUsers() {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search by name, email, phone or role" placeholder="Search by name, email, phone or role"
count={{ shown: rows.length, total: users.length, noun: 'users' }} count={{ shown: rows.length, total: users.length, noun: 'users' }}
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New user
</Button>
}
/> />
<DataTable <DataTable

View File

@@ -421,14 +421,6 @@ export default function Bookings() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button variant="outline" onClick={() => refetch()} loading={isFetching}>
<RefreshCw className="mr-1.5 h-4 w-4" /> Refresh
</Button>
}
/>
{isError && <Alert tone="danger">{messageOf(error, 'Could not load bookings')}</Alert>} {isError && <Alert tone="danger">{messageOf(error, 'Could not load bookings')}</Alert>}
{scan?.truncated && ( {scan?.truncated && (
@@ -466,6 +458,11 @@ export default function Bookings() {
placeholder="Search booking no, address or status" placeholder="Search booking no, address or status"
onExport={() => exportRows(rows, EXPORT_COLUMNS, `customer-bookings-${tab}`)} onExport={() => exportRows(rows, EXPORT_COLUMNS, `customer-bookings-${tab}`)}
exportDisabled={rows.length === 0} exportDisabled={rows.length === 0}
actions={
<Button variant="outline" onClick={() => refetch()} loading={isFetching}>
<RefreshCw className="mr-1.5 h-4 w-4" /> Refresh
</Button>
}
/> />
</div> </div>
</div> </div>

View File

@@ -405,14 +405,6 @@ export default function Tenants() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={() => navigate('/doormile/clients/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New client
</Button>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard icon={Building2} value={stats.total} label="Total clients" tone="brand" index={0} loading={isLoading} /> <KpiCard icon={Building2} value={stats.total} label="Total clients" tone="brand" index={0} loading={isLoading} />
<KpiCard <KpiCard
@@ -464,6 +456,9 @@ export default function Tenants() {
placeholder="Search by client name, email or phone" placeholder="Search by client name, email or phone"
className="w-full sm:w-72" className="w-full sm:w-72"
/> />
<Button onClick={() => navigate('/doormile/clients/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New client
</Button>
</div> </div>
</div> </div>

View File

@@ -180,16 +180,13 @@ export default function CompetitiveIntel() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader <Stack space="sm">
actions={ <div className="flex flex-wrap items-center justify-between gap-3 w-full">
<Tabs tabs={TABS} value={tab} onChange={changeTab} />
<Button onClick={openCreate}> <Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> {onBranches ? 'New branch' : 'New rate'} <Plus className="mr-1.5 h-4 w-4" /> {onBranches ? 'New branch' : 'New rate'}
</Button> </Button>
} </div>
/>
<Stack space="sm">
<Tabs tabs={TABS} value={tab} onChange={changeTab} />
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p className="text-caption text-ink-3"> <p className="text-caption text-ink-3">

View File

@@ -141,16 +141,6 @@ export default function Customers({ embedded = false }) {
return ( return (
<Stack space="lg"> <Stack space="lg">
{!embedded && (
<PageHeader
actions={
<Button onClick={() => navigate('/doormile/customer/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New customer
</Button>
}
/>
)}
<Grid cols={4}> <Grid cols={4}>
<KpiCard <KpiCard
icon={Users} icon={Users}
@@ -195,6 +185,11 @@ export default function Customers({ embedded = false }) {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search by name, phone or email" placeholder="Search by name, phone or email"
count={{ shown: rows.length, total: customers.length, noun: 'customers' }} count={{ shown: rows.length, total: customers.length, noun: 'customers' }}
actions={
<Button onClick={() => navigate('/doormile/customer/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New customer
</Button>
}
/> />
<DataTable <DataTable

View File

@@ -1,12 +1,11 @@
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { Ban, Bell, Bike, FileSpreadsheet, PackageSearch, Truck } from 'lucide-react'; import { Ban, Bell, Bike, Clock, FileSpreadsheet, PackageSearch, Truck } from 'lucide-react';
import { import {
Alert, Button, DataTable, DatePicker, Drawer, EmptyState, Field, IconButton, Alert, Button, DataTable, DatePicker, Drawer, EmptyState, Field, IconButton,
PageHeader, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Modal, PageHeader, SearchInput, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Modal,
Stack, StatusBadge, Surface, Tabs, Textarea, ZoneSelector, Stack, StatusBadge, Surface, Tabs, Textarea, ZoneSelector,
} from '@/components/ds'; } from '@/components/ds';
import { ListToolbar } from '@/components/doormile/ListToolbar';
import { import {
useBooking, useCancelDelivery, useChangeDeliveryRider, useDeliveries, useBooking, useCancelDelivery, useChangeDeliveryRider, useDeliveries,
useDeliveryCounts, useNotifyMiler, useRiderOptions, useTenants, useDeliveryCounts, useNotifyMiler, useRiderOptions, useTenants,
@@ -390,30 +389,24 @@ export default function Deliveries() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<DatePicker
value={selectedDate}
onChange={(d) => {
if (d) {
const s = dayjs(d).format('YYYY-MM-DD');
setDateParams({ from: s, to: s });
setSelectedDate(s);
}
}}
/>
}
/>
<Stack space="sm"> <Stack space="sm">
<Tabs tabs={tabs} value={tab} onChange={setTab} /> {/* Row 1: Tabs on left, DatePicker & Export on right */}
<div className="flex flex-wrap items-center justify-between gap-3 w-full">
<div className="no-scrollbar -mx-1 max-w-full min-w-0 overflow-x-auto px-1 py-0.5">
<Tabs tabs={tabs} value={tab} onChange={setTab} className="shrink-0" />
</div>
<ListToolbar <div className="flex items-center gap-2 shrink-0">
search={search} <DatePicker
onSearchChange={setSearch} value={selectedDate}
placeholder="Search by order, client, miler, customer or address" onChange={(d) => {
count={{ shown: rows.length, total: batchRows.length, noun: 'deliveries' }} if (d) {
actions={ const s = dayjs(d).format('YYYY-MM-DD');
setDateParams({ from: s, to: s });
setSelectedDate(s);
}
}}
/>
<Button <Button
variant="ghost" variant="ghost"
onClick={() => exportRows(rows, exportColumns, `deliveries-${tab}-${dateParams.from || 'all'}`)} onClick={() => exportRows(rows, exportColumns, `deliveries-${tab}-${dateParams.from || 'all'}`)}
@@ -421,50 +414,97 @@ export default function Deliveries() {
> >
<FileSpreadsheet className="mr-1.5 h-4 w-4" /> Export <FileSpreadsheet className="mr-1.5 h-4 w-4" /> Export
</Button> </Button>
} </div>
> </div>
<ZoneSelector className="w-full sm:w-auto" />
<Select value={batch} onValueChange={setBatch}>
<SelectTrigger className="w-full sm:w-52">
<SelectValue />
</SelectTrigger>
<SelectContent>
{BATCH_OPTIONS.map((option) => (
<SelectItem key={option.id} value={option.id}>
{option.label} · {option.range}
</SelectItem>
))}
</SelectContent>
</Select>
<Select value={tenantId} onValueChange={setTenantId}> {/* Row 2: Search, Zone, Batch, Clients, Milers, and Count */}
<SelectTrigger className="w-full sm:w-48"> <div className="flex flex-wrap items-center justify-between gap-3 w-full">
<SelectValue placeholder="All clients" /> <div className="flex flex-1 flex-wrap items-center gap-2.5 min-w-0">
</SelectTrigger> <SearchInput
<SelectContent> value={search}
<SelectItem value={ALL}>All clients</SelectItem> onChange={setSearch}
{(tenants || []).map((tenant) => ( placeholder="Search by order, client, miler, customer..."
<SelectItem key={tenant.tenantid} value={String(tenant.tenantid)}> className="w-full sm:w-64 lg:w-72"
{tenant.tenantname || `Tenant #${tenant.tenantid}`} />
</SelectItem> <ZoneSelector className="w-full sm:w-auto shrink-0" />
))}
</SelectContent>
</Select>
<Select value={riderId} onValueChange={setRiderId}> <Select value={batch} onValueChange={setBatch}>
<SelectTrigger className="w-full sm:w-48"> <SelectTrigger className="w-full sm:w-auto min-w-[210px] h-10">
<SelectValue placeholder="All milers" /> <div className="flex items-center gap-2 min-w-0">
</SelectTrigger> <Clock className="h-3.5 w-3.5 text-ink-3 shrink-0" />
<SelectContent> <SelectValue />
<SelectItem value={ALL}>All milers</SelectItem> </div>
{(riders || []).map((rider) => ( </SelectTrigger>
<SelectItem key={rider.userid} value={String(rider.userid)}> <SelectContent className="w-[300px] p-1.5 shadow-lg rounded-xl">
{rider.label} {BATCH_OPTIONS.map((option) => (
</SelectItem> <SelectItem
))} key={option.id}
</SelectContent> value={option.id}
</Select> textValue={option.id === 'all' ? 'All batches' : `${option.label} · ${option.range}`}
</ListToolbar> className="py-2 pl-2.5 pr-8 rounded-lg cursor-pointer my-0.5"
>
<div className="flex items-center justify-between gap-3 w-full">
<span className="font-medium text-ink-1 text-sm">{option.label}</span>
<span className="text-[11px] font-medium px-2 py-0.5 rounded-md bg-surface-sunken text-ink-3 border border-border/50 shrink-0">
{option.range}
</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
<Select value={tenantId} onValueChange={setTenantId}>
<SelectTrigger className="w-full sm:w-44">
<SelectValue placeholder="All clients" />
</SelectTrigger>
<SelectContent>
<SelectItem value={ALL}>All clients</SelectItem>
{(tenants || []).map((tenant) => (
<SelectItem key={tenant.tenantid} value={String(tenant.tenantid)}>
{tenant.tenantname || `Tenant #${tenant.tenantid}`}
</SelectItem>
))}
</SelectContent>
</Select>
<Select value={riderId} onValueChange={setRiderId}>
<SelectTrigger className="w-full sm:w-44">
<SelectValue placeholder="All milers" />
</SelectTrigger>
<SelectContent>
<SelectItem value={ALL}>All milers</SelectItem>
{(riders || []).map((rider) => (
<SelectItem key={rider.userid} value={String(rider.userid)}>
{rider.label}
</SelectItem>
))}
</SelectContent>
</Select>
{isFiltered && (
<Button
variant="ghost"
size="sm"
onClick={() => {
setSearch('');
setBatch('all');
setTenantId(ALL);
setRiderId(ALL);
}}
className="text-xs h-9 text-brand hover:text-brand-hover hover:bg-brand-soft"
>
Clear filters
</Button>
)}
</div>
<div className="text-caption text-ink-3 shrink-0">
{rows.length === batchRows.length
? `${batchRows.length} ${batchRows.length === 1 ? 'delivery' : 'deliveries'}`
: `${rows.length} of ${batchRows.length} deliveries`}
</div>
</div>
<DataTable <DataTable
columns={columns} columns={columns}

View File

@@ -209,14 +209,6 @@ export default function Exceptions() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> Log exception
</Button>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard <KpiCard
icon={ClipboardList} icon={ClipboardList}
@@ -262,6 +254,11 @@ export default function Exceptions() {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search by consignment, type or description" placeholder="Search by consignment, type or description"
count={{ shown: rows.length, total: exceptions.length, noun: 'exceptions' }} count={{ shown: rows.length, total: exceptions.length, noun: 'exceptions' }}
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> Log exception
</Button>
}
/> />
<DataTable <DataTable

View File

@@ -198,14 +198,6 @@ export default function Hubs() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New hub
</Button>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard <KpiCard
icon={Warehouse} icon={Warehouse}
@@ -251,6 +243,11 @@ export default function Hubs() {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search hubs, addresses or pincodes" placeholder="Search hubs, addresses or pincodes"
count={{ shown: rows.length, total: hubs.length, noun: 'hubs' }} count={{ shown: rows.length, total: hubs.length, noun: 'hubs' }}
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New hub
</Button>
}
/> />
<DataTable <DataTable

View File

@@ -437,24 +437,12 @@ export default function Orders() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<>
<Button variant="outline" onClick={() => navigate('/doormile/orders/createorders')}>
<Upload className="mr-1.5 h-4 w-4" /> Bulk upload
</Button>
<Button onClick={() => navigate('/doormile/orders/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New order
</Button>
</>
}
/>
<Stack space="sm"> <Stack space="sm">
{/* Both halves used to be `shrink-0` in a non-wrapping row, which came to {/* Both halves used to be `shrink-0` in a non-wrapping row, which came to
1049px of content — on a 375px phone the whole document scrolled 1049px of content — on a 375px phone the whole document scrolled
sideways rather than this strip. The row now wraps, and the tab strip sideways rather than this strip. The row now wraps, and the tab strip
scrolls inside its own track, so every tab stays reachable. */} scrolls inside its own track, so every tab stays reachable. */}
{/* Row 1: Tabs on left, Bulk upload & New order on right */}
<div className="flex flex-wrap items-center justify-between gap-3 w-full"> <div className="flex flex-wrap items-center justify-between gap-3 w-full">
<div className="no-scrollbar -mx-1 max-w-full min-w-0 overflow-x-auto px-1 py-0.5"> <div className="no-scrollbar -mx-1 max-w-full min-w-0 overflow-x-auto px-1 py-0.5">
<Tabs <Tabs
@@ -465,7 +453,19 @@ export default function Orders() {
/> />
</div> </div>
<div className="flex min-w-0 flex-1 flex-wrap items-center justify-end gap-2 sm:flex-none"> <div className="flex items-center gap-2 shrink-0">
<Button variant="outline" onClick={() => navigate('/doormile/orders/createorders')}>
<Upload className="mr-1.5 h-4 w-4" /> Bulk upload
</Button>
<Button onClick={() => navigate('/doormile/orders/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New order
</Button>
</div>
</div>
{/* Row 2: Search, Date, Zone, and Export */}
<div className="flex flex-wrap items-center justify-between gap-3 w-full">
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">
<DatePicker <DatePicker
value={selectedDate} value={selectedDate}
onChange={setSelectedDate} onChange={setSelectedDate}
@@ -476,8 +476,11 @@ export default function Orders() {
value={search} value={search}
onChange={setSearch} onChange={setSearch}
placeholder="Search by order number, address or note" placeholder="Search by order number, address or note"
className="w-full sm:w-64" className="w-full sm:w-80"
/> />
</div>
<div className="flex items-center gap-2">
{selectedIds.length > 0 && tab === 'pending' && ( {selectedIds.length > 0 && tab === 'pending' && (
<Button variant="outline" onClick={() => setCancelOpen(true)}> <Button variant="outline" onClick={() => setCancelOpen(true)}>
<Ban className="mr-1.5 h-4 w-4" /> Cancel {selectedIds.length} <Ban className="mr-1.5 h-4 w-4" /> Cancel {selectedIds.length}

View File

@@ -191,22 +191,18 @@ export default function OrdersPreview() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader <div className="flex flex-wrap items-center justify-between gap-3">
actions={ <Button variant="outline" onClick={() => navigate('/doormile/orders')}>
<> <ArrowLeft className="mr-1.5 h-4 w-4" /> Back to orders
<Button variant="outline" onClick={() => navigate('/doormile/orders')}> </Button>
<ArrowLeft className="mr-1.5 h-4 w-4" /> Back to orders <Button
</Button> onClick={() => commit.mutate({ deliveries: rows })}
<Button loading={commit.isPending}
onClick={() => commit.mutate({ deliveries: rows })} disabled={rows.length === 0}
loading={commit.isPending} >
disabled={rows.length === 0} <Send className="mr-1.5 h-4 w-4" /> Assign {rows.length || ''} orders
> </Button>
<Send className="mr-1.5 h-4 w-4" /> Assign {rows.length || ''} orders </div>
</Button>
</>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard icon={Package} value={number(stats.orders, '0')} label="Orders" tone="brand" index={0} /> <KpiCard icon={Package} value={number(stats.orders, '0')} label="Orders" tone="brand" index={0} />

View File

@@ -254,19 +254,6 @@ export default function Pricing() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<>
<Button variant="outline" onClick={() => setQuoteOpen(true)}>
<Calculator className="mr-1.5 h-4 w-4" /> Get a quote
</Button>
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New rule
</Button>
</>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard icon={Tag} value={stats.total} label="Pricing rules" tone="brand" index={0} loading={isLoading} /> <KpiCard icon={Tag} value={stats.total} label="Pricing rules" tone="brand" index={0} loading={isLoading} />
<KpiCard <KpiCard
@@ -294,6 +281,16 @@ export default function Pricing() {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search by client, zone or vehicle" placeholder="Search by client, zone or vehicle"
count={{ shown: rows.length, total: zonePricing.length, noun: 'rules' }} count={{ shown: rows.length, total: zonePricing.length, noun: 'rules' }}
actions={
<>
<Button variant="outline" onClick={() => setQuoteOpen(true)}>
<Calculator className="mr-1.5 h-4 w-4" /> Get a quote
</Button>
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New rule
</Button>
</>
}
> >
<Select value={String(zone)} onValueChange={setZone}> <Select value={String(zone)} onValueChange={setZone}>
<SelectTrigger className="w-full sm:w-48"> <SelectTrigger className="w-full sm:w-48">

View File

@@ -161,27 +161,6 @@ export default function OrdersDetails() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<>
<Input
type="date"
value={range.from}
onChange={(e) => setRange((current) => ({ ...current, from: e.target.value }))}
aria-label="From date"
className="w-40"
/>
<Input
type="date"
value={range.to}
onChange={(e) => setRange((current) => ({ ...current, to: e.target.value }))}
aria-label="To date"
className="w-40"
/>
</>
}
/>
<Stack space="sm"> <Stack space="sm">
<ListToolbar <ListToolbar
search={search} search={search}
@@ -198,6 +177,23 @@ export default function OrdersDetails() {
</Button> </Button>
} }
> >
<div className="flex items-center gap-1.5">
<Input
type="date"
value={range.from}
onChange={(e) => setRange((current) => ({ ...current, from: e.target.value }))}
aria-label="From date"
className="w-36"
/>
<span className="text-xs text-ink-4">to</span>
<Input
type="date"
value={range.to}
onChange={(e) => setRange((current) => ({ ...current, to: e.target.value }))}
aria-label="To date"
className="w-36"
/>
</div>
<Select value={tenantId} onValueChange={setTenantId}> <Select value={tenantId} onValueChange={setTenantId}>
<SelectTrigger className="w-full sm:w-48"> <SelectTrigger className="w-full sm:w-48">
<SelectValue placeholder="All clients" /> <SelectValue placeholder="All clients" />

View File

@@ -168,27 +168,6 @@ export default function OrdersSummary() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<>
<Input
type="date"
value={range.from}
onChange={(e) => setRange((current) => ({ ...current, from: e.target.value }))}
aria-label="From date"
className="w-40"
/>
<Input
type="date"
value={range.to}
onChange={(e) => setRange((current) => ({ ...current, to: e.target.value }))}
aria-label="To date"
className="w-40"
/>
</>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard icon={Package} value={number(stats.totalOrders, '0')} label="Total orders" tone="brand" index={0} loading={query.isLoading} /> <KpiCard icon={Package} value={number(stats.totalOrders, '0')} label="Total orders" tone="brand" index={0} loading={query.isLoading} />
<KpiCard <KpiCard
@@ -233,6 +212,23 @@ export default function OrdersSummary() {
</Button> </Button>
} }
> >
<div className="flex items-center gap-1.5">
<Input
type="date"
value={range.from}
onChange={(e) => setRange((current) => ({ ...current, from: e.target.value }))}
aria-label="From date"
className="w-36"
/>
<span className="text-xs text-ink-4">to</span>
<Input
type="date"
value={range.to}
onChange={(e) => setRange((current) => ({ ...current, to: e.target.value }))}
aria-label="To date"
className="w-36"
/>
</div>
<Select value={zone} onValueChange={setZone}> <Select value={zone} onValueChange={setZone}>
<SelectTrigger className="w-full sm:w-40"> <SelectTrigger className="w-full sm:w-40">
<SelectValue placeholder="All zones" /> <SelectValue placeholder="All zones" />

View File

@@ -118,27 +118,6 @@ export default function RidersSummary() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<>
<Input
type="date"
value={range.from}
onChange={(e) => setRange((current) => ({ ...current, from: e.target.value }))}
aria-label="From date"
className="w-40"
/>
<Input
type="date"
value={range.to}
onChange={(e) => setRange((current) => ({ ...current, to: e.target.value }))}
aria-label="To date"
className="w-40"
/>
</>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard icon={Bike} value={number(stats.riders, '0')} label="Milers active" tone="brand" index={0} loading={isLoading} /> <KpiCard icon={Bike} value={number(stats.riders, '0')} label="Milers active" tone="brand" index={0} loading={isLoading} />
<KpiCard icon={Package} value={number(stats.orders, '0')} label="Orders assigned" tone="navy" index={1} loading={isLoading} /> <KpiCard icon={Package} value={number(stats.orders, '0')} label="Orders assigned" tone="navy" index={1} loading={isLoading} />
@@ -176,6 +155,23 @@ export default function RidersSummary() {
</Button> </Button>
} }
> >
<div className="flex items-center gap-1.5">
<Input
type="date"
value={range.from}
onChange={(e) => setRange((current) => ({ ...current, from: e.target.value }))}
aria-label="From date"
className="w-36"
/>
<span className="text-xs text-ink-4">to</span>
<Input
type="date"
value={range.to}
onChange={(e) => setRange((current) => ({ ...current, to: e.target.value }))}
aria-label="To date"
className="w-36"
/>
</div>
<Select value={zone} onValueChange={setZone}> <Select value={zone} onValueChange={setZone}>
<SelectTrigger className="w-full sm:w-44"> <SelectTrigger className="w-full sm:w-44">
<SelectValue placeholder="All zones" /> <SelectValue placeholder="All zones" />

View File

@@ -260,14 +260,6 @@ export default function Riders() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={() => navigate('/doormile/milers/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New miler
</Button>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard <KpiCard
icon={Users} icon={Users}
@@ -333,6 +325,9 @@ export default function Riders() {
placeholder="Search by name, phone, id or city" placeholder="Search by name, phone, id or city"
className="w-full sm:w-64" className="w-full sm:w-64"
/> />
<Button onClick={() => navigate('/doormile/milers/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New miler
</Button>
</div> </div>
</div> </div>

View File

@@ -92,14 +92,6 @@ export default function Settings() {
className="w-44 bg-white shadow-xs" className="w-44 bg-white shadow-xs"
/> />
)} )}
{activeTab === 'customers' && (
<Button
onClick={() => navigate('/doormile/customer/create')}
className="inline-flex items-center gap-1.5"
>
<Plus className="h-4 w-4" /> New customer
</Button>
)}
</div> </div>
</div> </div>

View File

@@ -195,14 +195,6 @@ export default function Tripsheets() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New tripsheet
</Button>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard <KpiCard
icon={Truck} icon={Truck}
@@ -248,6 +240,11 @@ export default function Tripsheets() {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search by tripsheet id, hub or status" placeholder="Search by tripsheet id, hub or status"
count={{ shown: rows.length, total: tripsheets.length, noun: 'tripsheets' }} count={{ shown: rows.length, total: tripsheets.length, noun: 'tripsheets' }}
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New tripsheet
</Button>
}
/> />
<DataTable <DataTable

View File

@@ -208,14 +208,6 @@ export default function Vehicles() {
return ( return (
<Stack space="lg"> <Stack space="lg">
<PageHeader
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New vehicle
</Button>
}
/>
<Grid cols={4}> <Grid cols={4}>
<KpiCard <KpiCard
icon={Car} icon={Car}
@@ -261,6 +253,11 @@ export default function Vehicles() {
onSearchChange={setSearch} onSearchChange={setSearch}
placeholder="Search by registration, type or status" placeholder="Search by registration, type or status"
count={{ shown: rows.length, total: vehicles.length, noun: 'vehicles' }} count={{ shown: rows.length, total: vehicles.length, noun: 'vehicles' }}
actions={
<Button onClick={openCreate}>
<Plus className="mr-1.5 h-4 w-4" /> New vehicle
</Button>
}
/> />
<DataTable <DataTable