refactor(whatsapp-call): migrate to unified Call model from PR #14026

Replace the WhatsappCall model with the unified Call model introduced
in feat/voice-call-model (PR #14026), enabling multi-provider support
(Twilio + WhatsApp) under a single data model.

Key changes:
- Delete WhatsappCall model and its 3 migrations (whatsapp_calls table)
- Enhance Call model with WhatsApp-specific helpers (sdp_offer,
  ice_servers, recording_url, direction_label, ringing?, in_progress?)
- Update all backend services, controller, and jobs to use Call model
- Map statuses: accepted→in_progress, rejected→failed, missed→no_answer,
  ended→completed
- Map directions: inbound→incoming, outbound→outgoing (enums)
- Rename call_id column to provider_call_id, always set provider: :whatsapp
- Add authorization check on initiate action
- Add race condition guard in outbound call_connect webhook handler
- Consolidate direction_label helper into Call model (DRY)
- Update frontend data key from waCallId to callId in VoiceCall bubble
- Remove whatsapp_calls table from schema.rb, fix missing started_at

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-04-12 17:17:30 +00:00
co-authored by Claude Opus 4.6
parent 0a0915ace8
commit f49032610f
15 changed files with 210 additions and 266 deletions
@@ -47,7 +47,7 @@ const isFailed = computed(() =>
// Call source and metadata — all camelCase due to deep transform
const isWhatsappCall = computed(() => data.value?.callSource === 'whatsapp');
const waCallId = computed(() => data.value?.waCallId);
const waCallId = computed(() => data.value?.callId);
const acceptedBy = computed(() => data.value?.acceptedBy);
const durationSeconds = computed(() => data.value?.durationSeconds);
const recordingUrl = computed(() => data.value?.recordingUrl);
@@ -193,7 +193,7 @@ export async function acceptWhatsappCallById(waCallId) {
// 1. Check if the call is already in the incoming store
let call = callsStore.incomingCalls.find(
c => c.id === waCallId || c.waCallId === waCallId
c => c.id === waCallId || c.callId === String(waCallId)
);
// 2. Not in store (page was refreshed) → fetch from API
@@ -205,7 +205,6 @@ export async function acceptWhatsappCallById(waCallId) {
call = {
id: data.id,
callId: data.call_id,
waCallId: data.id,
direction: data.direction,
inboxId: data.inbox_id,
conversationId: data.conversation_id,