Catalyst / admin/Apex 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / Apex

public

Bid Management and Orchestration Tool with AI Capability

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
Apex / Synapse-Apexv2 / frontend / src / pages / Capabilities.tsx 3964 B · main
 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
import { useEffect, useState } from "react";
import { api } from "../lib/api";

export default function Capabilities() {
  const [caps, setCaps] = useState<any[]>([]);
  const [frameworks, setFrameworks] = useState<any[]>([]);
  const [selFw, setSelFw] = useState<number[]>([]);
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState("");

  async function load() { setCaps(await api.get("/capabilities")); }
  useEffect(() => {
    load();
    api.get("/bids/meta/frameworks").then(setFrameworks);
  }, []);

  async function upload(e: React.ChangeEvent<HTMLInputElement>) {
    if (!e.target.files?.length) return;
    setBusy(true);
    setMsg("");
    const fd = new FormData();
    fd.append("kind", "service");
    fd.append("framework_ids", selFw.join(","));
    Array.from(e.target.files).forEach((f) => fd.append("files", f));
    try {
      await api.postForm("/capabilities/upload", fd);
      setMsg("Extraction started — capabilities will appear shortly. Click Refresh.");
    } catch (err: any) { setMsg(err.message); }
    setBusy(false);
  }

  async function approve(id: number) { await api.post(`/capabilities/${id}/approve`); load(); }
  async function del(id: number) { await api.del(`/capabilities/${id}`); load(); }
  async function saveResp(rid: number, text: string) { await api.patch(`/capabilities/responses/${rid}`, { response_text: text }); }
  async function regen(rid: number) { await api.post(`/capabilities/responses/${rid}/regenerate`); setMsg("Regeneration queued."); }

  return (
    <div>
      <div className="topbar"><div><h1>Capability library</h1><span className="muted">Module 1  service & architecture ingestion</span></div></div>
      <div className="card">
        <h2>Ingest documents</h2>
        <label>Align generated responses to frameworks</label>
        <select multiple value={selFw.map(String)} onChange={(e) => setSelFw(Array.from(e.target.selectedOptions).map((o) => Number(o.value)))} style={{ maxWidth: 320 }}>
          {frameworks.map((f) => <option key={f.id} value={f.id}>{f.name}</option>)}
        </select>
        <div className="row" style={{ marginTop: 10 }}>
          <label className="secondary" style={{ border: "1px solid var(--apex-indigo)", padding: "8px 14px", borderRadius: 8, color: "var(--apex-indigo)", cursor: "pointer", width: "auto", margin: 0 }}>
            {busy ? "Uploading…" : "Upload service / architecture docs"}
            <input type="file" multiple hidden onChange={upload} accept=".pdf,.docx,.xlsx,.txt,.md" />
          </label>
          <button className="ghost" onClick={load}>Refresh</button>
        </div>
        {msg && <p className="muted">{msg}</p>}
      </div>

      {caps.map((c) => (
        <div className="card" key={c.id}>
          <div className="spread">
            <div><b>{c.name}</b> <span className="chip">{c.category}</span> <span className={"chip " + (c.status === "approved" ? "ok" : "")}>{c.status}</span></div>
            <div className="row">
              {c.status !== "approved" && <button className="secondary" onClick={() => approve(c.id)}>Approve for reuse</button>}
              <button className="iconbtn danger" onClick={() => del(c.id)}></button>
            </div>
          </div>
          <p className="muted" style={{ fontSize: 14 }}>{c.description}</p>
          {c.responses.map((r: any) => (
            <div key={r.id} style={{ marginTop: 8 }}>
              <label>Framework-aligned response {r.approved && <span className="chip ok">approved</span>}</label>
              <textarea rows={4} defaultValue={r.response_text} onBlur={(e) => saveResp(r.id, e.target.value)} />
              <button className="ghost" style={{ marginTop: 4 }} onClick={() => regen(r.id)}>Regenerate</button>
            </div>
          ))}
        </div>
      ))}
      {caps.length === 0 && <div className="card muted">No capabilities yet. Upload documents to extract them.</div>}
    </div>
  );
}