initial commit

This commit is contained in:
2026-08-26 15:01:15 +05:30
parent aa42d9ee81
commit cf4f4ecd9c
87 changed files with 12814 additions and 2328 deletions

View File

@@ -20,11 +20,11 @@ import {
getConsignmentLogs,
getAdminTenant,
getTenantLocations
} from '@/api/doormile/endpoints';
import { getAdminTenants } from '@/api/doormile/endpoints';
import { parseDoormileTimestamp } from '@/lib/doormileTimestamp';
import { getRowBatchId, getBatchLabel, BATCHES } from '@/lib/batchBucket';
} from 'pages/api/doormileApi';
import { getalltenants, getallridersummary } from 'pages/api/api';
import { parseDoormileTimestamp } from 'utils/doormileTimestamp';
import { getRowBatchId, getBatchLabel, BATCHES } from 'utils/batchBucket';
import { STATUS } from 'themes/dt/tokens';
// Only the read-only half of actions.js belongs here. Tenant resolution,
// payload building and execution live in the panel's submit handler — an
// intent must have no route to a write.
@@ -34,7 +34,7 @@ import { ASSIGN_TRIGGER } from './assignActions';
import { REPEAT_TRIGGER } from './repeatRuns';
import { CREATE_BULK_TRIGGER } from './bulkOrderActions';
import { routeQuestion, isRouteTrustworthy, askDocs } from './ragRouter';
import { ORDER_STATUS_LABELS, ORDER_STATUS_ORDER, groupForBookingStatus, isInGroup, statusesInGroup } from '@/lib/orderStatusGroups';
import { ORDER_STATUS_LABELS, ORDER_STATUS_ORDER, groupForBookingStatus, isInGroup, statusesInGroup } from 'utils/orderStatusGroups';
// ==============================|| Doormile Bot — intent catalog ||============================== //
//
@@ -279,7 +279,7 @@ const batchFromWords = (text) => {
// "done"/"completed" alongside "delivered", "declined"/"rejected" alongside
// "cancelled". Order matters: more specific phrases are checked before the
// broader "delivered" pattern so "undelivered" doesn't false-match it.
// Returns an ORDER STATUS GROUP key (@/lib/orderStatusGroups.js) — the same
// Returns an ORDER STATUS GROUP key (utils/orderStatusGroups.js) — the same
// taxonomy the Orders page's tabs count with. It previously returned api.js's
// delivery-status buckets, which is the Deliveries page's rider-centric view,
// so "how many assigned orders" never agreed with the Assigned tab.
@@ -460,15 +460,6 @@ const summarizeStatuses = (rows) => {
// STATUS is the raw palette and has no 'assigned' key; the group taxonomy and
// the colour palette are separate concerns and shouldn't be forced to match
// names.
const STATUS = {
pending: '#f59e0b',
accepted: '#6366f1',
active: '#14b8a6',
delivered: '#10b981',
cancelled: '#ef4444',
muted: '#94a3b8'
};
const GROUP_COLOR = {
pending: STATUS.pending,
assigned: STATUS.accepted,
@@ -512,7 +503,7 @@ const summarizeByField = (rows, field) => {
};
const resolveTenant = async (text) => {
const tenants = (await getAdminTenants()) || [];
const tenants = (await getalltenants()) || [];
const lower = text.toLowerCase();
return tenants.find((t) => t.tenantname && lower.includes(String(t.tenantname).toLowerCase()));
};
@@ -737,7 +728,7 @@ const ABOUT_TRIGGER =
// (in-transit) — statusBreakdown's match used to fire on that word alone and
// never returns null on a match (it always finds *some* count, even 0), so
// it never yielded to riderCounts and every rider question silently called
// getBookings instead of getMilerSummary. Fixed two ways, deliberately
// getBookings instead of getallridersummary. Fixed two ways, deliberately
// redundant: riderCounts/tenantList are ordered ahead of the generic
// intents below (INTENTS is checked in order, first match wins), AND the
// generic intents explicitly refuse to match when "rider" is mentioned, so
@@ -1175,7 +1166,7 @@ const INTENTS = [
return name ? { name } : null;
},
run: async ({ name }) => {
const tenants = (await getAdminTenants()) || [];
const tenants = (await getalltenants()) || [];
const match = bestNameMatch(name, tenants, (t) => t.tenantname);
if (!match) return null;
const [detail, locations] = await Promise.all([
@@ -1194,7 +1185,7 @@ const INTENTS = [
? { title: 'Locations', items: sites.map((l) => ({ label: l.locationname || `Location ${l.locationid}`, meta: l.pincode })) }
: undefined,
sourceCalls: [
{ name: 'getAdminTenants', target: '/admin/tenants', status: 'complete', stats: `matched ${t.tenantname}` },
{ name: 'getalltenants', target: '/admin/tenants', status: 'complete', stats: `matched ${t.tenantname}` },
{ name: 'getAdminTenant', target: `/admin/tenants/${match.tenantid}`, status: detail ? 'complete' : 'error' },
{
name: 'getTenantLocations',
@@ -1339,8 +1330,8 @@ const INTENTS = [
let tenant = null;
let tenants = null;
if (tenantName || ranking?.groupBy === 'tenant') {
tenants = (await getAdminTenants()) || [];
sourceCalls.push({ name: 'getAdminTenants', target: '/admin/tenants', status: 'complete', stats: `${tenants.length} tenants` });
tenants = (await getalltenants()) || [];
sourceCalls.push({ name: 'getalltenants', target: '/admin/tenants', status: 'complete', stats: `${tenants.length} tenants` });
if (tenantName) {
tenant = bestNameMatch(tenantName, tenants, (t) => t.tenantname);
// Unrecognised name — don't quietly drop the filter and answer a
@@ -1457,13 +1448,13 @@ const INTENTS = [
label: 'Rider availability — e.g. "how many riders are active"',
match: (text) => (mentionsRiders(text) ? {} : null),
run: async () => {
const summary = await getMilerSummary();
const summary = await getallridersummary();
return {
headline: `${summary?.active ?? 0} active riders (${summary?.available ?? 0} available, ${
summary?.onDelivery ?? 0
} on a delivery) out of ${summary?.total ?? 0} total.`,
detail: `${summary?.inactive ?? 0} inactive/offline.`,
sourceCalls: [{ name: 'getMilerSummary', target: '/admin/milers', status: 'complete', stats: `${summary?.total ?? 0} riders` }]
sourceCalls: [{ name: 'getallridersummary', target: '/admin/milers', status: 'complete', stats: `${summary?.total ?? 0} riders` }]
};
}
},
@@ -1472,7 +1463,7 @@ const INTENTS = [
label: 'Tenant count — e.g. "how many tenants do we have"',
match: (text) => (/\btenants?\b/i.test(text) && /\bhow many\b|\blist\b|\ball\b/i.test(text) ? {} : null),
run: async () => {
const tenants = (await getAdminTenants()) || [];
const tenants = (await getalltenants()) || [];
return {
headline: `${tenants.length} tenant${tenants.length === 1 ? '' : 's'} total.`,
metric: { value: tenants.length, label: 'Tenants' },
@@ -1484,7 +1475,7 @@ const INTENTS = [
.filter((t) => t.tenantname)
.map((t) => ({ label: t.tenantname, meta: t.tenantid != null ? `#${t.tenantid}` : undefined }))
},
sourceCalls: [{ name: 'getAdminTenants', target: '/admin/tenants', status: 'complete', stats: `${tenants.length} tenants` }]
sourceCalls: [{ name: 'getalltenants', target: '/admin/tenants', status: 'complete', stats: `${tenants.length} tenants` }]
};
}
},
@@ -1870,7 +1861,7 @@ const INTENTS = [
// rather than failing the whole answer.
const scan = await fetchBookingsInRange(start, end);
const rows = scan.rows;
const riders = await getMilerSummary().catch(() => null);
const riders = await getallridersummary().catch(() => null);
return {
headline: `${countPhrase(scan, rows.length)} order${rows.length === 1 ? '' : 's'} ${rangeLabel}.`,
metric: { value: rows.length, label: `Orders ${rangeLabel}${scan.truncated ? ' (at least)' : ''}` },
@@ -1885,8 +1876,8 @@ const INTENTS = [
sourceCalls: [
scanCall(scan, `${rows.length} orders ${rangeLabel}`),
riders
? { name: 'getMilerSummary', target: '/admin/milers', status: 'complete', stats: `${riders.total} riders` }
: { name: 'getMilerSummary', target: '/admin/milers', status: 'error', errorMessage: 'Rider summary unavailable' }
? { name: 'getallridersummary', target: '/admin/milers', status: 'complete', stats: `${riders.total} riders` }
: { name: 'getallridersummary', target: '/admin/milers', status: 'error', errorMessage: 'Rider summary unavailable' }
]
};
}
@@ -2019,7 +2010,7 @@ const INTENTS = [
stats: statusStats(matched),
detail: `Out of ${rows.length} orders created ${describeDay(day)} across all tenants.` + truncationNote(scan),
sourceCalls: [
{ name: 'getAdminTenants', target: '/admin/tenants', status: 'complete' },
{ name: 'getalltenants', target: '/admin/tenants', status: 'complete' },
scanCall(scan, `${rows.length} total → ${matched.length} for ${tenant.tenantname}`)
]
};