feat(dialer): volunteer caller frontend (Telnyx WebRTC UI) - #207
Conversation
Frontend for the volunteer dialer, stacked on dialer-backend: - VolunteerDialer container: WebRTC call controls, timer, status bar, disposition form, contact flow - TexterTodoList: call-shift request entry point (CallRequest) and call-aware assignment summary - AdminCampaignStats: call-campaign stat tweaks - dialer GraphQL operations (hooks) and @telnyx/webrtc dependency Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(dialer): add canned responses + tags to calling * feat(dialer): allow releasing calls (#209) * feat(dialer): allow releasing calls * feat(dialer): add texting history to call screen (#210) * feat(dialer): add texting history to call screen * chore: update seeds to not use logger.info * chore(dialer): fix variable interpolation in script
* feat(dialer): volunteer caller backend (Design-B call tracking)
Backend for the volunteer dialer, stacked on dialer-campaign-type:
- GraphQL schema, resolvers, and Telnyx WebRTC token route
- dialer lib: shift assignment, contact-hours-aware serving, atomic
call_status claim, disposition + attempt tracking
- forward migrations (campaign-type's create migrations left untouched):
- 000009: drop call_campaigns_no_autoassign (call campaigns use autoassign
for shifts)
- 000010: add call_status/attempt_count/last_attempted_at to
dialer_campaign_contact and disposition to dialer_call
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(dialer): address feedback
* feat(dialer): volunteer caller frontend (Telnyx WebRTC UI) (#207)
* chore(tool-versions): update node version (#195)
* feat(dialer): volunteer caller frontend (Telnyx WebRTC UI)
Frontend for the volunteer dialer, stacked on dialer-backend:
- VolunteerDialer container: WebRTC call controls, timer, status bar,
disposition form, contact flow
- TexterTodoList: call-shift request entry point (CallRequest) and
call-aware assignment summary
- AdminCampaignStats: call-campaign stat tweaks
- dialer GraphQL operations (hooks) and @telnyx/webrtc dependency
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(dialer): add canned responses + tags to calling (#208)
* feat(dialer): add canned responses + tags to calling
* feat(dialer): allow releasing calls (#209)
* feat(dialer): allow releasing calls
* feat(dialer): add texting history to call screen (#210)
* feat(dialer): add texting history to call screen
* chore: update seeds to not use logger.info
* chore(dialer): fix variable interpolation in script
---------
Co-authored-by: Aashish John <aashishjohn25@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Aashish John <aashishjohn25@gmail.com>
| hideIfZero: true | ||
| })} | ||
| {isCallCampaign ? ( | ||
| <Button |
There was a problem hiding this comment.
Suggestion: We could probs show a badge with the number of calls left in the assignment as an indicator of their progress?
| // The Telnyx SDK attaches the remote party's audio stream to this element | ||
| // and plays it; without a remoteElement there is no audio output. | ||
| useEffect(() => { | ||
| const audio = document.createElement("audio"); |
There was a problem hiding this comment.
Question: Is it possible to do something like <audio ref={remoteAudioRef} in JSX to give React control over this instead?
| const clientRef = useRef<TelnyxRTC | null>(null); | ||
| const activeCallRef = useRef<Call | null>(null); | ||
| const remoteAudioRef = useRef<HTMLAudioElement | null>(null); | ||
| const wasAnsweredRef = useRef(false); |
There was a problem hiding this comment.
Question: Seems like this isn't read anywhere? Could it be removed?
| setActiveCall(call); | ||
|
|
||
| // Map Telnyx numeric state to our display state | ||
| const stateLabel: string = (call as any).state ?? ""; |
There was a problem hiding this comment.
Suggestion: Looks like this satisfies TS without going to any
const stateLabel: string = (call).state ?? "";
| case "hangup": | ||
| case "destroy": | ||
| case "purge": | ||
| setCallEndCause((call as any).cause ?? null); |
There was a problem hiding this comment.
Suggestion: Looks like this satisfies TS without going to any
setCallEndCause((call).cause ?? null);
| activeCallRef.current = call; | ||
| setActiveCall(call); |
There was a problem hiding this comment.
Question: Should these only happen for requesting/trying/ringing/active ?
| assignableFilter: | ||
| "and do_not_call = false and call_status in ('not_attempted', 'no_answer')", | ||
| // Prioritize never-attempted contacts over no-answer retries. | ||
| assignableOrder: | ||
| "(case when call_status = 'not_attempted' then 10 else 20 end) asc" |
There was a problem hiding this comment.
Question: I think retries should be opted into by admins in some way. Otherwise could we end up calling the same people over and over?
| // ordering baked into the per-stage option defaults). Only call campaigns | ||
| // override the table and assignable rules. | ||
| interface ContactTableConfig { | ||
| table?: string; |
There was a problem hiding this comment.
Required: I would define a type for the two contact tables and only accept those values wherever we're passing in a table like this. Something like
type ContactTable = "campaign_contact" | "dialer_campaign_contact"
* feat(campaigns): add campaign type (SMS/Call) to campaign editor * feat(admin): add campaign type to admin UI * feat(admin): add new dialer tables * chore(admin): address feedback * chore(dialer): address new table feedback * chore(dialer): address feedback * chore(dialer): regenerate schema dump * chore(dialer): address feedback * feat(dialer): volunteer caller backend (#205) * feat(dialer): volunteer caller backend (Design-B call tracking) Backend for the volunteer dialer, stacked on dialer-campaign-type: - GraphQL schema, resolvers, and Telnyx WebRTC token route - dialer lib: shift assignment, contact-hours-aware serving, atomic call_status claim, disposition + attempt tracking - forward migrations (campaign-type's create migrations left untouched): - 000009: drop call_campaigns_no_autoassign (call campaigns use autoassign for shifts) - 000010: add call_status/attempt_count/last_attempted_at to dialer_campaign_contact and disposition to dialer_call Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(dialer): address feedback * feat(dialer): volunteer caller frontend (Telnyx WebRTC UI) (#207) * chore(tool-versions): update node version (#195) * feat(dialer): volunteer caller frontend (Telnyx WebRTC UI) Frontend for the volunteer dialer, stacked on dialer-backend: - VolunteerDialer container: WebRTC call controls, timer, status bar, disposition form, contact flow - TexterTodoList: call-shift request entry point (CallRequest) and call-aware assignment summary - AdminCampaignStats: call-campaign stat tweaks - dialer GraphQL operations (hooks) and @telnyx/webrtc dependency Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(dialer): add canned responses + tags to calling (#208) * feat(dialer): add canned responses + tags to calling * feat(dialer): allow releasing calls (#209) * feat(dialer): allow releasing calls * feat(dialer): add texting history to call screen (#210) * feat(dialer): add texting history to call screen * chore: update seeds to not use logger.info * chore(dialer): fix variable interpolation in script --------- Co-authored-by: Aashish John <aashishjohn25@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Aashish John <aashishjohn25@gmail.com> * chore(dialer): fix down migration for campaign type campaign view --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Aashish John <aashishjohn25@gmail.com>
No description provided.