a2a-source-edit: write frontend/src/App.jsx
This commit is contained in:
@@ -44,6 +44,7 @@ export function App() {
|
|||||||
() => (config && needsSignIn ? signInUrl(config) : null),
|
() => (config && needsSignIn ? signInUrl(config) : null),
|
||||||
[config, needsSignIn],
|
[config, needsSignIn],
|
||||||
);
|
);
|
||||||
|
const resultState = running ? "loading" : error ? "error" : result?.status === "ok" ? "success" : result ? "error" : "idle";
|
||||||
|
|
||||||
async function runTriage(event) {
|
async function runTriage(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -58,6 +59,9 @@ export function App() {
|
|||||||
max_messages: Number(maxMessages),
|
max_messages: Number(maxMessages),
|
||||||
});
|
});
|
||||||
setResult(data);
|
setResult(data);
|
||||||
|
if (data?.status && data.status !== "ok") {
|
||||||
|
setError(data.summary || "The inbox could not be triaged. Check the pasted messages and try again.");
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message || String(err));
|
setError(err.message || String(err));
|
||||||
} finally {
|
} finally {
|
||||||
@@ -157,57 +161,61 @@ export function App() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{error ? <section className="error">{error}</section> : null}
|
<section className="results" data-testid="agent-result" data-state={resultState} aria-live="polite">
|
||||||
|
{running ? <p>Triaging the shared inbox and drafting approval-ready replies...</p> : null}
|
||||||
|
{error ? <section className="error">{error}</section> : null}
|
||||||
|
{!running && !error && !result ? <p className="empty-state">Run the prefilled fixture to generate a prioritized queue, drafts, receipt, and JSON download.</p> : null}
|
||||||
|
|
||||||
{result ? (
|
{result ? (
|
||||||
<section className="results" data-testid="agent-result">
|
<>
|
||||||
<div className="result-header">
|
<div className="result-header">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Structured result</p>
|
<p className="eyebrow">Structured result</p>
|
||||||
<h2>{result.summary || result.status}</h2>
|
<h2>{result.summary || result.status}</h2>
|
||||||
|
</div>
|
||||||
|
{result.artifact ? (
|
||||||
|
<button data-testid="agent-download" type="button" onClick={downloadReport}>
|
||||||
|
Download {result.artifact.name}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{result.artifact ? (
|
|
||||||
<button data-testid="agent-download" type="button" onClick={downloadReport}>
|
<p className="boundary">{result.approval_boundary}</p>
|
||||||
Download {result.artifact.name}
|
{result.receipt ? (
|
||||||
</button>
|
<p className={result.receipt.persisted ? "receipt ok" : "receipt warn"}>
|
||||||
|
Receipt {result.receipt.receipt_id}: {result.receipt.message}
|
||||||
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="boundary">{result.approval_boundary}</p>
|
<div className="queue">
|
||||||
{result.receipt ? (
|
{(result.queue || []).map((item) => (
|
||||||
<p className={result.receipt.persisted ? "receipt ok" : "receipt warn"}>
|
<article className={`queue-card ${item.priority}`} key={item.message_id}>
|
||||||
Receipt {result.receipt.receipt_id}: {result.receipt.message}
|
<div className="queue-top">
|
||||||
</p>
|
<span className="pill">{item.priority}</span>
|
||||||
) : null}
|
<span>{item.category}</span>
|
||||||
|
|
||||||
<div className="queue">
|
|
||||||
{(result.queue || []).map((item) => (
|
|
||||||
<article className={`queue-card ${item.priority}`} key={item.message_id}>
|
|
||||||
<div className="queue-top">
|
|
||||||
<span className="pill">{item.priority}</span>
|
|
||||||
<span>{item.category}</span>
|
|
||||||
</div>
|
|
||||||
<h3>{item.subject}</h3>
|
|
||||||
<p><strong>From:</strong> {item.sender}</p>
|
|
||||||
<p><strong>Why:</strong> {item.reason}</p>
|
|
||||||
<p><strong>Next:</strong> {item.next_action}</p>
|
|
||||||
<details open>
|
|
||||||
<summary>Approval-ready draft</summary>
|
|
||||||
<div className="draft">
|
|
||||||
<p><strong>To:</strong> {item.draft.to}</p>
|
|
||||||
<p><strong>Subject:</strong> {item.draft.subject}</p>
|
|
||||||
<pre>{item.draft.body}</pre>
|
|
||||||
<p><strong>Approval reason:</strong> {item.draft.approval_reason}</p>
|
|
||||||
<ul>
|
|
||||||
{(item.draft.policy_basis || []).map((basis) => <li key={basis}>{basis}</li>)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</details>
|
<h3>{item.subject}</h3>
|
||||||
</article>
|
<p><strong>From:</strong> {item.sender}</p>
|
||||||
))}
|
<p><strong>Why:</strong> {item.reason}</p>
|
||||||
</div>
|
<p><strong>Next:</strong> {item.next_action}</p>
|
||||||
</section>
|
<details open>
|
||||||
) : null}
|
<summary>Approval-ready draft</summary>
|
||||||
|
<div className="draft">
|
||||||
|
<p><strong>To:</strong> {item.draft.to}</p>
|
||||||
|
<p><strong>Subject:</strong> {item.draft.subject}</p>
|
||||||
|
<pre>{item.draft.body}</pre>
|
||||||
|
<p><strong>Approval reason:</strong> {item.draft.approval_reason}</p>
|
||||||
|
<ul>
|
||||||
|
{(item.draft.policy_basis || []).map((basis) => <li key={basis}>{basis}</li>)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user