File size: 6,854 Bytes
baf8a7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { useEffect, useState } from "react";
import { IdentificationCard, ArrowRight, WarningCircle } from "@phosphor-icons/react";
import { api, type RunSummary, ApiError } from "../lib/api";
import { ErrorNote, Note, Spinner } from "./common";

interface Parsed {
  raw: string; valid: boolean; error: string;
  is_listed: boolean | null; industry_code: string; nic_division_name: string;
  state: string; state_code: string; year: number | null; age_years: number | null;
  ownership_code: string; ownership: string; is_public_company: boolean | null;
  registration_number: string; listing: string;
}

/** Intake for a pre-IPO issuer: no exchange history, so the CIN is the only
 *  record that exists until documents are supplied. */
export function CinIntake({ onStarted }: { onStarted: (run: RunSummary) => void }) {
  const [cin, setCin] = useState("");
  const [name, setName] = useState("");
  const [parsed, setParsed] = useState<Parsed | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [starting, setStarting] = useState(false);

  useEffect(() => {
    const v = cin.trim().toUpperCase();
    if (v.length < 21) { setParsed(null); return; }
    const t = setTimeout(() => {
      api.inspectCin(v).then(setParsed).catch(() => setParsed(null));
    }, 150);
    return () => clearTimeout(t);
  }, [cin]);

  const start = async () => {
    setStarting(true); setError(null);
    try {
      onStarted(await api.createRunFromCin(cin.trim().toUpperCase(), name));
    } catch (e) {
      setError(e instanceof ApiError ? e.message : String(e));
      setStarting(false);
    }
  };

  const seg = (label: string, value: string, chars: string) => (
    <div style={{ display: "grid", gridTemplateColumns: "116px 1fr", gap: 10, fontSize: 12.5 }}>
      <span style={{ color: "var(--text-2)" }}>{label}</span>
      <span>
        <span className="mono">{value}</span>
        <span className="f-id" style={{ display: "block", marginTop: 1 }}>{chars}</span>
      </span>
    </div>
  );

  return (
    <div className="grid cols-sidebar">
      <section className="panel">
        <div className="panel-h">
          <IdentificationCard size={16} />
          <h2>Pre-IPO issuer</h2>
        </div>
        <div className="panel-b">
          <Note tone="quiet">
            An unlisted company has no exchange filing history, so it cannot be found by
            search. Its Corporate Identity Number is a real registry identifier and settles
            several eligibility questions on its own. Financial disclosures stay absent until
            documents are supplied, and the draft will say so rather than fill them in.
          </Note>

          <div className="field" style={{ marginTop: 14 }}>
            <label htmlFor="cin">Corporate Identity Number</label>
            <input id="cin" className="ctl" placeholder="U62090KA2025PTC207753"
                   maxLength={21} value={cin}
                   onChange={(e) => setCin(e.target.value.toUpperCase())} />
            <div className="help">21 characters, as issued by the Ministry of Corporate Affairs.</div>
          </div>

          <div className="field">
            <label htmlFor="coname">Registered name (optional)</label>
            <input id="coname" className="ctl" style={{ fontFamily: "var(--sans)" }}
                   placeholder="As on the certificate of incorporation"
                   value={name} onChange={(e) => setName(e.target.value)} />
            <div className="help">
              Recorded as unverified: a name typed by hand is not corroborated by any register.
            </div>
          </div>

          {cin.trim().length >= 21 && parsed && !parsed.valid && (
            <ErrorNote message={parsed.error} />
          )}

          {error && <div style={{ marginTop: 12 }}><ErrorNote message={error} /></div>}

          <button className="btn btn-primary" onClick={start}
                  disabled={!parsed?.valid || starting}
                  style={{ width: "100%", justifyContent: "center", marginTop: 6 }}>
            {starting ? <><Spinner /> Building ledger</>
                      : <>Build disclosure ledger <ArrowRight size={15} /></>}
          </button>
        </div>
      </section>

      <aside style={{ display: "grid", gap: 14 }}>
        {parsed?.valid ? (
          <>
            <section className="panel">
              <div className="panel-h"><h2>What the identifier encodes</h2></div>
              <div className="panel-b" style={{ display: "grid", gap: 10 }}>
                {seg("Listing", parsed.is_listed ? "Listed" : "Unlisted",
                     `letter ${parsed.listing}, character 1`)}
                {seg("Industry", `${parsed.industry_code}`,
                     parsed.nic_division_name)}
                {seg("Registrar", parsed.state, `code ${parsed.state_code}`)}
                {seg("Incorporated", String(parsed.year ?? ""),
                     `${parsed.age_years} year(s) ago`)}
                {seg("Class", parsed.ownership, `code ${parsed.ownership_code}`)}
                {seg("Registration", parsed.registration_number, "with the Registrar")}
              </div>
            </section>

            {parsed.is_public_company === false && (
              <Note tone="warn">
                <b>A private company cannot make a public issue.</b> The engine will report
                this as a blocker: conversion to a public limited company under the Companies
                Act is a prerequisite, not a drafting fix.
              </Note>
            )}
            {parsed.year !== null && new Date().getFullYear() - parsed.year < 3 && (
              <Note tone="warn">
                <b>Incorporated {parsed.year}.</b> Regulation 229(6) tests operating profit in
                two of the last three financial years. The engine will report whether enough
                financial years can have closed.
              </Note>
            )}
          </>
        ) : (
          <div className="panel">
            <div className="panel-b">
              <div style={{ fontSize: 12.5, color: "var(--text-2)", lineHeight: 1.6 }}>
                Enter a Corporate Identity Number to see what it encodes. Every one of its
                21 characters means something.
              </div>
              <pre className="mono" style={{
                fontSize: 11, lineHeight: 1.7, color: "var(--text-3)",
                marginTop: 12, whiteSpace: "pre-wrap",
              }}>{`U 62090 KA 2025 PTC 207753
β”‚   β”‚    β”‚   β”‚    β”‚    β”” registration no.
β”‚   β”‚    β”‚   β”‚    β”” class of company
β”‚   β”‚    β”‚   β”” year of incorporation
β”‚   β”‚    β”” state of the registrar
β”‚   β”” NIC industry code
β”” L listed, U unlisted`}</pre>
            </div>
          </div>
        )}
      </aside>
    </div>
  );
}