From e5b413ecaca7c0b7f7b40f665725778e059a181b Mon Sep 17 00:00:00 2001 From: Yashas Date: Tue, 8 Sep 2026 13:26:46 +0530 Subject: [PATCH] console: the customer accepts from their own phone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recording the acceptance was the third human touchpoint, and it was a person in the office typing what a customer had said somewhere else. The customer's WhatsApp reply now drives it (81), so this stage has no step of its own. "Record the acceptance" drops from a step to a folded recovery lever — kept, because a customer who says yes on a CALL still needs somebody to record it, and because a reply Engage judged ambiguous has to be actionable by a person. That left Quote Presented with an empty screen at exactly the stage an operator is most likely to wonder whether something has broken, so the waiting state now says so. Grey, and no pulse: nothing is happening, and that is the correct state — unlike the AI strip, where a still dot would mean something is wrong. The reply itself surfaces in two places: the audit trail carries it as "The customer said", and the lead file files it under Proposal beside the acceptance it produced. The evidence and the decision it justifies are one line apart, which matters when nobody in the office made it. Co-Authored-By: Claude Opus 5 (1M context) --- src/api/config.js | 9 +++++++-- src/components/Timeline.jsx | 1 + src/screens/Lead.jsx | 17 ++++++++++++++++- src/screens/screens.css | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/api/config.js b/src/api/config.js index bf472ae..c633368 100644 --- a/src/api/config.js +++ b/src/api/config.js @@ -42,7 +42,7 @@ export const STAGES = [ // 7 September: a lead waited for its three documents inside "Contacted", so // the queue that most needed watching was the one that did not exist. { uid: 'zk-state-docs', name: 'Document Pending', kind: 'needs', need: 'Document collection', by: 'the partner agent' }, - { uid: 'zk-state-quoted', name: 'Quote Presented', kind: 'customer', need: 'Customer decision', by: 'the customer' }, + { uid: 'zk-state-quoted', name: 'Quote Presented', kind: 'customer', need: 'Customer decision', by: 'the customer', doing: 'Waiting for the customer to reply on WhatsApp' }, // The one queue where the machine stops and a person decides. { uid: 'zk-state-referred', name: 'Referred to Underwriting', kind: 'needs', need: 'Underwriting referral', by: 'an underwriter' }, { uid: 'zk-state-payment', name: 'Payment Pending', kind: 'needs', need: 'Premium confirmation', by: 'operations' }, @@ -135,7 +135,12 @@ const STATE_ACTIVITIES = { { uid: 'zk-act-quote', label: 'Generate Quote', by: 'Rating engine', role: 'force' }, ], 'zk-state-quoted': [ - { uid: 'zk-act-accept', label: 'Record the acceptance', by: 'you, on consent', role: 'do' }, + // The customer accepts from their own phone: their WhatsApp reply is + // routed to Customer Reply, Engage reads it, and Engage performs this. + // Kept reachable — role 'force', folded away — because a customer who + // says yes on a CALL still needs somebody to record it, and because a + // reply Engage judged ambiguous has to be actionable by a person. + { uid: 'zk-act-accept', label: 'Record the acceptance by hand', by: 'you, on consent', role: 'force' }, { uid: 'zk-act-quote', label: 'Re-quote', by: 'Engage AI', role: 'again' }, { uid: 'zk-act-collect-docs', label: 'Add a document', by: 'you', role: 'again' }, { uid: 'zk-act-kyc', label: 'Verify KYC', by: 'KYC AI', role: 'force' }, diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index 3c46f7a..a55836f 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -37,6 +37,7 @@ const NARRATIVE = [ ['referral_analysis', 'Referral analysis'], ['uw_decision_notes', 'Underwriting decision'], ['documents_notes', 'Documents'], + ['customer_reply', 'The customer said'], ['lost_reason', 'Why it was dropped'], ['resume_note', 'Why now'], ] diff --git a/src/screens/Lead.jsx b/src/screens/Lead.jsx index 4ecf97a..f1d2512 100644 --- a/src/screens/Lead.jsx +++ b/src/screens/Lead.jsx @@ -69,7 +69,7 @@ const GROUPS = [ ['Motor risk', ['motor_reg_no','motor_make_model','motor_mfg_year','motor_cc','motor_fuel','motor_idv','motor_ncb_pct','motor_addons','motor_prev_insurer','motor_prev_policy_no','motor_prev_expiry','motor_prev_claim']], ['AI advice', ['ai_recommended_cover','ai_recommended_addons','ai_recommendation_rationale','ai_recommendation_confidence']], ['Quote', ['product_code','quoted_od_premium','quoted_tp_premium','quoted_addon_premium','quoted_section_premiums','quoted_gst','quoted_premium','quoted_breakup','quote_valid_till']], - ['Proposal', ['acceptance_ref','accepted_at']], + ['Proposal', ['acceptance_ref','accepted_at','customer_reply','customer_reply_from']], ['KYC', ['kyc_mode','kyc_ref','kyc_outcome','kyc_mismatch_notes']], ['Underwriting', ['uw_outcome','uw_survey_required','referral_analysis','uw_referral_reason','uw_decision_notes']], ['Policy', ['payment_link','payment_ref','premium_realised','realised_at','policy_no','policy_issued_at']], @@ -368,6 +368,21 @@ export default function Lead() { + ) : stage?.kind === 'customer' && stage.doing ? ( + /* Not an agent working, and not a task of yours either — the lead + is with someone outside the business. Without this the screen + went blank at exactly the stage where an operator is most likely + to wonder whether something has broken. */ +
+
) : stage?.kind === 'auto' ? (