import { useEffect, useMemo, useRef, useState } from "react"; import { FileText, ArrowRight, Sliders } from "@phosphor-icons/react"; import { api, inr, ApiError, type DraftSection, type IssueTerms, type RunSummary, } from "../lib/api"; import { CitationPopover, Empty, ErrorNote, Note, Spinner, TableSkeleton } from "./common"; export function Draft({ run, onNext, onRunUpdate, }: { run: RunSummary; onNext: () => void; onRunUpdate: (r: RunSummary) => void }) { const [sections, setSections] = useState(null); const [issue, setIssue] = useState(null); const [active, setActive] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const docRef = useRef(null); const load = async () => { setError(null); setSections(null); try { const [d, t] = await Promise.all([api.draft(run.id), api.getIssue(run.id)]); setSections(d.sections); setIssue(t); setActive(d.sections[0]?.id ?? ""); onRunUpdate(d); } catch (e) { setError(e instanceof ApiError ? e.message : String(e)); } }; useEffect(() => { load(); }, [run.id]); const current = useMemo( () => sections?.find((s) => s.id === active) ?? sections?.[0], [sections, active], ); const saveIssue = async (next: IssueTerms) => { setBusy(true); try { const saved = await api.putIssue(run.id, next); setIssue(saved); const d = await api.draft(run.id); setSections(d.sections); onRunUpdate(d); } catch (e) { setError(e instanceof ApiError ? e.message : String(e)); } finally { setBusy(false); } }; return ( <>

Offer document draft

Drafted from the ledger alone. Templates carry tokens, never figures, so a number that is not bound to a sourced fact cannot reach the page. Hover any figure to see the filing and page it came from. Figures in grey dashes are your proposed issue terms, not facts about the issuer.

{error &&
}
{!sections ? (
) : !current ? (
The draft has not been generated.
) : (

{current.number}. {current.title}

{current.icdr_clause}
{current.word_count} words · {current.fact_ids.length} sourced figures · narrative from {current.narrative_source}
)}
); } function IssueForm({ issue, busy, onSave, }: { issue: IssueTerms; busy: boolean; onSave: (t: IssueTerms) => void }) { const [d, setD] = useState(issue); useEffect(() => setD(issue), [issue]); const num = (k: keyof IssueTerms) => ({ value: String(d[k] ?? 0), onChange: (e: React.ChangeEvent) => setD({ ...d, [k]: Number(e.target.value) || 0 }), }); const gross = d.fresh_issue_shares * d.price_per_share; const net = gross - d.issue_expenses; const objTotal = d.objects.reduce((a, o) => a + o.amount, 0); const gap = objTotal - net; return ( <>
Gross proceeds {inr(gross)}
{inr(d.ofs_shares * d.price_per_share)}, capped at 20% of the issue for an SME
Net proceeds {inr(net)}
Objects of the issue
{d.objects.map((o, i) => (
{o.label} { const objects = [...d.objects]; objects[i] = { ...o, amount: Number(e.target.value) || 0 }; setD({ ...d, objects }); }} />
))}
Objects total {inr(objTotal)}
Net proceeds available {inr(net)}
{Math.abs(gap) > 1 && (
{gap > 0 ? "over by " : "short by "}{inr(Math.abs(gap))}
)}
); }