Compare commits
2 Commits
54333ef847
...
7c328d2b88
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c328d2b88 | |||
| 3dd482f180 |
@ -52,6 +52,11 @@ export const STAGES = [
|
|||||||
// policy nobody paid for. It gets its own band on the overview so the role
|
// policy nobody paid for. It gets its own band on the overview so the role
|
||||||
// that owns it does not have to find it in a list of things mostly handled
|
// that owns it does not have to find it in a list of things mostly handled
|
||||||
// by somebody else.
|
// by somebody else.
|
||||||
|
// WHERE THE AI PUTS DOWN WHAT IT CANNOT HONESTLY FINISH. An AI employee has
|
||||||
|
// no way to raise a ticket and no way to send a message; the only move it
|
||||||
|
// has for "a person should take this" is Flag for Review, and it lands here
|
||||||
|
// with the reason it wrote. Nothing is running on these leads.
|
||||||
|
{ uid: 'zk-state-review', name: 'Needs a Person', kind: 'needs', need: 'Flagged by the AI', by: 'operations' },
|
||||||
{ uid: 'zk-state-payment', name: 'Payment Pending', kind: 'needs', need: 'Premium confirmation', by: 'operations', approval: 'ops_admin', approvalLabel: 'Premium awaiting your confirmation', approvalNote: 'Cover cannot start until the premium is received — section 64VB. Only operations can confirm it.' },
|
{ uid: 'zk-state-payment', name: 'Payment Pending', kind: 'needs', need: 'Premium confirmation', by: 'operations', approval: 'ops_admin', approvalLabel: 'Premium awaiting your confirmation', approvalNote: 'Cover cannot start until the premium is received — section 64VB. Only operations can confirm it.' },
|
||||||
{ uid: 'zk-state-issued', name: 'Policy Issued', kind: 'auto', doing: 'Closing the file', by: 'Engage AI' },
|
{ uid: 'zk-state-issued', name: 'Policy Issued', kind: 'auto', doing: 'Closing the file', by: 'Engage AI' },
|
||||||
// Nurture. There is no scheduler yet, so Resume Outreach is a button and it
|
// Nurture. There is no scheduler yet, so Resume Outreach is a button and it
|
||||||
@ -160,6 +165,10 @@ const STATE_ACTIVITIES = {
|
|||||||
{ uid: 'zk-act-uw-decline', label: 'Decline the risk', by: 'you', role: 'do' },
|
{ uid: 'zk-act-uw-decline', label: 'Decline the risk', by: 'you', role: 'do' },
|
||||||
{ uid: 'zk-act-uw-prepare', label: 'Prepare Referral', by: 'Referral AI', role: 'force' },
|
{ uid: 'zk-act-uw-prepare', label: 'Prepare Referral', by: 'Referral AI', role: 'force' },
|
||||||
],
|
],
|
||||||
|
'zk-state-review': [
|
||||||
|
{ uid: 'zk-act-review-resume', label: 'Send back to the workflow', by: 'you', role: 'do' },
|
||||||
|
{ uid: 'zk-act-review-refer', label: 'Send to underwriting', by: 'you', role: 'do' },
|
||||||
|
],
|
||||||
'zk-state-payment': [
|
'zk-state-payment': [
|
||||||
{ uid: 'zk-act-realise', label: 'Confirm premium received', by: 'you', role: 'do' },
|
{ uid: 'zk-act-realise', label: 'Confirm premium received', by: 'you', role: 'do' },
|
||||||
// Not offered: the chase is a SCHEDULED activity now (86), booked 24h
|
// Not offered: the chase is a SCHEDULED activity now (86), booked 24h
|
||||||
|
|||||||
@ -56,6 +56,14 @@ export const ACTIVITY_ROLES = {
|
|||||||
'zk-act-realise': ['ops_admin'],
|
'zk-act-realise': ['ops_admin'],
|
||||||
'zk-act-onboard': ['ops_admin', 'ai_engage'],
|
'zk-act-onboard': ['ops_admin', 'ai_engage'],
|
||||||
'zk-act-resume': ['ops_admin', 'partner_agent', 'bank_rm', 'csr_direct'],
|
'zk-act-resume': ['ops_admin', 'partner_agent', 'bank_rm', 'csr_direct'],
|
||||||
|
|
||||||
|
// The AI's one way of saying "a person should take this". Every employee
|
||||||
|
// holds it, and so does ops — a person can park a lead for the same reasons.
|
||||||
|
'zk-act-flag-review': ['ops_admin', 'ai_intake', 'ai_engage', 'ai_kyc', 'ai_advisor', 'ai_uw_referral'],
|
||||||
|
// And the two ways back out of Needs a Person. The underwriter is here too:
|
||||||
|
// a flag they are sent is theirs to route, not only ops'.
|
||||||
|
'zk-act-review-resume':['ops_admin', 'sme_underwriter'],
|
||||||
|
'zk-act-review-refer': ['ops_admin', 'sme_underwriter'],
|
||||||
'zk-act-drop': ['ops_admin', 'partner_agent', 'bank_rm', 'csr_direct'],
|
'zk-act-drop': ['ops_admin', 'partner_agent', 'bank_rm', 'csr_direct'],
|
||||||
|
|
||||||
// ── The one place this mirror deliberately DISAGREES with the workflow ──
|
// ── The one place this mirror deliberately DISAGREES with the workflow ──
|
||||||
|
|||||||
@ -45,8 +45,22 @@ const MONEY = new Set(['quoted_premium','quoted_od_premium','quoted_tp_premium',
|
|||||||
'sme_burglary_si','sme_ee_si','sme_bi_gross_profit','sme_claims_36m_amount','sme_stock_si'])
|
'sme_burglary_si','sme_ee_si','sme_bi_gross_profit','sme_claims_36m_amount','sme_stock_si'])
|
||||||
|
|
||||||
/** Field groups, in the order the lead was actually worked. */
|
/** Field groups, in the order the lead was actually worked. */
|
||||||
|
/* What the AI said stopped it, turned into a sentence an operator can act on.
|
||||||
|
Keyed by the stored value so an unknown one degrades to the generic line. */
|
||||||
|
const BLOCKER = {
|
||||||
|
missing_evidence: 'It says the evidence it needed was not there.',
|
||||||
|
contradictory_data: 'It says the evidence it had contradicts itself.',
|
||||||
|
tool_failed: 'It says a lookup or a tool it depends on failed.',
|
||||||
|
outside_my_remit: 'It says the judgement is a person\u2019s to make, not its own.',
|
||||||
|
other: 'It could not complete its step.',
|
||||||
|
}
|
||||||
|
|
||||||
const GROUPS = [
|
const GROUPS = [
|
||||||
['Source', ['lead_ref','product_line','source_channel','partner_code','partner_branch','rm_or_agent_id','consent_artefact']],
|
['Source', ['lead_ref','product_line','source_channel','partner_code','partner_branch','rm_or_agent_id','consent_artefact']],
|
||||||
|
// First, because when a lead is flagged this is the only thing anyone opens
|
||||||
|
// it to read. Empty on every lead that was never flagged, and an empty group
|
||||||
|
// does not render.
|
||||||
|
['Flagged for review', ['review_blocker','review_reason','review_return','review_note']],
|
||||||
['Customer', ['customer_name','entity_name','mobile','email','pan','gstin','udyam_no']],
|
['Customer', ['customer_name','entity_name','mobile','email','pan','gstin','udyam_no']],
|
||||||
['Intake', ['lead_score','attribution_status','attribution_reason','dedupe_match_ref','eligibility_outcome','eligibility_reason']],
|
['Intake', ['lead_score','attribution_status','attribution_reason','dedupe_match_ref','eligibility_outcome','eligibility_reason']],
|
||||||
['Contact', ['contact_outcome','outreach_window','contact_notes','call_transcript']],
|
['Contact', ['contact_outcome','outreach_window','contact_notes','call_transcript']],
|
||||||
@ -430,6 +444,31 @@ export default function Lead() {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
) : stage?.uid === 'zk-state-review' ? (
|
||||||
|
/* THE AI PUT THIS DOWN. It could not finish its step honestly and said
|
||||||
|
so in the workflow, which is the only way it has of reaching a person.
|
||||||
|
Its own words lead, because deciding where the lead resumes is a
|
||||||
|
judgement made on them. */
|
||||||
|
<div className="attn" role="status">
|
||||||
|
<div>
|
||||||
|
<h3><span className="dot dot--amber" />The AI stopped here and asked for a person</h3>
|
||||||
|
{row.review_reason ? <p className="attn__said">“{row.review_reason}”</p> : null}
|
||||||
|
<p>
|
||||||
|
{BLOCKER[row.review_blocker] ?? 'It could not complete its step.'}{' '}
|
||||||
|
Nothing is running on this lead. Read the trail, fix what is missing, then send it
|
||||||
|
back to the workflow at the right step — or hand it to an underwriter.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="attn__acts">
|
||||||
|
{actions.filter((a) => a.role === 'do').map((a) => (
|
||||||
|
<button key={a.uid} type="button"
|
||||||
|
className={'btn ' + (a.uid === 'zk-act-review-resume' ? 'btn--navy' : 'btn--ghost')}
|
||||||
|
onClick={() => { setOpen(a.uid); goToAction() }}>
|
||||||
|
{a.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : primaryAction ? (
|
) : primaryAction ? (
|
||||||
<div className="attn" role="status">
|
<div className="attn" role="status">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -388,3 +388,12 @@
|
|||||||
.kpis { grid-template-columns: repeat(2, 1fr); row-gap: 16px; }
|
.kpis { grid-template-columns: repeat(2, 1fr); row-gap: 16px; }
|
||||||
.facts { grid-template-columns: repeat(2, 1fr); row-gap: 16px; }
|
.facts { grid-template-columns: repeat(2, 1fr); row-gap: 16px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The AI's own words, when it flagged a lead. Quoted, because deciding what
|
||||||
|
to do next is a judgement made on the sentence it wrote. */
|
||||||
|
.attn__said {
|
||||||
|
margin: 8px 0 0 !important; padding: 8px 14px;
|
||||||
|
border-left: 2px solid var(--zk-amber-bar); background: #fff;
|
||||||
|
border-radius: 0 var(--r-sm) var(--r-sm) 0;
|
||||||
|
font-size: var(--fs-md) !important; color: var(--zk-ink) !important; max-width: 78ch;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user