feat(cortex): update chat/nudge flow, deepgram streaming and agenda form
Refines chat panel, nudge messages and artifact rendering, reworks deepgram token/streaming handling, and adds test tooling deps (playwright, cypress) to package.json. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,8 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { ToastAction } from '@/components/ui/toast';
|
||||
import { createEncounter } from '@/app/epd/agenda/actions';
|
||||
import { useCortexStore } from '@/stores/cortex-store';
|
||||
import { formatPatientName as formatPatientNameFromDb } from '@/lib/fhir/patient-mapper';
|
||||
import {
|
||||
APPOINTMENT_TYPES,
|
||||
LOCATION_CLASSES,
|
||||
@@ -47,7 +49,12 @@ interface PatientResult {
|
||||
birthDate?: string;
|
||||
}
|
||||
|
||||
function normalizePatientName(name: string) {
|
||||
return name.toLowerCase().trim().replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
export function AgendaCreateForm({ prefillData, onClose }: AgendaCreateFormProps) {
|
||||
const activePatient = useCortexStore((s) => s.activePatient);
|
||||
// Form State
|
||||
const [patientId, setPatientId] = useState<string>(prefillData?.patient?.id || '');
|
||||
const [patientName, setPatientName] = useState<string>(prefillData?.patient?.name || '');
|
||||
@@ -69,6 +76,7 @@ export function AgendaCreateForm({ prefillData, onClose }: AgendaCreateFormProps
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [showResults, setShowResults] = useState(false);
|
||||
const searchRef = useRef<HTMLDivElement>(null);
|
||||
const autoResolvedPatientRef = useRef<string | null>(null);
|
||||
|
||||
// Initialize search query if patient is prefilled but we want to allow editing
|
||||
useEffect(() => {
|
||||
@@ -77,6 +85,69 @@ export function AgendaCreateForm({ prefillData, onClose }: AgendaCreateFormProps
|
||||
}
|
||||
}, [prefillData]);
|
||||
|
||||
// If Cortex only extracted a name, resolve it once so the appointment form
|
||||
// can submit without forcing the user to retype and select the same client.
|
||||
useEffect(() => {
|
||||
const prefilledPatient = prefillData?.patient;
|
||||
if (!prefilledPatient?.name || prefilledPatient.id || patientId) return;
|
||||
|
||||
const normalizedPrefill = normalizePatientName(prefilledPatient.name);
|
||||
if (autoResolvedPatientRef.current === normalizedPrefill) return;
|
||||
|
||||
autoResolvedPatientRef.current = normalizedPrefill;
|
||||
|
||||
if (activePatient) {
|
||||
const activePatientName = formatPatientNameFromDb(activePatient);
|
||||
if (normalizePatientName(activePatientName) === normalizedPrefill) {
|
||||
setPatientId(activePatient.id);
|
||||
setPatientName(activePatientName);
|
||||
setSearchQuery(activePatientName);
|
||||
setShowResults(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
async function resolvePrefilledPatient() {
|
||||
setIsSearching(true);
|
||||
try {
|
||||
const res = await fetch(`/api/cortex/patients/search?q=${encodeURIComponent(prefilledPatient.name)}`);
|
||||
if (!res.ok || cancelled) return;
|
||||
|
||||
const data = await res.json();
|
||||
const patients = (data.patients || []) as PatientResult[];
|
||||
const exactMatch = patients.find(
|
||||
(patient) => normalizePatientName(patient.name) === normalizedPrefill
|
||||
);
|
||||
const match = exactMatch || (patients.length === 1 ? patients[0] : null);
|
||||
|
||||
if (match) {
|
||||
setPatientId(match.id);
|
||||
setPatientName(match.name);
|
||||
setSearchQuery(match.name);
|
||||
setSearchResults([]);
|
||||
setShowResults(false);
|
||||
} else {
|
||||
setSearchResults(patients);
|
||||
setShowResults(patients.length > 0);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to resolve prefilled patient', err);
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setIsSearching(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolvePrefilledPatient();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [prefillData, patientId, activePatient]);
|
||||
|
||||
// Handle outside click to close search results
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
|
||||
Reference in New Issue
Block a user