feat: show processing status for one-off campaigns (#14592)

## Summary

One-off SMS and WhatsApp campaigns now show a `Processing` state while
the audience send is in progress. The campaign moves to `Completed`
after processing finishes, and already-processing campaigns are skipped
by the scheduler to avoid duplicate sends.

## Closes

- [CW-6037: feat: Introduce an in-progress status for
campaigns](https://linear.app/chatwoot/issue/CW-6037/feat-introduce-an-in-progress-status-for-campaigns)

## Screenshot

SMS campaign card showing the new `Processing` status.

<img width="3840" height="2160" alt="framed-campaign-processing-status"
src="https://github.com/user-attachments/assets/de7913b5-65fb-4121-9034-24a568eb0382"
/>

## What changed

- Added `processing` as a campaign status.
- Mark one-off campaigns as `processing` under a row lock before the
send service runs.
- Complete SMS, Twilio SMS, and WhatsApp one-off campaigns after
audience processing finishes.
- Keep campaigns in `processing` if an unexpected service error escapes,
so the scheduler does not automatically resend the audience.
- Added the `Processing` label for SMS and WhatsApp campaign cards.

## Known operational behavior

If a worker is interrupted or an unexpected service error escapes after
a campaign is marked `processing`, the campaign can remain in
`processing`. This is intentional for now to avoid automatic
full-audience resends. Installation admins can decide whether to mark
the campaign completed or restart it manually from the Rails console
after checking what was sent.

## How to test

- Create a one-off SMS or WhatsApp campaign scheduled for now.
- Run the scheduled job or trigger the campaign job.
- Confirm the campaign card shows `Processing` while the audience is
being processed. For small audiences, refresh during processing or use a
larger audience so the state is observable.
- Confirm the campaign moves to `Completed` after audience processing
finishes.
- Confirm an already-processing campaign is not enqueued again by the
scheduled job.
This commit is contained in:
Sojan Jose
2026-06-01 16:47:17 +05:30
committed by GitHub
parent ed3059c1aa
commit f27bbef73b
11 changed files with 132 additions and 14 deletions
@@ -49,6 +49,7 @@ const emit = defineEmits(['edit', 'delete']);
const { t } = useI18n();
const STATUS_COMPLETED = 'completed';
const STATUS_PROCESSING = 'processing';
const { formatMessage } = useMessageFormatter();
@@ -68,9 +69,15 @@ const campaignStatus = computed(() => {
: t('CAMPAIGN.LIVE_CHAT.CARD.STATUS.DISABLED');
}
return props.status === STATUS_COMPLETED
? t('CAMPAIGN.SMS.CARD.STATUS.COMPLETED')
: t('CAMPAIGN.SMS.CARD.STATUS.SCHEDULED');
if (props.status === STATUS_COMPLETED) {
return t('CAMPAIGN.SMS.CARD.STATUS.COMPLETED');
}
if (props.status === STATUS_PROCESSING) {
return t('CAMPAIGN.SMS.CARD.STATUS.PROCESSING');
}
return t('CAMPAIGN.SMS.CARD.STATUS.SCHEDULED');
});
const inboxName = computed(() => props.inbox?.name || '');
@@ -88,6 +88,7 @@
},
"CARD": {
"STATUS": {
"PROCESSING": "Processing",
"COMPLETED": "Completed",
"SCHEDULED": "Scheduled"
},
@@ -146,6 +147,7 @@
},
"CARD": {
"STATUS": {
"PROCESSING": "Processing",
"COMPLETED": "Completed",
"SCHEDULED": "Scheduled"
},