import * as React from "react"; import { ChevronDown, ChevronUp, FileText, Tag } from "lucide-react"; import { cn } from "@/lib/utils"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type PolicyType = | "Income" | "Security" | "Serviceability" | "Loan Type" | "Borrower"; export type PolicyQueryType = | "single_bank" | "cross_bank_comparison" | "threshold_filter" | "ranking" | "scenario_match" | "general"; export type PolicyVerdict = "yes" | "soft_no" | "no" | "unknown"; /** What the Policy AI classifier detected about the broker's query. */ export interface PolicyQueryContext { policyType: PolicyType; /** Fine-grained categories, e.g. ["Bonus Income", "Add Backs"] */ categories: string[]; queryType: PolicyQueryType; /** Only set for single_bank queries. */ bankName?: string; } /** A single citation attached to a Policy AI response. */ export interface PolicyCitationItem { /** 1-based index used inline as [N] markers in the answer text. */ index: number; bankName: string; category: string; /** Excerpt from the source policy document. */ excerpt: string; } /** A bank's verdict in a cross-bank comparison. */ export interface PolicyBankVerdict { bankName: string; verdict: PolicyVerdict; /** Brief human-readable explanation from the policy engine. */ details?: string; citations?: number[]; } /** A bank's entry in a TOPSIS-ranked list. */ export interface PolicyRankedBankItem { rank: number; bankName: string; /** TOPSIS closeness score 0.0–1.0 (higher = better). */ score: number; verdict: PolicyVerdict; /** Key policy highlights shown as bullets under the bank row. */ highlights: string[]; citations?: number[]; } /** * Discriminated union of the three response content formats that Policy AI * can return, corresponding to retrieval tiers A / B / C. */ export type PolicyResponseContent = | { type: "single_bank"; bankName: string; verdict: PolicyVerdict; /** Prose answer with inline [N] citation markers. */ answer: string; citations: PolicyCitationItem[]; } | { type: "cross_bank_comparison"; categories: string[]; banks: PolicyBankVerdict[]; summaryCounts: { yes: number; softNo: number; no: number; total: number; }; citations: PolicyCitationItem[]; } | { type: "ranked_list"; categories: string[]; banks: PolicyRankedBankItem[]; citations: PolicyCitationItem[]; }; /** * A past conversation entry — reserved for the conversation history feature. * Used when rendering a list of previous Policy AI sessions (inline mode). */ export interface PolicyConversationItem { id: string; /** First user query — used as the conversation title. */ title: string; /** Human-readable timestamp, e.g. "Today, 10:24 AM". */ timestamp?: string; } /** A single message in the Policy AI chat. */ export interface PolicyAIMessage { id: string; role: "user" | "assistant"; content: string; /** Classification info shown above the response as a query chip. */ queryContext?: PolicyQueryContext; /** Structured response payload (absent for user messages). */ responseContent?: PolicyResponseContent; } // --------------------------------------------------------------------------- // PolicyQueryChip props // --------------------------------------------------------------------------- export interface PolicyQueryChipProps { context: PolicyQueryContext; className?: string; } // --------------------------------------------------------------------------- // PolicyVerdictBadge props // --------------------------------------------------------------------------- export interface PolicyVerdictBadgeProps { verdict: PolicyVerdict; className?: string; } // --------------------------------------------------------------------------- // PolicyCitationPanel props // --------------------------------------------------------------------------- export interface PolicyCitationPanelProps { citations: PolicyCitationItem[]; className?: string; } // --------------------------------------------------------------------------- // Internal helpers // --------------------------------------------------------------------------- const POLICY_TYPE_COLORS: Record = { Income: "text-primary", Security: "text-warning", Serviceability: "text-info", "Loan Type": "text-secondary", Borrower: "text-success", }; const QUERY_TYPE_LABELS: Record = { single_bank: "Single bank", cross_bank_comparison: "Cross-bank", threshold_filter: "Filter", ranking: "Ranking", scenario_match: "Scenario", general: "General", }; // --------------------------------------------------------------------------- // PolicyQueryChip (Atom) // --------------------------------------------------------------------------- /** * Chip rendered above every Policy AI assistant response. * Shows the detected policy type, categories, and query routing type — * equivalent to Jira Rovo's "Searching Confluence…" context indicator. * * @example * */ export function PolicyQueryChip({ context, className }: PolicyQueryChipProps) { const { policyType, categories, queryType, bankName } = context; const typeColor = POLICY_TYPE_COLORS[policyType]; return (
); } // --------------------------------------------------------------------------- // PolicyVerdictBadge (Atom) // --------------------------------------------------------------------------- const VERDICT_CONFIG: Record< PolicyVerdict, { variant: "success" | "warning" | "destructive" | "secondary"; label: string; } > = { yes: { variant: "success", label: "Yes" }, soft_no: { variant: "warning", label: "Soft No" }, no: { variant: "destructive", label: "No" }, unknown: { variant: "secondary", label: "—" }, }; /** * Coloured badge indicating a bank's lending policy verdict. * - Yes → success (green) * - Soft No → warning (amber) — accepted on specialist/non-prime products * - No → destructive (red) — not accepted * - Unknown → secondary — data not available */ export function PolicyVerdictBadge({ verdict, className, }: PolicyVerdictBadgeProps) { const { variant, label } = VERDICT_CONFIG[verdict]; return ( {label} ); } // --------------------------------------------------------------------------- // PolicyCitationPanel (Molecule) // --------------------------------------------------------------------------- /** * Collapsible panel listing the policy document sources behind an AI response. * Rendered below every response format (SingleBankAnswer, ComparisonTable, RankedList). * Collapsed by default; expands on toggle. */ export function PolicyCitationPanel({ citations, className, }: PolicyCitationPanelProps) { const [open, setOpen] = React.useState(false); if (citations.length === 0) return null; return (
{open && (
    {citations.map((cite) => (
  1. {/* Index chip */} {cite.index}
    {cite.bankName} {cite.category}

    {cite.excerpt}

  2. ))}
)}
); }