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>
178 lines
4.1 KiB
TypeScript
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;
|