Files
temetro/.agents/skills/ai-elements/scripts/conversation.tsx
T
Khalid Abdi 929bec8f31 feat: AI-added records save with placeholders + "Added by AI" provenance
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>
2026-06-14 19:27:17 +03:00

178 lines
4.1 KiB
TypeScript

"use client";
import {
Conversation,
ConversationContent,
ConversationDownload,
ConversationEmptyState,
ConversationScrollButton,
} from "@/components/ai-elements/conversation";
import { Message, MessageContent } from "@/components/ai-elements/message";
import { MessageSquareIcon } from "lucide-react";
import { nanoid } from "nanoid";
import { useEffect, useState } from "react";
const messages: {
key: string;
content: string;
role: "user" | "assistant";
}[] = [
{
content: "Hello, how are you?",
key: nanoid(),
role: "user",
},
{
content: "I'm good, thank you! How can I assist you today?",
key: nanoid(),
role: "assistant",
},
{
content: "I'm looking for information about your services.",
key: nanoid(),
role: "user",
},
{
content:
"Sure! We offer a variety of AI solutions. What are you interested in?",
key: nanoid(),
role: "assistant",
},
{
content: "I'm interested in natural language processing tools.",
key: nanoid(),
role: "user",
},
{
content: "Great choice! We have several NLP APIs. Would you like a demo?",
key: nanoid(),
role: "assistant",
},
{
content: "Yes, a demo would be helpful.",
key: nanoid(),
role: "user",
},
{
content: "Alright, I can show you a sentiment analysis example. Ready?",
key: nanoid(),
role: "assistant",
},
{
content: "Yes, please proceed.",
key: nanoid(),
role: "user",
},
{
content: "Here is a sample: 'I love this product!' → Positive sentiment.",
key: nanoid(),
role: "assistant",
},
{
content: "Impressive! Can it handle multiple languages?",
key: nanoid(),
role: "user",
},
{
content: "Absolutely, our models support over 20 languages.",
key: nanoid(),
role: "assistant",
},
{
content: "How do I get started with the API?",
key: nanoid(),
role: "user",
},
{
content: "You can sign up on our website and get an API key instantly.",
key: nanoid(),
role: "assistant",
},
{
content: "Is there a free trial available?",
key: nanoid(),
role: "user",
},
{
content: "Yes, we offer a 14-day free trial with full access.",
key: nanoid(),
role: "assistant",
},
{
content: "What kind of support do you provide?",
key: nanoid(),
role: "user",
},
{
content: "We provide 24/7 chat and email support for all users.",
key: nanoid(),
role: "assistant",
},
{
content: "Thank you for the information!",
key: nanoid(),
role: "user",
},
{
content: "You're welcome! Let me know if you have any more questions.",
key: nanoid(),
role: "assistant",
},
];
const Example = () => {
const [visibleMessages, setVisibleMessages] = useState<
{
key: string;
content: string;
role: "user" | "assistant";
}[]
>([]);
useEffect(() => {
let currentIndex = 0;
const interval = setInterval(() => {
if (currentIndex < messages.length && messages[currentIndex]) {
const currentMessage = messages[currentIndex];
setVisibleMessages((prev) => [
...prev,
{
content: currentMessage.content,
key: currentMessage.key,
role: currentMessage.role,
},
]);
currentIndex += 1;
} else {
clearInterval(interval);
}
}, 500);
return () => clearInterval(interval);
}, []);
return (
<Conversation className="relative size-full">
<ConversationContent>
{visibleMessages.length === 0 ? (
<ConversationEmptyState
description="Messages will appear here as the conversation progresses."
icon={<MessageSquareIcon className="size-6" />}
title="Start a conversation"
/>
) : (
visibleMessages.map(({ key, content, role }) => (
<Message from={role} key={key}>
<MessageContent>{content}</MessageContent>
</Message>
))
)}
</ConversationContent>
<ConversationDownload messages={visibleMessages} />
<ConversationScrollButton />
</Conversation>
);
};
export default Example;