334334 :label =" t('send_campaign')"
335335 :loading =" isSubmitting"
336336 :disabled =" isActionDisabled"
337- @click =" submit"
337+ @click =" submit(false) "
338338 />
339339 </div >
340340 </div >
341341 </template >
342342 </Dialog >
343+
344+ <ComplianceDialog ref =" complianceDialogRef" @confirm-partial =" submit(true)" />
345+
346+ <component
347+ :is =" CreditsDialog"
348+ ref =" CreditsDialogCampaignRef"
349+ engagement-type =" contact"
350+ action-type =" campaign"
351+ @secondary-action =" submit(true)"
352+ />
343353</template >
344354
345355<script setup lang="ts">
346356import type { Contact } from ' @/types/contact' ;
347357import { extractUnavailableSenderEmails } from ' @/utils/senderOptions' ;
348358import { updateMiningSourcesValidityFromUnavailable } from ' @/utils/sources' ;
349359import Editor from ' primevue/editor' ;
360+ import ComplianceDialog from ' ./ComplianceDialog.vue' ;
361+ import {
362+ CreditsDialog ,
363+ CreditsDialogCampaignRef ,
364+ openCreditsDialog ,
365+ } from ' @/utils/credits' ;
350366
351367const isVisible = defineModel <boolean >(' visible' , { required: true });
352368
@@ -402,6 +418,9 @@ type SenderOptionItem = {
402418const senderOptions = ref <SenderOptionItem []>([]);
403419const fallbackSenderEmail = ref (' ' );
404420const runtimeConfig = useRuntimeConfig ();
421+ const complianceDialogRef = ref <InstanceType <typeof ComplianceDialog > | null >(
422+ null ,
423+ );
405424
406425const DEFAULT_PROJECT_URL = ' https://example.com/project' ;
407426const DEFAULT_PROJECT_IMAGE_SRC =
@@ -623,7 +642,6 @@ const editorModules = computed(() => ({
623642}));
624643
625644type AttributeTarget = ' subject' | ' body' ;
626-
627645const activeAttributeTarget = ref <AttributeTarget >(' body' );
628646
629647const attributeMenuItems = computed (() =>
@@ -1046,7 +1064,7 @@ async function sendPreview() {
10461064 }
10471065}
10481066
1049- async function submit() {
1067+ async function submit(partialCampaign = false ) {
10501068 if (! ensureValidForm ()) {
10511069 return ;
10521070 }
@@ -1056,6 +1074,9 @@ async function submit() {
10561074 }
10571075
10581076 isSubmitting .value = true ;
1077+ let shouldCloseDialog = true ;
1078+ let showErrorToast = true ;
1079+
10591080 try {
10601081 const data = await $saasEdgeFunctions (' email-campaigns/campaigns/create' , {
10611082 method: ' POST' ,
@@ -1073,42 +1094,90 @@ async function submit() {
10731094 trackClick: form .trackClick ,
10741095 plainTextOnly: form .plainTextOnly ,
10751096 onlyValidContacts: form .onlyValidContacts ,
1097+ partialCampaign ,
1098+ },
1099+ onResponse : ({ response }) => {
1100+ if (response .status === 402 ) {
1101+ openCreditsDialog (
1102+ CreditsDialogCampaignRef ,
1103+ true ,
1104+ response ._data .total ,
1105+ response ._data .available ,
1106+ response ._data .availableAlready ,
1107+ );
1108+ shouldCloseDialog = false ;
1109+ showErrorToast = false ;
1110+ return ;
1111+ }
1112+
1113+ if (response .status === 266 && response ._data ?.reason === ' credits' ) {
1114+ openCreditsDialog (
1115+ CreditsDialogCampaignRef ,
1116+ false ,
1117+ response ._data .total ,
1118+ response ._data .available ,
1119+ response ._data .availableAlready ,
1120+ );
1121+ shouldCloseDialog = false ;
1122+ showErrorToast = false ;
1123+ return ;
1124+ }
1125+
1126+ if (response .status === 266 && response ._data ?.reason === ' consent' ) {
1127+ complianceDialogRef .value ?.openModal (
1128+ response ._data .total ,
1129+ response ._data .available ,
1130+ );
1131+ shouldCloseDialog = false ;
1132+ showErrorToast = false ;
1133+ return ;
1134+ }
1135+
1136+ if (response .status === 200 ) {
1137+ return ;
1138+ }
1139+
1140+ throw new Error (response ._data ?.error || ' Campaign creation failed' );
10761141 },
10771142 });
10781143
1079- $toast .add ({
1080- group: ' has-links' ,
1081- severity: ' success' ,
1082- summary: t (' campaign_started' ),
1083- detail: {
1084- message: t (' campaign_started_detail' ),
1085- button: {
1086- text: t (' see_campaigns' ),
1087- action : () => navigateTo (' /campaigns' ),
1144+ if (shouldCloseDialog ) {
1145+ $toast .add ({
1146+ group: ' has-links' ,
1147+ severity: ' success' ,
1148+ summary: t (' campaign_started' ),
1149+ detail: {
1150+ message: t (' campaign_started_detail' ),
1151+ button: {
1152+ text: t (' see_campaigns' ),
1153+ action : () => navigateTo (' /campaigns' ),
1154+ },
10881155 },
1089- },
1090- life: 6000 ,
1091- });
1156+ life: 6000 ,
1157+ });
10921158
1093- if (data ?.campaignId ) {
1094- startCampaignCompletionWatcher (data .campaignId );
1095- }
1159+ if (data ?.campaignId ) {
1160+ startCampaignCompletionWatcher (data .campaignId );
1161+ }
10961162
1097- isVisible .value = false ;
1098- } catch (error : unknown ) {
1099- const parsedError = error as EdgeResponseError ;
1100- const code = parsedError ?.data ?.code ;
1101- if (code === ' SENDER_SMTP_FAILED' && fallbackSenderEmail .value ) {
1102- form .senderEmail =
1103- parsedError ?.data ?.fallbackSenderEmail || fallbackSenderEmail .value ;
1163+ isVisible .value = false ;
11041164 }
1165+ } catch (error : unknown ) {
1166+ if (showErrorToast ) {
1167+ const parsedError = error as EdgeResponseError ;
1168+ const code = parsedError ?.data ?.code ;
1169+ if (code === ' SENDER_SMTP_FAILED' && fallbackSenderEmail .value ) {
1170+ form .senderEmail =
1171+ parsedError ?.data ?.fallbackSenderEmail || fallbackSenderEmail .value ;
1172+ }
11051173
1106- $toast .add ({
1107- severity: ' error' ,
1108- summary: t (' campaign_start_failed' ),
1109- detail: resolveErrorMessage (error , ' error_campaign_start_failed' ),
1110- life: 5000 ,
1111- });
1174+ $toast .add ({
1175+ severity: ' error' ,
1176+ summary: t (' campaign_start_failed' ),
1177+ detail: resolveErrorMessage (error , ' error_campaign_start_failed' ),
1178+ life: 5000 ,
1179+ });
1180+ }
11121181 } finally {
11131182 isSubmitting .value = false ;
11141183 }
0 commit comments