updates on the ui changes
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,8 +389,14 @@ export default function Deliveries() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack space="lg">
|
<Stack space="lg">
|
||||||
<PageHeader
|
<Stack space="sm">
|
||||||
actions={
|
{/* 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>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 shrink-0">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={selectedDate}
|
value={selectedDate}
|
||||||
onChange={(d) => {
|
onChange={(d) => {
|
||||||
@@ -402,18 +407,6 @@ export default function Deliveries() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Stack space="sm">
|
|
||||||
<Tabs tabs={tabs} value={tab} onChange={setTab} />
|
|
||||||
|
|
||||||
<ListToolbar
|
|
||||||
search={search}
|
|
||||||
onSearchChange={setSearch}
|
|
||||||
placeholder="Search by order, client, miler, customer or address"
|
|
||||||
count={{ shown: rows.length, total: batchRows.length, noun: 'deliveries' }}
|
|
||||||
actions={
|
|
||||||
<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,24 +414,48 @@ 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" />
|
|
||||||
|
{/* Row 2: Search, Zone, Batch, Clients, Milers, and Count */}
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-3 w-full">
|
||||||
|
<div className="flex flex-1 flex-wrap items-center gap-2.5 min-w-0">
|
||||||
|
<SearchInput
|
||||||
|
value={search}
|
||||||
|
onChange={setSearch}
|
||||||
|
placeholder="Search by order, client, miler, customer..."
|
||||||
|
className="w-full sm:w-64 lg:w-72"
|
||||||
|
/>
|
||||||
|
<ZoneSelector className="w-full sm:w-auto shrink-0" />
|
||||||
|
|
||||||
<Select value={batch} onValueChange={setBatch}>
|
<Select value={batch} onValueChange={setBatch}>
|
||||||
<SelectTrigger className="w-full sm:w-52">
|
<SelectTrigger className="w-full sm:w-auto min-w-[210px] h-10">
|
||||||
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
|
<Clock className="h-3.5 w-3.5 text-ink-3 shrink-0" />
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
|
</div>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent className="w-[300px] p-1.5 shadow-lg rounded-xl">
|
||||||
{BATCH_OPTIONS.map((option) => (
|
{BATCH_OPTIONS.map((option) => (
|
||||||
<SelectItem key={option.id} value={option.id}>
|
<SelectItem
|
||||||
{option.label} · {option.range}
|
key={option.id}
|
||||||
|
value={option.id}
|
||||||
|
textValue={option.id === 'all' ? 'All batches' : `${option.label} · ${option.range}`}
|
||||||
|
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>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Select value={tenantId} onValueChange={setTenantId}>
|
<Select value={tenantId} onValueChange={setTenantId}>
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
<SelectTrigger className="w-full sm:w-44">
|
||||||
<SelectValue placeholder="All clients" />
|
<SelectValue placeholder="All clients" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -452,7 +469,7 @@ export default function Deliveries() {
|
|||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Select value={riderId} onValueChange={setRiderId}>
|
<Select value={riderId} onValueChange={setRiderId}>
|
||||||
<SelectTrigger className="w-full sm:w-48">
|
<SelectTrigger className="w-full sm:w-44">
|
||||||
<SelectValue placeholder="All milers" />
|
<SelectValue placeholder="All milers" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -464,7 +481,30 @@ export default function Deliveries() {
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</ListToolbar>
|
|
||||||
|
{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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -191,9 +191,7 @@ 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')}>
|
<Button variant="outline" onClick={() => navigate('/doormile/orders')}>
|
||||||
<ArrowLeft className="mr-1.5 h-4 w-4" /> Back to orders
|
<ArrowLeft className="mr-1.5 h-4 w-4" /> Back to orders
|
||||||
</Button>
|
</Button>
|
||||||
@@ -204,9 +202,7 @@ export default function OrdersPreview() {
|
|||||||
>
|
>
|
||||||
<Send className="mr-1.5 h-4 w-4" /> Assign {rows.length || ''} orders
|
<Send className="mr-1.5 h-4 w-4" /> Assign {rows.length || ''} orders
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</div>
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<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} />
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user