mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
929bec8f31
Stop blocking AI imports/proposals on missing non-critical fields. Records the chat agent drafts now save with safe placeholders, auto-generated file numbers, and a source="ai" marker that surfaces an "Added by AI" badge so a clinician can review/edit them later. Backend: - add `source` (manual|ai) column to patients/appointments/prescriptions (migration 0014) + canonical types, services, validation schemas - relax patient/appointment validation: empty file number allowed, demographic + type/provider/initials fall back to placeholders (initials derived from name) - patients.generateFileNumber() auto-assigns an MRN when one is missing - proposeAppointment accepts a name when no file number resolves; AI commits + /api/ai/import stamp source="ai" Frontend: - `source` on Appointment/Patient/Prescription types; AI commits send source="ai" - reusable <AiBadge> shown on the Patients table/detail and prescriptions list Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
223 lines
6.7 KiB
TypeScript
223 lines
6.7 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Confirmation,
|
|
ConfirmationAccepted,
|
|
ConfirmationAction,
|
|
ConfirmationActions,
|
|
ConfirmationRejected,
|
|
ConfirmationRequest,
|
|
ConfirmationTitle,
|
|
} from "@/components/ai-elements/confirmation";
|
|
import {
|
|
Tool,
|
|
ToolContent,
|
|
ToolHeader,
|
|
ToolInput,
|
|
ToolOutput,
|
|
} from "@/components/ai-elements/tool";
|
|
import type { ToolUIPart } from "ai";
|
|
import { CheckIcon, XIcon } from "lucide-react";
|
|
import { nanoid } from "nanoid";
|
|
|
|
const handleReject = () => {
|
|
// In production, call addConfirmationResponse
|
|
};
|
|
|
|
const handleAccept = () => {
|
|
// In production, call addConfirmationResponse
|
|
};
|
|
|
|
const toolCall: ToolUIPart = {
|
|
errorText: undefined,
|
|
input: {
|
|
database: "analytics",
|
|
params: ["2024-01-01"],
|
|
query: "SELECT COUNT(*) FROM users WHERE created_at >= ?",
|
|
},
|
|
output: `| User ID | Name | Email | Created At |
|
|
|---------|------|-------|------------|
|
|
| 1 | John Doe | john@example.com | 2024-01-15 |
|
|
| 2 | Jane Smith | jane@example.com | 2024-01-20 |
|
|
| 3 | Bob Wilson | bob@example.com | 2024-02-01 |
|
|
| 4 | Alice Brown | alice@example.com | 2024-02-10 |
|
|
| 5 | Charlie Davis | charlie@example.com | 2024-02-15 |`,
|
|
state: "output-available" as const,
|
|
toolCallId: nanoid(),
|
|
type: "tool-database_query" as const,
|
|
};
|
|
|
|
const Example = () => (
|
|
<div className="space-y-4" style={{ minHeight: "1400px" }}>
|
|
{/* 1. input-streaming: Pending */}
|
|
<Tool defaultOpen>
|
|
<ToolHeader
|
|
state="input-streaming"
|
|
title="database_query"
|
|
type="tool-database_query"
|
|
/>
|
|
<ToolContent>
|
|
<ToolInput input={{}} />
|
|
</ToolContent>
|
|
</Tool>
|
|
|
|
{/* 2. approval-requested: Awaiting Approval */}
|
|
<Tool>
|
|
<ToolHeader
|
|
state={"approval-requested" as ToolUIPart["state"]}
|
|
title="database_query"
|
|
type="tool-database_query"
|
|
/>
|
|
<ToolContent>
|
|
<ToolInput input={toolCall.input} />
|
|
<Confirmation approval={{ id: nanoid() }} state="approval-requested">
|
|
<ConfirmationTitle>
|
|
<ConfirmationRequest>
|
|
This tool will execute a query on the production database.
|
|
</ConfirmationRequest>
|
|
<ConfirmationAccepted>
|
|
<CheckIcon className="size-4 text-green-600 dark:text-green-400" />
|
|
<span>Accepted</span>
|
|
</ConfirmationAccepted>
|
|
<ConfirmationRejected>
|
|
<XIcon className="size-4 text-destructive" />
|
|
<span>Rejected</span>
|
|
</ConfirmationRejected>
|
|
</ConfirmationTitle>
|
|
<ConfirmationActions>
|
|
<ConfirmationAction onClick={handleReject} variant="outline">
|
|
Reject
|
|
</ConfirmationAction>
|
|
<ConfirmationAction onClick={handleAccept} variant="default">
|
|
Accept
|
|
</ConfirmationAction>
|
|
</ConfirmationActions>
|
|
</Confirmation>
|
|
</ToolContent>
|
|
</Tool>
|
|
|
|
{/* 3. approval-responded: Responded */}
|
|
<Tool>
|
|
<ToolHeader
|
|
state={"approval-responded" as ToolUIPart["state"]}
|
|
title="database_query"
|
|
type="tool-database_query"
|
|
/>
|
|
<ToolContent>
|
|
<ToolInput input={toolCall.input} />
|
|
<Confirmation
|
|
approval={{ approved: true, id: nanoid() }}
|
|
state="approval-responded"
|
|
>
|
|
<ConfirmationTitle>
|
|
<ConfirmationRequest>
|
|
This tool will execute a query on the production database.
|
|
</ConfirmationRequest>
|
|
<ConfirmationAccepted>
|
|
<CheckIcon className="size-4 text-green-600 dark:text-green-400" />
|
|
<span>Accepted</span>
|
|
</ConfirmationAccepted>
|
|
<ConfirmationRejected>
|
|
<XIcon className="size-4 text-destructive" />
|
|
<span>Rejected</span>
|
|
</ConfirmationRejected>
|
|
</ConfirmationTitle>
|
|
</Confirmation>
|
|
</ToolContent>
|
|
</Tool>
|
|
|
|
{/* 4. input-available: Running */}
|
|
<Tool>
|
|
<ToolHeader
|
|
state="input-available"
|
|
title="database_query"
|
|
type="tool-database_query"
|
|
/>
|
|
<ToolContent>
|
|
<ToolInput input={toolCall.input} />
|
|
</ToolContent>
|
|
</Tool>
|
|
|
|
{/* 5. output-available: Completed */}
|
|
<Tool>
|
|
<ToolHeader state={toolCall.state} type={toolCall.type} />
|
|
<ToolContent>
|
|
<ToolInput input={toolCall.input} />
|
|
<Confirmation
|
|
approval={{ approved: true, id: nanoid() }}
|
|
state="output-available"
|
|
>
|
|
<ConfirmationTitle>
|
|
<ConfirmationRequest>
|
|
This tool will execute a query on the production database.
|
|
</ConfirmationRequest>
|
|
<ConfirmationAccepted>
|
|
<CheckIcon className="size-4 text-green-600 dark:text-green-400" />
|
|
<span>Accepted</span>
|
|
</ConfirmationAccepted>
|
|
<ConfirmationRejected>
|
|
<XIcon className="size-4 text-destructive" />
|
|
<span>Rejected</span>
|
|
</ConfirmationRejected>
|
|
</ConfirmationTitle>
|
|
</Confirmation>
|
|
{toolCall.state === "output-available" && (
|
|
<ToolOutput errorText={toolCall.errorText} output={toolCall.output} />
|
|
)}
|
|
</ToolContent>
|
|
</Tool>
|
|
|
|
{/* 6. output-error: Error */}
|
|
<Tool>
|
|
<ToolHeader
|
|
state="output-error"
|
|
title="database_query"
|
|
type="tool-database_query"
|
|
/>
|
|
<ToolContent>
|
|
<ToolInput input={toolCall.input} />
|
|
<ToolOutput
|
|
errorText="Connection timeout: Unable to reach database server"
|
|
output={undefined}
|
|
/>
|
|
</ToolContent>
|
|
</Tool>
|
|
|
|
{/* 7. output-denied: Denied */}
|
|
<Tool>
|
|
<ToolHeader
|
|
state={"output-denied" as ToolUIPart["state"]}
|
|
title="database_query"
|
|
type="tool-database_query"
|
|
/>
|
|
<ToolContent>
|
|
<ToolInput input={toolCall.input} />
|
|
<Confirmation
|
|
approval={{
|
|
approved: false,
|
|
id: nanoid(),
|
|
reason: "Query could impact production performance",
|
|
}}
|
|
state="output-denied"
|
|
>
|
|
<ConfirmationTitle>
|
|
<ConfirmationRequest>
|
|
This tool will execute a query on the production database.
|
|
</ConfirmationRequest>
|
|
<ConfirmationAccepted>
|
|
<CheckIcon className="size-4 text-green-600 dark:text-green-400" />
|
|
<span>Accepted</span>
|
|
</ConfirmationAccepted>
|
|
<ConfirmationRejected>
|
|
<XIcon className="size-4 text-destructive" />
|
|
<span>Rejected: Query could impact production performance</span>
|
|
</ConfirmationRejected>
|
|
</ConfirmationTitle>
|
|
</Confirmation>
|
|
</ToolContent>
|
|
</Tool>
|
|
</div>
|
|
);
|
|
|
|
export default Example;
|