updates on the issue and the another thing updates on the ai optimization

This commit is contained in:
2026-09-23 11:58:24 +05:30
parent c6dfa3cb8f
commit 7544a07066
17 changed files with 622 additions and 104 deletions

View File

@@ -0,0 +1,88 @@
import { useState } from 'react';
import PropTypes from 'prop-types';
import AddressAutocomplete from '@/components/doormile/AddressAutocomplete';
// ==============================|| Doormile AI — the address turn ||============================== //
//
// The address step, as a real search field rather than a line typed into the
// chat box.
//
// It used to be an ordinary `text` step: the assistant asked for "the full
// address" and the operator typed one into the composer, which was then
// geocoded blind. Two things were wrong with that. They got no suggestions, so
// an area or street had to be remembered and spelled the way the geocoder
// wanted; and when it missed, all that came back was "I couldn't find that
// address", with no sight of what it HAD found.
//
// This is the same `AddressAutocomplete` the Create Order page uses, so the
// two surfaces resolve an address the same way and cannot drift.
//
// Picking from the list also hands the flow the coordinates the operator
// actually chose, rather than whatever a second geocode of the same words
// returns — see the `place` branch in orderFlow's resolve. That matters: the
// dispatch optimiser routes on coordinates, and re-geocoding a label can
// legitimately return a different place.
//
// Typing free text and pressing the composer still works. This is an extra way
// in, not a replacement for one.
const AIAddressStep = ({ step, isBusy, onChoose, onCancel }) => {
const [text, setText] = useState('');
const [picked, setPicked] = useState(null);
return (
<div className="w-full border border-slate-200/90 rounded-2xl bg-white p-3.5 sm:p-4 mb-3.5 shadow-3xs space-y-3">
<div className="text-xs font-bold text-slate-800">{step.ask}</div>
<AddressAutocomplete
id={`ai-address-${step.id}`}
value={text}
disabled={isBusy}
onChange={(v) => {
setText(v);
// Editing after a pick invalidates it. Those coordinates belonged to
// the old selection and must not ride along with new words — that is
// how a booking ends up located somewhere nobody chose.
setPicked(null);
}}
onPlaceSelected={(place) => {
setPicked(place);
setText(place?.formatted_address || place?.name || '');
}}
/>
{picked && (
<div className="text-[11px] text-emerald-700 font-medium">
Location set — {picked.city || picked.suburb || 'coordinates captured'}
</div>
)}
<div className="flex items-center gap-2">
<button
type="button"
disabled={isBusy || !text.trim()}
onClick={() => onChoose(text.trim(), picked ? { place: picked } : undefined)}
className="px-4 py-2 bg-brand hover:bg-brand-dark text-white font-semibold text-xs rounded-xl shadow-xs active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed transition-all cursor-pointer"
>
Continue
</button>
<button
type="button"
onClick={onCancel}
className="px-3 py-2 text-slate-500 hover:text-slate-800 text-xs font-medium cursor-pointer"
>
Cancel
</button>
</div>
</div>
);
};
AIAddressStep.propTypes = {
step: PropTypes.shape({ id: PropTypes.string, ask: PropTypes.string }).isRequired,
isBusy: PropTypes.bool,
onChoose: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired
};
export default AIAddressStep;

View File

@@ -12,6 +12,7 @@ import { IconButton } from '@/components/ds';
import { Spark, Metric, StatGrid, StateBlock, AnswerList } from './AIParts';
import AIFlowStep from './AIFlowStep';
import AIAddressStep from './AIAddressStep';
import AIRowsStep from './AIRowsStep';
import { failedRowsCsv, downloadCsv } from '@/lib/assistant/bulkFile';
@@ -127,6 +128,19 @@ const AssistantMessage = ({ message, onCopy, onSubmitForm, onCancelAction, onCho
/>
)}
{/* The address turn. Without this branch the step passed the gate in
AIPanel and nothing drew it — the question appeared with an empty
space beneath it, which is worse than the free-text version it
replaced. */}
{message.flowStep?.type === 'address' && !message.flowStep.__answered && (
<AIAddressStep
step={message.flowStep}
isBusy={message.flowStep.__answered}
onChoose={(value, option) => onChooseStep(message, value, option)}
onCancel={() => onCancelAction(message)}
/>
)}
{message.flowStep?.type === 'select' && (
<AIFlowStep
step={message.flowStep}

View File

@@ -400,7 +400,11 @@ const AIPanel = ({ isOpen, onClose }) => {
text: next.ask || step.ask,
// A `select` or `rows` step carries the step definition so AIMessage
// can render its control; a `text` step is answered in the composer.
flowStep: step.type === 'select' || step.type === 'rows' ? step : undefined,
// 'address' joins the list so the sidebar gets the same search
// field as Home. A step type absent here falls through to the plain
// composer, which is how the address step ended up asking for a full
// address as free text.
flowStep: ['select', 'rows', 'address'].includes(step.type) ? step : undefined,
flowDraft: next.flow.draft
});
return;

View File

@@ -2090,30 +2090,6 @@ 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"