updates on the map configuration and the design
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { memo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { AiOutlineCopy as CopyOutlined } from 'react-icons/ai';
|
||||
import { LuChevronDown } from 'react-icons/lu';
|
||||
import { LuChevronDown, LuDownload } from 'react-icons/lu';
|
||||
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
@@ -112,7 +112,9 @@ const AssistantMessage = ({ message, onCopy, onSubmitForm, onCancelAction, onCho
|
||||
{/* A conversational step that needs a dropdown rather than typing. */}
|
||||
{message.liveStop && (
|
||||
<VStack className="dai-action" gap={1} padding={0}>
|
||||
<Button label="Stop" variant="secondary" size="sm" onClick={() => onStopLive(message)} />
|
||||
<Button variant="secondary" size="sm" onClick={() => onStopLive(message)}>
|
||||
Stop
|
||||
</Button>
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
@@ -195,11 +197,16 @@ const AssistantMessage = ({ message, onCopy, onSubmitForm, onCancelAction, onCho
|
||||
{message.downloadRows?.length > 0 && (
|
||||
<VStack className="dai-action" gap={1} padding={0}>
|
||||
<Button
|
||||
label={`Download the ${message.downloadRows.length} row${message.downloadRows.length === 1 ? '' : 's'} that didn’t go through`}
|
||||
variant="secondary"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center justify-center gap-2 text-xs font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 hover:text-slate-900 rounded-lg px-3.5 py-2 w-full shadow-2xs transition-colors cursor-pointer"
|
||||
onClick={() => downloadCsv('doormile-bulk-orders-unsendable.csv', failedRowsCsv(message.downloadRows))}
|
||||
/>
|
||||
>
|
||||
<LuDownload className="w-3.5 h-3.5 text-slate-500 shrink-0" />
|
||||
<span>
|
||||
Download {message.downloadRows.length} unsendable row{message.downloadRows.length === 1 ? '' : 's'} (CSV)
|
||||
</span>
|
||||
</Button>
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Home } from 'lucide-react';
|
||||
import { Button, EmptyState, Surface } from '@/components/ds';
|
||||
import React from 'react';
|
||||
import { useLocation, useNavigate, Link } from 'react-router-dom';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Radio,
|
||||
Package,
|
||||
Bike,
|
||||
BarChart3,
|
||||
Home,
|
||||
MapPin,
|
||||
Truck
|
||||
} from 'lucide-react';
|
||||
import { useAuth } from '@/lib/AuthContext';
|
||||
|
||||
/**
|
||||
* The 404 page.
|
||||
*
|
||||
* Signed-in operators get a second line naming the route table, because a
|
||||
* missing route in a console is nearly always a link that was renamed rather
|
||||
* than a page that never existed.
|
||||
*/
|
||||
export default function PageNotFound() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -17,26 +19,139 @@ export default function PageNotFound() {
|
||||
|
||||
const path = location.pathname;
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen items-center justify-center p-6">
|
||||
<Surface radius="xl" padding="lg" className="w-full max-w-md">
|
||||
<EmptyState
|
||||
icon={Home}
|
||||
title="Page not found"
|
||||
description={`Nothing is registered at "${path}".`}
|
||||
action={
|
||||
<Button onClick={() => navigate(isAuthenticated ? '/doormile/dispatch' : '/login')}>
|
||||
{isAuthenticated ? 'Back to Dispatch' : 'Sign in'}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
const quickLinks = [
|
||||
{
|
||||
title: 'Live Dispatch',
|
||||
desc: 'Real-time fleet tracking & active runs',
|
||||
icon: Radio,
|
||||
path: '/doormile/dispatch',
|
||||
color: 'text-rose-600 bg-rose-50 border-rose-200/70 group-hover:bg-rose-100/70'
|
||||
},
|
||||
{
|
||||
title: 'Orders Management',
|
||||
desc: 'Pending, assigned, and bulk bookings',
|
||||
icon: Package,
|
||||
path: '/doormile/orders',
|
||||
color: 'text-blue-600 bg-blue-50 border-blue-200/70 group-hover:bg-blue-100/70'
|
||||
},
|
||||
{
|
||||
title: 'Live Deliveries',
|
||||
desc: 'Active consignments & transit logs',
|
||||
icon: Truck,
|
||||
path: '/doormile/deliveries',
|
||||
color: 'text-amber-600 bg-amber-50 border-amber-200/70 group-hover:bg-amber-100/70'
|
||||
},
|
||||
{
|
||||
title: 'Milers Fleet',
|
||||
desc: 'Rider profiles, roster & status',
|
||||
icon: Bike,
|
||||
path: '/doormile/milers',
|
||||
color: 'text-emerald-600 bg-emerald-50 border-emerald-200/70 group-hover:bg-emerald-100/70'
|
||||
},
|
||||
{
|
||||
title: 'Reports & Analytics',
|
||||
desc: 'Daily summaries & financial KPIs',
|
||||
icon: BarChart3,
|
||||
path: '/doormile/reports/orderssummary',
|
||||
color: 'text-purple-600 bg-purple-50 border-purple-200/70 group-hover:bg-purple-100/70'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<main className="relative min-h-screen w-full bg-white text-slate-900 flex flex-col items-center justify-center p-6 selection:bg-rose-500 selection:text-white">
|
||||
{/* Background subtle mesh & grid */}
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_80%_60%_at_50%_-10%,rgba(216,30,6,0.06),rgba(255,255,255,0))] pointer-events-none" />
|
||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#0f172a08_1px,transparent_1px),linear-gradient(to_bottom,#0f172a08_1px,transparent_1px)] bg-[size:32px_32px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_40%,#000_70%,transparent_100%)] pointer-events-none" />
|
||||
|
||||
<div className="relative z-10 w-full max-w-2xl flex flex-col items-center text-center">
|
||||
{/* Top Brand Pill */}
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-white border border-slate-200/90 shadow-2xs mb-5">
|
||||
<span className="w-2 h-2 rounded-full bg-rose-600 animate-pulse" />
|
||||
<span className="text-xs font-semibold tracking-wider uppercase text-slate-600">
|
||||
Doormile Operations Console
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Clean 404 Visual Graphic without any overlay icon */}
|
||||
<div className="select-none my-1">
|
||||
<span className="text-8xl sm:text-9xl font-black tracking-tight text-slate-900 bg-clip-text bg-gradient-to-b from-slate-900 via-slate-800 to-slate-400">
|
||||
404
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Heading & Context */}
|
||||
<h1 className="text-2xl sm:text-3xl font-bold tracking-tight text-slate-900 mb-2">
|
||||
Page not found
|
||||
</h1>
|
||||
|
||||
<p className="text-sm sm:text-base text-slate-600 max-w-lg mb-4">
|
||||
The requested destination is not registered in the console or has been moved to a new route.
|
||||
</p>
|
||||
|
||||
{/* Target Path Badge */}
|
||||
<div className="inline-flex items-center gap-2 max-w-full px-3.5 py-1.5 rounded-lg bg-slate-100 border border-slate-200 font-mono text-xs text-rose-600 mb-7 shadow-2xs truncate">
|
||||
<MapPin className="w-3.5 h-3.5 text-slate-500 shrink-0" />
|
||||
<span className="truncate">{path}</span>
|
||||
</div>
|
||||
|
||||
{/* Primary & Secondary Action CTAs */}
|
||||
<div className="flex flex-wrap items-center justify-center gap-3 w-full max-w-md mb-9">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate(isAuthenticated ? '/doormile/dispatch' : '/login')}
|
||||
className="flex-1 min-w-[160px] inline-flex items-center justify-center gap-2 px-5 py-2.5 rounded-xl font-medium text-sm text-white bg-rose-600 hover:bg-rose-700 active:scale-[0.98] shadow-sm shadow-rose-600/20 transition-all cursor-pointer"
|
||||
>
|
||||
<Home className="w-4 h-4" />
|
||||
<span>{isAuthenticated ? 'Back to Dispatch' : 'Sign in to Console'}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate(-1)}
|
||||
className="flex-1 min-w-[140px] inline-flex items-center justify-center gap-2 px-5 py-2.5 rounded-xl font-medium text-sm text-slate-700 bg-white hover:bg-slate-50 border border-slate-200 active:scale-[0.98] transition-all cursor-pointer shadow-2xs"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
<span>Go Back</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Quick Operations Links */}
|
||||
{isAuthenticated && (
|
||||
<p className="mt-2 text-center text-caption text-ink-4">
|
||||
If this address used to work, check the route table in <code>src/App.jsx</code>.
|
||||
</p>
|
||||
<div className="w-full bg-slate-50/80 border border-slate-200 rounded-2xl p-5 shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3 text-left">
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-slate-600">
|
||||
Quick Navigation
|
||||
</span>
|
||||
<span className="text-[11px] text-slate-500">Operational shortcuts</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2.5 text-left">
|
||||
{quickLinks.map((item) => {
|
||||
const IconComponent = item.icon;
|
||||
return (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className="flex items-center gap-3 p-2.5 rounded-xl bg-white hover:bg-slate-50/90 border border-slate-200/90 hover:border-slate-300 shadow-2xs transition-all group"
|
||||
>
|
||||
<div className={`w-8 h-8 rounded-lg flex items-center justify-center shrink-0 border ${item.color} transition-transform group-hover:scale-105`}>
|
||||
<IconComponent className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-xs font-semibold text-slate-800 group-hover:text-slate-950 truncate">
|
||||
{item.title}
|
||||
</div>
|
||||
<div className="text-[11px] text-slate-500 truncate">
|
||||
{item.desc}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Surface>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5237,12 +5237,12 @@
|
||||
padding-right: 44px;
|
||||
}
|
||||
|
||||
/* --- Header: purple gradient with order id + status + rider --- */
|
||||
/* --- Header: red gradient with order id + status + rider --- */
|
||||
.dispatch-container .dispatch-popup .pu-header {
|
||||
background: var(--color-accent);
|
||||
background: linear-gradient(135deg, #D81E06 0%, #C8102E 100%);
|
||||
padding: 14px 16px 12px;
|
||||
color: #fff;
|
||||
border-radius: 5px 5px 0 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-header-top {
|
||||
@@ -5254,8 +5254,7 @@
|
||||
}
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-id {
|
||||
/* Override the legacy pu-id styling — no separate background, sits on
|
||||
the purple gradient instead. */
|
||||
/* Override the legacy pu-id styling — sits on the red header instead. */
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
@@ -5374,13 +5373,13 @@
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-accent);
|
||||
color: #D81E06;
|
||||
}
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-header-step-value {
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
color: var(--color-accent);
|
||||
color: #D81E06;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -5405,8 +5404,8 @@
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-accent);
|
||||
border-bottom: 1px solid rgba(123, 31, 162, 0.18);
|
||||
color: #D81E06;
|
||||
border-bottom: 1px solid rgba(216, 30, 6, 0.15);
|
||||
}
|
||||
|
||||
/* --- Timeline: lay events out as a 2-column grid so the 6-row vertical
|
||||
@@ -5438,18 +5437,18 @@
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
background: rgba(123, 31, 162, 0.05);
|
||||
border: 1px solid rgba(123, 31, 162, 0.12);
|
||||
border-radius: 5px;
|
||||
background: rgba(216, 30, 6, 0.04);
|
||||
border: 1px solid rgba(216, 30, 6, 0.1);
|
||||
border-radius: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-detail-icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 5px;
|
||||
background: rgba(123, 31, 162, 0.12);
|
||||
color: var(--color-accent);
|
||||
border-radius: 6px;
|
||||
background: rgba(216, 30, 6, 0.1);
|
||||
color: #D81E06;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -5492,7 +5491,7 @@
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed rgba(123, 31, 162, 0.18);
|
||||
border-top: 1px dashed rgba(216, 30, 6, 0.18);
|
||||
}
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-distance-chip {
|
||||
@@ -5500,8 +5499,8 @@
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 5px 10px;
|
||||
background: linear-gradient(135deg, rgba(123, 31, 162, 0.08), rgba(156, 39, 176, 0.06));
|
||||
border: 1px solid rgba(123, 31, 162, 0.2);
|
||||
background: rgba(216, 30, 6, 0.05);
|
||||
border: 1px solid rgba(216, 30, 6, 0.15);
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
@@ -5509,7 +5508,7 @@
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-distance-icon {
|
||||
display: inline-flex;
|
||||
color: var(--color-accent);
|
||||
color: #D81E06;
|
||||
}
|
||||
|
||||
.dispatch-container .dispatch-popup .pu-distance-icon svg {
|
||||
|
||||
Reference in New Issue
Block a user