feat: migrate SMS/voice to Telnyx (TeXML), keep browser dial pad on Twilio
Twilio now handles only the in-browser dial pad (Access Token + browser-Device outbound TwiML); Telnyx takes over SMS, inbound voicemail, the AI voice conversation loop, and staff-triggered outbound calls via TeXML. Adds TelnyxSettings model, Communication.provider discriminator, new comms/routes modules, and a real Telnyx Settings page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1460,7 +1460,7 @@ export default function AppointmentsPage() {
|
||||
if (!user || selectedReminderColumns.size === 0) return;
|
||||
setIsSendingReminders(true);
|
||||
try {
|
||||
const res = await apiRequest("POST", "/api/twilio/send-reminders-batch", {
|
||||
const res = await apiRequest("POST", "/api/telnyx/send-reminders-batch", {
|
||||
date: formattedSelectedDate,
|
||||
staffIds: Array.from(selectedReminderColumns),
|
||||
aiFollowUp: reminderAiFollowUp,
|
||||
@@ -1481,7 +1481,7 @@ export default function AppointmentsPage() {
|
||||
if (!user || selectedRescheduleColumns.size === 0) return;
|
||||
setIsSendingReschedule(true);
|
||||
try {
|
||||
const res = await apiRequest("POST", "/api/twilio/send-reschedule-batch", {
|
||||
const res = await apiRequest("POST", "/api/telnyx/send-reschedule-batch", {
|
||||
date: formattedSelectedDate,
|
||||
staffIds: Array.from(selectedRescheduleColumns),
|
||||
aiFollowUp: rescheduleAiFollowUp,
|
||||
|
||||
@@ -43,9 +43,9 @@ export default function PatientConnectionPage() {
|
||||
const { toast } = useToast();
|
||||
|
||||
useQuery<{ enabled: boolean }>({
|
||||
queryKey: ["/api/twilio/after-hours-handoff"],
|
||||
queryKey: ["/api/telnyx/after-hours-handoff"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/twilio/after-hours-handoff");
|
||||
const res = await apiRequest("GET", "/api/telnyx/after-hours-handoff");
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: (data: { enabled: boolean }) => setAfterHoursEnabled(data.enabled),
|
||||
@@ -53,7 +53,7 @@ export default function PatientConnectionPage() {
|
||||
|
||||
const afterHoursMutation = useMutation({
|
||||
mutationFn: async (enabled: boolean) =>
|
||||
apiRequest("PUT", "/api/twilio/after-hours-handoff", { enabled }),
|
||||
apiRequest("PUT", "/api/telnyx/after-hours-handoff", { enabled }),
|
||||
onSuccess: (_: any, enabled: boolean) => {
|
||||
toast({
|
||||
title: enabled ? "After-hours AI enabled" : "After-hours AI disabled",
|
||||
@@ -81,7 +81,7 @@ export default function PatientConnectionPage() {
|
||||
to: string;
|
||||
patientId: number;
|
||||
}) => {
|
||||
return apiRequest("POST", "/api/twilio/make-call", {
|
||||
return apiRequest("POST", "/api/telnyx/make-call", {
|
||||
to,
|
||||
message:
|
||||
"Hello, this is a call from your dental office. We are calling to connect with you.",
|
||||
@@ -112,7 +112,7 @@ export default function PatientConnectionPage() {
|
||||
to: string;
|
||||
patientId: number;
|
||||
}) => {
|
||||
return apiRequest("POST", "/api/twilio/make-ai-call", { to, patientId, purpose: "recall" });
|
||||
return apiRequest("POST", "/api/telnyx/make-ai-call", { to, patientId, purpose: "recall" });
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
@@ -150,7 +150,7 @@ export default function PatientConnectionPage() {
|
||||
);
|
||||
});
|
||||
|
||||
// Handle calling patient via Twilio
|
||||
// Handle calling patient via Telnyx
|
||||
const handleCall = (patient: Patient) => {
|
||||
if (!patient.phone?.trim()) {
|
||||
toast({
|
||||
@@ -166,7 +166,7 @@ export default function PatientConnectionPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// Handle AI-driven call via Twilio
|
||||
// Handle AI-driven call via Telnyx
|
||||
const handleAiCall = (patient: Patient) => {
|
||||
if (!patient.phone?.trim()) {
|
||||
toast({
|
||||
@@ -201,7 +201,7 @@ export default function PatientConnectionPage() {
|
||||
};
|
||||
|
||||
const { data: recentCommunications = [] } = useQuery<any[]>({
|
||||
queryKey: ["/api/twilio/recent-communications"],
|
||||
queryKey: ["/api/telnyx/recent-communications"],
|
||||
refetchInterval: 10000,
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Staff } from "@repo/db/types";
|
||||
import { NpiProviderTable } from "@/components/settings/npiProviderTable";
|
||||
import { ProgramBridgeTable } from "@/components/settings/program-bridge-table";
|
||||
import { TwilioSettingsCard } from "@/components/settings/twilio-settings-card";
|
||||
import { TelnyxSettingsCard } from "@/components/settings/telnyx-settings-card";
|
||||
import { AiSettingsCard } from "@/components/settings/ai-settings-card";
|
||||
import { OfficeHoursCard } from "@/components/settings/office-hours-card";
|
||||
import { OfficeContactCard } from "@/components/settings/office-contact-card";
|
||||
@@ -30,6 +31,7 @@ type SectionId =
|
||||
| "npi"
|
||||
| "programs"
|
||||
| "twilio"
|
||||
| "telnyx"
|
||||
| "ai"
|
||||
| "aichat"
|
||||
| "officehours"
|
||||
@@ -260,6 +262,9 @@ export default function SettingsPage() {
|
||||
case "twilio":
|
||||
return <TwilioSettingsCard />;
|
||||
|
||||
case "telnyx":
|
||||
return <TelnyxSettingsCard />;
|
||||
|
||||
case "ai":
|
||||
return <AiSettingsCard />;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user