feat: chat window, preferred language, insurance contact, and AI call eligibility
- Schedule: right-click Chat option opens floating SMS chat window - Chat window: SMS template selector with appointment date/time pre-filled - Chat window: office name and phone pulled from Settings > Office Contact - Chat window: Preferred Language selector (English, Spanish, Portuguese, Mandarin, Cantonese, Arabic, Haitian Creole) with fully translated templates and locale-aware date/time formatting - Patient form: Preferred Language field (add/edit), default English - Settings > Office Contact: added Dental Office Name field - Settings > Advanced: Insurance Contact page (CRUD — company name + phone) - Prisma schema: preferredLanguage on Patient, officeName on OfficeContact, new InsuranceContact model - Patient management: Upload Patient Document moved below Patient Records - Insurance Eligibility: AI Call Insurance collapsible section; insurance company and phone auto-populated from saved Insurance Contacts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
LoaderCircleIcon,
|
||||
Stethoscope,
|
||||
Download,
|
||||
MessageSquare,
|
||||
} from "lucide-react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
@@ -60,6 +61,7 @@ import {
|
||||
import { SeleniumTaskBanner } from "@/components/ui/selenium-task-banner";
|
||||
import { PatientStatusBadge } from "@/components/appointments/patient-status-badge";
|
||||
import type { OfficeHoursData } from "@/components/settings/office-hours-card";
|
||||
import { MessageThread } from "@/components/patient-connection/message-thread";
|
||||
|
||||
// Define types for scheduling
|
||||
interface TimeSlot {
|
||||
@@ -201,6 +203,10 @@ export default function AppointmentsPage() {
|
||||
const [selectProceduresPatientId, setSelectProceduresPatientId] = useState<number | null>(null);
|
||||
const [selectProceduresAppointmentId, setSelectProceduresAppointmentId] = useState<number | null>(null);
|
||||
|
||||
// Chat popup state
|
||||
const [chatPatient, setChatPatient] = useState<Patient | null>(null);
|
||||
const [chatAppointmentInfo, setChatAppointmentInfo] = useState<{ date: string; startTime: string } | undefined>(undefined);
|
||||
|
||||
// Create context menu hook
|
||||
const { show } = useContextMenu({
|
||||
id: APPOINTMENT_CONTEXT_MENU_ID,
|
||||
@@ -756,6 +762,24 @@ export default function AppointmentsPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// Open chat window for the patient linked to this appointment
|
||||
const handleChat = (appointmentId: number) => {
|
||||
const apt = appointments.find((a) => a.id === appointmentId);
|
||||
if (!apt) return;
|
||||
const patient = patientsFromDay.find((p) => p.id === (apt as any).patientId);
|
||||
if (!patient) return;
|
||||
const processed = processedAppointments.find((a) => a.id === appointmentId);
|
||||
setChatPatient(patient as Patient);
|
||||
if (processed) {
|
||||
setChatAppointmentInfo({
|
||||
date: typeof processed.date === "string" ? processed.date : formatLocalDate(processed.date as Date),
|
||||
startTime: typeof processed.startTime === "string" ? processed.startTime.substring(0, 5) : "",
|
||||
});
|
||||
} else {
|
||||
setChatAppointmentInfo(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to display context menu
|
||||
const handleContextMenu = (e: React.MouseEvent, appointmentId: number) => {
|
||||
// Prevent the default browser context menu
|
||||
@@ -1478,6 +1502,14 @@ export default function AppointmentsPage() {
|
||||
Claim Status
|
||||
</span>
|
||||
</Item>
|
||||
|
||||
{/* Chat */}
|
||||
<Item onClick={({ props }) => handleChat(props.appointmentId)}>
|
||||
<span className="flex items-center gap-2 text-blue-600">
|
||||
<MessageSquare className="h-4 w-4" />
|
||||
Chat
|
||||
</span>
|
||||
</Item>
|
||||
</Menu>
|
||||
|
||||
{/* Main Content */}
|
||||
@@ -1688,6 +1720,19 @@ export default function AppointmentsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Chat popup */}
|
||||
{chatPatient && (
|
||||
<div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-lg shadow-xl w-full max-w-lg h-[600px] flex flex-col overflow-hidden">
|
||||
<MessageThread
|
||||
patient={chatPatient}
|
||||
appointmentInfo={chatAppointmentInfo}
|
||||
onBack={() => { setChatPatient(null); setChatAppointmentInfo(undefined); }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Select Procedures modal — stays on appointments page */}
|
||||
{isSelectProceduresOpen && selectProceduresPatientId !== null && (
|
||||
<ClaimForm
|
||||
|
||||
Reference in New Issue
Block a user