updates on the ai bot and assigned by chatbot
This commit is contained in:
@@ -59,122 +59,39 @@
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Stage indicator
|
||||
--------------------------------------------------------------------------
|
||||
The layout rules below are NOT scoped under .co-root. Only theming needs
|
||||
the scope; structure must survive it. When `co-root` was being dropped by
|
||||
PageShell (it didn't forward className), every scoped rule failed silently
|
||||
and the chips rendered as run-together plain text — "1Pickup & Drop2Schedule".
|
||||
Keeping layout unscoped means the worst case is unthemed, not unreadable.
|
||||
Header picker — tenant / business-location control that sits opposite the
|
||||
title block. One fixed-width slot shared by every state (loading, error,
|
||||
badge, dropdown) so the control never changes size or drifts out of
|
||||
vertical alignment with the title as `locationsStatus` changes.
|
||||
-------------------------------------------------------------------------- */
|
||||
.co-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 20px;
|
||||
.co-header-picker {
|
||||
min-width: 280px;
|
||||
max-width: 320px;
|
||||
flex: 0 1 auto;
|
||||
/* Pins the picker to the far right of the header row even when the row
|
||||
wraps — a single child on a wrapped flex line sits at flex-start under
|
||||
justify-content: space-between, so margin-left: auto is what actually
|
||||
keeps it flush right in that case. */
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.co-step {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 6px 11px;
|
||||
border: 1px solid var(--co-border);
|
||||
border-radius: 999px;
|
||||
background: var(--co-surface);
|
||||
font-size: 12.5px;
|
||||
font-weight: 500;
|
||||
color: var(--co-text-3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.co-root .co-step[data-state='active'] {
|
||||
color: var(--co-brand);
|
||||
border-color: color-mix(in srgb, var(--co-brand) 35%, transparent);
|
||||
background: color-mix(in srgb, var(--co-brand) 6%, #fff);
|
||||
}
|
||||
|
||||
.co-root .co-step[data-state='done'] {
|
||||
color: var(--co-pickup);
|
||||
border-color: color-mix(in srgb, var(--co-pickup) 30%, transparent);
|
||||
background: color-mix(in srgb, var(--co-pickup) 6%, #fff);
|
||||
}
|
||||
|
||||
.co-step-num {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
border-radius: 999px;
|
||||
font-size: 10.5px;
|
||||
.co-root .co-header-picker-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: currentColor;
|
||||
color: #fff;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
color: var(--co-text-3);
|
||||
}
|
||||
|
||||
.co-root .co-step[data-state='done'] .co-step-num::after {
|
||||
content: '✓';
|
||||
}
|
||||
|
||||
.co-step-sep {
|
||||
flex: 0 0 auto;
|
||||
width: 14px;
|
||||
height: 1px;
|
||||
background: var(--co-border);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Journey — Pickup ●───→● Drop
|
||||
-------------------------------------------------------------------------- */
|
||||
.co-journey {
|
||||
.co-root .co-header-picker-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 9px 13px;
|
||||
margin-bottom: 14px;
|
||||
min-height: 38px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--co-border);
|
||||
border-radius: var(--co-radius);
|
||||
background: var(--co-surface);
|
||||
font-size: 12.5px;
|
||||
color: var(--co-text-2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.co-journey-dot {
|
||||
flex: 0 0 auto;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.co-root .co-journey-dot[data-end='pickup'] {
|
||||
background: var(--co-pickup);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--co-pickup) 18%, transparent);
|
||||
}
|
||||
|
||||
.co-root .co-journey-dot[data-end='drop'] {
|
||||
background: var(--co-drop);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--co-drop) 18%, transparent);
|
||||
}
|
||||
|
||||
.co-journey-line {
|
||||
flex: 1 1 auto;
|
||||
min-width: 24px;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, var(--co-pickup), var(--co-drop));
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.co-journey-label {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
color: var(--co-text);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
@@ -251,12 +168,23 @@
|
||||
color: var(--co-text-3);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Sticky order summary (desktop only)
|
||||
-------------------------------------------------------------------------- */
|
||||
.co-root .co-summary {
|
||||
position: sticky;
|
||||
top: calc(var(--appshell-header-height, 64px) + 12px);
|
||||
/* Pickup/drop dot — shared by the order-summary legs below. */
|
||||
.co-journey-dot {
|
||||
flex: 0 0 auto;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
margin-top: 4px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.co-root .co-journey-dot[data-end='pickup'] {
|
||||
background: var(--co-pickup);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--co-pickup) 18%, transparent);
|
||||
}
|
||||
|
||||
.co-root .co-journey-dot[data-end='drop'] {
|
||||
background: var(--co-drop);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--co-drop) 18%, transparent);
|
||||
}
|
||||
|
||||
.co-root .co-summary-leg {
|
||||
@@ -291,12 +219,6 @@
|
||||
/* --------------------------------------------------------------------------
|
||||
Responsive
|
||||
-------------------------------------------------------------------------- */
|
||||
@media (max-width: 1023px) {
|
||||
.co-root .co-summary {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.co-root .co-title {
|
||||
font-size: 20px;
|
||||
@@ -306,19 +228,9 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.co-root .co-steps {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.co-root .co-step {
|
||||
padding: 5px 9px;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
/* The step separators are decoration; drop them before the chips wrap
|
||||
awkwardly on a 360px screen. */
|
||||
.co-root .co-step-sep {
|
||||
display: none;
|
||||
.co-header-picker {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -647,7 +647,7 @@ const Createorder1 = () => {
|
||||
|
||||
<PageShell className="co-root">
|
||||
<VStack className="co-header" gap={0} padding={0}>
|
||||
<HStack justify="between" vAlign="start" gap={2} wrap="wrap" padding={0}>
|
||||
<HStack justify="between" vAlign="center" gap={3} wrap="wrap" padding={0}>
|
||||
<HStack gap={2} vAlign="center" padding={0}>
|
||||
<AccentAvatar color={BRAND} selected size={40}>
|
||||
<MdAddShoppingCart size={20} />
|
||||
@@ -658,17 +658,24 @@ const Createorder1 = () => {
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
{/* One fixed-width, consistently-labelled slot for whichever
|
||||
picker state is active, so the control never jumps width or
|
||||
loses vertical alignment with the title block as it switches
|
||||
between loading / error / badge / dropdown. */}
|
||||
{locationsStatus === 'loading' ? (
|
||||
<Text color="secondary" weight="bold">
|
||||
Loading business locations…
|
||||
</Text>
|
||||
<div className="co-header-picker co-header-picker-status">
|
||||
<Text color="secondary" weight="bold">
|
||||
Loading business locations…
|
||||
</Text>
|
||||
</div>
|
||||
) : locationsStatus === 'no-tenant' && isStaffLogin ? (
|
||||
// Staff login (express-console-api.md: token tenantid 0/null
|
||||
// = "Doormile's own staff... sees everything, all tenants")
|
||||
// — pick which tenant to create this order for. Everything
|
||||
// downstream (pricing, saved locations, the booking's own
|
||||
// tenantid) is keyed off `tid`, which equals selectedTenantId.
|
||||
<div style={{ minWidth: 260 }}>
|
||||
<VStack className="co-header-picker" gap={0.5} padding={0}>
|
||||
<Text className="co-header-picker-label">Tenant</Text>
|
||||
<Selector
|
||||
label="Tenant"
|
||||
isLabelHidden
|
||||
@@ -679,23 +686,44 @@ const Createorder1 = () => {
|
||||
isLoading={staffTenantListLoading}
|
||||
hasSearch
|
||||
/>
|
||||
</div>
|
||||
</VStack>
|
||||
) : locationsStatus === 'no-tenant' ? (
|
||||
<Text style={{ color: '#b91c1c' }} weight="bold">
|
||||
This login has no tenant assigned — sign in as a tenant user
|
||||
</Text>
|
||||
<div className="co-header-picker co-header-picker-status">
|
||||
<Text style={{ color: '#b91c1c' }} weight="bold">
|
||||
This login has no tenant assigned — sign in as a tenant user
|
||||
</Text>
|
||||
</div>
|
||||
) : locationsStatus === 'error' ? (
|
||||
<Text style={{ color: '#b91c1c', cursor: 'pointer' }} weight="bold" onClick={gettenantlocations}>
|
||||
Could not load business locations — tap to retry
|
||||
</Text>
|
||||
<div
|
||||
className="co-header-picker co-header-picker-status"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={gettenantlocations}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
gettenantlocations();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#b91c1c', cursor: 'pointer' }} weight="bold">
|
||||
Could not load business locations — tap to retry
|
||||
</Text>
|
||||
</div>
|
||||
) : locationsStatus === 'empty' ? (
|
||||
<Text style={{ color: '#b45309' }} weight="bold">
|
||||
No saved locations — enter pickup details manually below
|
||||
</Text>
|
||||
<div className="co-header-picker co-header-picker-status">
|
||||
<Text style={{ color: '#b45309' }} weight="bold">
|
||||
No saved locations — enter pickup details manually below
|
||||
</Text>
|
||||
</div>
|
||||
) : tenantLocations.length === 1 ? (
|
||||
<Badge variant="neutral" icon={<MdMyLocation size={14} />} label={locationDisplayName(tenantLocations[0])} />
|
||||
<VStack className="co-header-picker" gap={0.5} padding={0}>
|
||||
<Text className="co-header-picker-label">Business Location</Text>
|
||||
<Badge variant="neutral" icon={<MdMyLocation size={14} />} label={locationDisplayName(tenantLocations[0])} />
|
||||
</VStack>
|
||||
) : (
|
||||
<div style={{ minWidth: 300 }}>
|
||||
<VStack className="co-header-picker" gap={0.5} padding={0}>
|
||||
<Text className="co-header-picker-label">Business Location</Text>
|
||||
<Selector
|
||||
label="Business Location"
|
||||
isLabelHidden
|
||||
@@ -731,50 +759,11 @@ const Createorder1 = () => {
|
||||
hasSearch
|
||||
hasClear
|
||||
/>
|
||||
</div>
|
||||
</VStack>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
{/* Stage indicator. Derived from the form's own state rather than a
|
||||
wizard step counter — the page is one scrollable form, so this
|
||||
reports progress instead of gating it. */}
|
||||
<HStack className="co-steps" padding={0} gap={0}>
|
||||
{[
|
||||
{ n: 1, label: 'Pickup & Drop', done: Boolean(pickCust.address && dropCust.address) },
|
||||
{ n: 2, label: 'Schedule', done: Boolean(selectedtime) },
|
||||
{ n: 3, label: 'Package', done: Boolean(category) },
|
||||
{ n: 4, label: 'Review', done: showDistance && totalCharge > 0 }
|
||||
].map((step, i, all) => {
|
||||
const active = !step.done && all.slice(0, i).every((prev) => prev.done);
|
||||
return (
|
||||
<HStack key={step.n} padding={0} gap={0} vAlign="center">
|
||||
{i > 0 && <span className="co-step-sep" />}
|
||||
<span className="co-step" data-state={step.done ? 'done' : active ? 'active' : 'todo'}>
|
||||
<span className="co-step-num">{step.done ? '' : step.n}</span>
|
||||
{step.label}
|
||||
</span>
|
||||
</HStack>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
|
||||
{/* The delivery journey, stated once at the top so the order's shape
|
||||
is obvious before reading any field. */}
|
||||
<HStack className="co-journey" padding={0} gap={0} vAlign="center">
|
||||
<span className="co-journey-dot" data-end="pickup" />
|
||||
<span className="co-journey-label">{pickCust.address || 'Pickup address'}</span>
|
||||
<span className="co-journey-line" />
|
||||
{showDistance && (
|
||||
<span className="co-price-note">
|
||||
{distance} km{durationMin ? ` · ${durationMin} min` : ''}
|
||||
</span>
|
||||
)}
|
||||
<span className="co-journey-line" />
|
||||
<span className="co-journey-label">{dropCust.address || 'Drop address'}</span>
|
||||
<span className="co-journey-dot" data-end="drop" />
|
||||
</HStack>
|
||||
|
||||
<Grid columns={{ minWidth: 420, repeat: 'fit' }} gap={3}>
|
||||
{/* ================================================= || Pickup || ================================================= */}
|
||||
<Card padding={0} elevation="none" className="co-card" data-end="pickup">
|
||||
@@ -1282,9 +1271,14 @@ const Createorder1 = () => {
|
||||
</Card>
|
||||
|
||||
{/* ============================== || Order summary || ============================== */}
|
||||
{/* Sticky on desktop, static below 1024px (see CreateOrder.css). Reads
|
||||
straight off form state so it updates as the operator types — there
|
||||
is no second source of truth to drift from. */}
|
||||
{/* A normal full-width card in the single-column form flow (not a
|
||||
sidebar next to a taller column), so it stays static. It used to
|
||||
carry `position: sticky`, inherited from a two-column layout this
|
||||
page never actually has — with only one column, the card is the
|
||||
last block on the page, so sticking it caused a visible jump/
|
||||
collapse right as its own container's bottom edge scrolled into
|
||||
view. Reads straight off form state so it updates as the operator
|
||||
types — there is no second source of truth to drift from. */}
|
||||
<Card padding={0} elevation="none" className="co-card co-summary">
|
||||
<SectionHeader color="#0f172a" icon={<MdCheckCircle size={16} />} title="Order Summary" subtitle="Updates as you fill the form" />
|
||||
<VStack gap={2} padding={3}>
|
||||
|
||||
@@ -49,7 +49,7 @@ import { FaLocationDot } from 'react-icons/fa6';
|
||||
|
||||
import Loader from 'components/Loader';
|
||||
import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import { PageShell } from 'components/nearle_components/PageLayout';
|
||||
import { PageShell, TABLE_FRAME } from 'components/nearle_components/PageLayout';
|
||||
import { DT } from 'themes/dt/tokens';
|
||||
import { TableScroll } from 'themes/dt/primitives';
|
||||
import './OrdersRedesign.css';
|
||||
@@ -1250,7 +1250,10 @@ const MultipleOrders = () => {
|
||||
</VStack>
|
||||
|
||||
{/* RIGHT — file / drop preview */}
|
||||
<Card padding={2} elevation="none" style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{/* Same frame as every DataCard table — this preview holds a table but
|
||||
predates DataCard, so it states the shared constant rather than
|
||||
carrying a second definition of it. */}
|
||||
<Card padding={2} elevation="none" style={{ border: TABLE_FRAME, display: 'flex', flexDirection: 'column' }}>
|
||||
<HStack justify="between" vAlign="center" gap={1} wrap="wrap" style={{ marginBottom: 8 }}>
|
||||
<HStack gap={2} vAlign="center">
|
||||
<Text weight="bold">{previewMode === 'drops' ? 'Drop List' : previewMode === 'preview' ? 'File Preview' : 'Preview'}</Text>
|
||||
|
||||
Reference in New Issue
Block a user