updates on the design and chatbot fix

This commit is contained in:
2026-09-22 17:32:07 +05:30
parent 6585a8da00
commit b9b0380927
12 changed files with 383 additions and 199 deletions

View File

@@ -1428,6 +1428,7 @@ const AIPanel = ({ isOpen, onClose }) => {
<IconButton
size="sm"
variant="ghost"
side="bottom"
label={view === 'history' ? 'Back to conversation' : 'Conversation history'}
tooltip={view === 'history' ? 'Back to conversation' : 'History'}
aria-pressed={view === 'history'}
@@ -1437,6 +1438,7 @@ const AIPanel = ({ isOpen, onClose }) => {
<IconButton
size="sm"
variant="ghost"
side="bottom"
label="New conversation"
tooltip="New conversation"
icon={<LuRotateCcw />}
@@ -1445,6 +1447,7 @@ const AIPanel = ({ isOpen, onClose }) => {
<IconButton
size="sm"
variant="ghost"
side="bottom"
label={isWide ? 'Standard width (20%)' : 'Expand width (30%)'}
tooltip={isWide ? 'Standard width (20%)' : 'Expand width (30%)'}
aria-pressed={isWide}
@@ -1454,6 +1457,7 @@ const AIPanel = ({ isOpen, onClose }) => {
<IconButton
size="sm"
variant="ghost"
side="bottom"
label="Close assistant"
tooltip="Close assistant"
icon={<LuX />}

View File

@@ -69,7 +69,12 @@ export const CHIP_LABELS = {
'How many tripsheets are dispatched?': 'Tripsheets',
'How many open exceptions?': 'Open exceptions',
'How many pricing rules are configured?': 'Pricing rules',
"Repeat yesterday's orders": 'Repeat yesterday'
"Repeat yesterday's orders": 'Repeat yesterday',
'How many consignments do we have?': 'Consignments',
'How many app users do we have?': 'App users',
'How many partners do we have?': 'Partners',
'How many competitor branches are tracked?': 'Competitor branches',
'How many carriers do we have?': 'Carriers'
};
// The one follow-up phrasing that is not already a page suggestion. Verified
@@ -121,39 +126,27 @@ const ORDERS = {
};
const RIDERS = {
label: 'Riders',
label: 'Milers',
suggestions: [
{ icon: LuBike, text: 'How many riders are active?' },
{ icon: LuClock3, text: "Give me today's operations summary" },
{ icon: LuPackage, text: 'How many orders today?' },
{ icon: LuTruck, text: 'How many vehicles are available?' }
{ icon: LuUserPlus, text: 'Assign a rider' }
],
more: [
{ icon: LuBuilding2, text: 'Current hub status' },
{ icon: LuBanknote, text: 'Total revenue today' }
]
more: []
};
const VEHICLES = {
label: 'Vehicles',
suggestions: [
{ icon: LuTruck, text: 'How many vehicles are available?' },
{ icon: LuBike, text: 'How many riders are active?' },
{ icon: LuBuilding2, text: 'Current hub status' },
{ icon: LuClock3, text: "Give me today's operations summary" }
],
more: [{ icon: LuPackage, text: 'How many orders today?' }]
suggestions: [{ icon: LuTruck, text: 'How many vehicles are available?' }],
more: []
};
const HUBS = {
label: 'Hubs',
suggestions: [
{ icon: LuBuilding2, text: 'Current hub status' },
{ icon: LuTimerOff, text: 'Which hubs are experiencing delays?' },
{ icon: LuTruck, text: 'How many vehicles are available?' },
{ icon: LuBike, text: 'How many riders are active?' }
{ icon: LuTimerOff, text: 'Which hubs are experiencing delays?' }
],
more: [{ icon: LuPackage, text: 'How many orders today?' }]
more: []
};
const DISPATCH = {
@@ -162,24 +155,16 @@ const DISPATCH = {
{ icon: LuLayers, text: 'Morning Batch orders today' },
{ icon: LuTimerOff, text: 'Which orders are delayed?' },
{ icon: LuBike, text: 'How many riders are active?' },
{ icon: LuClock3, text: "Give me today's operations summary" }
{ icon: LuUserPlus, text: 'Assign a rider' }
],
more: [
{ icon: LuPackage, text: 'How many orders today?' },
{ icon: LuCircleDot, text: 'How many pending orders today?' }
]
more: []
};
// Linehaul and middle-mile runs.
const TRIPSHEETS = {
label: 'Tripsheets',
suggestions: [
{ icon: LuLayers, text: 'How many tripsheets are dispatched?' },
{ icon: LuTruck, text: 'How many vehicles are available?' },
{ icon: LuBike, text: 'How many riders are active?' },
{ icon: LuClock3, text: "Give me today's operations summary" }
],
more: [{ icon: LuBuilding2, text: 'Current hub status' }]
suggestions: [{ icon: LuLayers, text: 'How many tripsheets are dispatched?' }],
more: []
};
// The register of things that went wrong.
@@ -187,9 +172,7 @@ const EXCEPTIONS = {
label: 'Exceptions',
suggestions: [
{ icon: LuCircleDot, text: 'How many open exceptions?' },
{ icon: LuTimerOff, text: 'Which orders are delayed?' },
{ icon: LuTimerOff, text: 'Which hubs are experiencing delays?' },
{ icon: LuClock3, text: "Give me today's operations summary" }
{ icon: LuTimerOff, text: 'Which orders are delayed?' }
],
more: []
};
@@ -198,13 +181,8 @@ const EXCEPTIONS = {
// real page and deserves its own questions like any other.
const PRICING = {
label: 'Pricing',
suggestions: [
{ icon: LuBanknote, text: 'How many pricing rules are configured?' },
{ icon: LuBanknote, text: 'Total revenue today' },
{ icon: LuUsers, text: 'How many tenants do we have?' },
{ icon: LuClock3, text: "Give me today's operations summary" }
],
more: [{ icon: LuBanknote, text: 'Total revenue this week' }]
suggestions: [{ icon: LuBanknote, text: 'How many pricing rules are configured?' }],
more: []
};
// Bookings — what customers raised in the app, before it becomes an order.
@@ -227,23 +205,62 @@ const BOOKINGS = {
suggestions: [
{ icon: LuPackage, text: 'How many orders today?' },
{ icon: LuCircleDot, text: 'How many pending orders today?' },
{ icon: LuTimerOff, text: 'Which orders are delayed?' },
{ icon: LuClock3, text: "Give me today's operations summary" }
],
more: [
{ icon: LuPackage, text: 'How many delivered orders today?' },
{ icon: LuPackage, text: 'How many cancelled orders today?' },
{ icon: LuClock3, text: 'Orders today vs yesterday' }
]
{ icon: LuTimerOff, text: 'Which orders are delayed?' }
],
more: []
};
// Deliveries — an order after it has been picked up and become a consignment.
//
// This page used to be `{ ...ORDERS, label: 'Deliveries' }`: the Orders
// question set under a different name, so a table of consignments offered
// "Create an order" and "Repeat yesterday's orders". The label made it look
// deliberate, which is the same trap the Tripsheets/Exceptions/Pricing note
// below describes.
//
// Every phrasing here is checked against its intent's own `match`:
// consignmentStatus is /\bconsignments?\b/, parcelTrack wants a tracking
// number, and the delivered/delayed counts are the order intents narrowed to
// the statuses this page actually shows.
const DELIVERIES = {
label: 'Deliveries',
suggestions: [
{ icon: LuTruck, text: 'How many consignments do we have?' },
{ icon: LuPackage, text: 'How many delivered orders today?' },
{ icon: LuTimerOff, text: 'Which orders are delayed?' },
{ icon: LuBike, text: 'How many riders are active?' }
],
more: []
};
// Reference data behind the console — not an operational page, so it gets the
// one question that is actually about it rather than the operations summary.
const APP_USERS = {
label: 'App users',
suggestions: [{ icon: LuUsers, text: 'How many app users do we have?' }],
more: []
};
const COMPETITIVE_INTEL = {
label: 'Competitive intel',
suggestions: [
{ icon: LuBuilding2, text: 'How many competitor branches are tracked?' },
// NOT "how many carrier pricing rules", which is the phrasing
// carrierPricingCount documents for itself and which never reaches it:
// pricingCount matches /\bpricing\b/ and runs 57 lines earlier, so that
// wording is answered with the general pricing-rule count. Its actual
// predicate is /\bcarriers?\b/.
{ icon: LuBanknote, text: 'How many carriers do we have?' }
],
more: []
};
const TENANTS = {
label: 'Tenants',
suggestions: [
{ icon: LuUsers, text: 'How many tenants do we have?' },
{ icon: LuPackage, text: 'How many orders today?' },
{ icon: LuBanknote, text: 'Total revenue today' },
{ icon: LuClock3, text: "Give me today's operations summary" }
{ icon: LuUsers, text: 'How many partners do we have?' }
],
more: []
};
@@ -352,7 +369,7 @@ const FOLLOW_UPS = {
// with the wrong label.
const ROUTES = [
['/doormile/control-x', DISPATCH],
['/doormile/deliveries', { ...ORDERS, label: 'Deliveries' }],
['/doormile/deliveries', DELIVERIES],
['/doormile/bookings', BOOKINGS],
['/doormile/orders', ORDERS],
['/doormile/milers', RIDERS],
@@ -386,7 +403,11 @@ const ROUTES = [
// so none is shadowed by the order intents that run earlier in the catalog.
['/doormile/tripsheets', TRIPSHEETS],
['/doormile/exceptions', EXCEPTIONS],
['/doormile/pricing', PRICING]
['/doormile/pricing', PRICING],
// Missing from this list entirely until now, so both fell through to
// DEFAULT_CONTEXT and offered rider and hub questions over their own data.
['/doormile/app-users', APP_USERS],
['/doormile/competitive-intel', COMPETITIVE_INTEL]
];
// ---- every question, from every page ---------------------------------------
@@ -399,7 +420,20 @@ const ROUTES = [
//
// `more` is retired: AIWelcome renders one flat list now, so a second array
// would only be a second place for a question to hide.
const ALL_CONTEXTS = [ORDERS, RIDERS, VEHICLES, HUBS, DISPATCH, TENANTS, REPORTS, DEFAULT_CONTEXT, ...ROUTES.map(([, c]) => c)];
const ALL_CONTEXTS = [
ORDERS,
RIDERS,
VEHICLES,
HUBS,
DISPATCH,
DELIVERIES,
APP_USERS,
COMPETITIVE_INTEL,
TENANTS,
REPORTS,
DEFAULT_CONTEXT,
...ROUTES.map(([, c]) => c)
];
// Every question declared anywhere. Two jobs: it is the icon index behind
// QUESTION_BY_TEXT (so a follow-up naming a question that is no longer global

View File

@@ -2090,6 +2090,30 @@ const INTENTS = [
const INTENTS_BY_ID = Object.fromEntries(INTENTS.map((i) => [i.id, i]));
// Which intent the ordered catalog reaches for a question, WITHOUT running it.
//
// Exported for the page-suggestion test. The catalog is ordered and the first
// match wins, so "something matches this chip" and "the right thing matches
// this chip" are different questions — ROADMAP.md B1/B2 are two shipped cases
// where they had different answers.
//
// Covers the deterministic pass only. answerQuestion tries semantic routing
// and the multi-part splitter ahead of this, both of which are additive: they
// can route a question the catalog would miss, never away from one it hits.
export const resolveIntentId = (text) => {
const normalized = correctTypos(text);
for (const intent of INTENTS) {
let params = null;
try {
params = intent.match(normalized);
} catch {
params = null;
}
if (params) return intent.id;
}
return null;
};
export const SUPPORTED_QUESTIONS = INTENTS.map((i) => i.label);
// Clean, directly-askable example phrasings — for "recommended question"