1(function() { 2 try { 3 var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {}; 4 e.SENTRY_RELEASE = { id: "b96dc26" }; 5 var n = new e.Error().stack; 6 n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "93395b00-18b8-4d39-8532-a1b5ee0f24bb", e._sentryDebugIdIdentifier = "sentry-dbid-93395b00-18b8-4d39-8532-a1b5ee0f24bb"); 7 } catch (e) {} 8})(); 9import { n as __esmMin } from "./rolldown-runtime-sLwMD6dS.js"; 10import { $s as visit, As as Input, Bt as CapacityBarSegment, Js as init_turbo, Lo as smtp_imap_reconnect_index_path, M as init_currency_helper, Mi as init_routes, Os as Checkbox, Rt as CapacityBar, Vt as init_capacity_bar, Xt as init_label, Yt as Label, a as Datepicker, hs as warmup_unlock_email_account_path, i as init_main, j as CurrencyHelper, js as init_input, ks as init_checkbox, mo as sequence_path, si as email_accounts_warmup_activation_path, yo as sequence_settings_path } from "./controllers-jdPQjaCB.js"; 11import { l as unsafeCast, r as isBoolean, t as dictLookup } from "./type-guards-DVyG5ljW.js"; 12import { i as require_compiler_runtime, s as escapeHtml$1, t as require_jsx_runtime, u as init_utils } from "./jsx-runtime-CHSMGLHp.js"; 13import { t as require_react } from "./react-CrLPhuc5.js"; 14import { n as init_icon, t as Icon } from "./icon-BNzYbsdJ.js"; 15import { r as init_button, t as Button } from "./button-DOiPZHfy.js"; 16import { a as init_tooltip, i as TooltipTrigger, n as TooltipContent, r as TooltipProvider, t as Tooltip } from "./tooltip-HHfiWInP.js"; 17import { i as init_tag, n as Tag, r as TagPrimitive } from "./tag-j7SWTISf.js"; 18import { Ct as SequenceEmailAccountSidepanel, D as init_inertia_bridge, L as visitCohortAware, St as init_sequence_protect_email_account_modal, wt as init_sequence_email_account_sidepanel, xt as SequenceProtectEmailAccountModal } from "./turbo-mount-DM0tFk1M.js"; 19import { i as init_popover, n as PopoverContent, r as PopoverTrigger, t as Popover } from "./popover-BGt3etf0.js"; 20import { n as init_hint, t as Hint } from "./hint-D06PdzJb.js"; 21import { r as init_separator, t as Separator } from "./separator-D5aQTBY-.js"; 22import { n as useActionCable, t as init_useActionCable } from "./useActionCable-BKKuFkGL.js"; 23import { _ as UnlockSending, b as init_ramp_up_configuration, i as init_google_icon, n as microsoft_icon_default, r as google_icon_default, t as init_microsoft_icon, v as init_unlock_sending, y as RampUpConfiguration } from "./microsoft_icon-9Xj1njOk.js"; 24import { a as init_dist$1, l as router, s as usePage, t as Head_default } from "./dist-ZlUHm0R7.js"; 25import { r as init_AppLayout, t as AppLayout } from "./AppLayout-KSwds5qH.js"; 26import { n as init_switch, t as Switch } from "./switch-BaZYiTgE.js"; 27import { a as init_pending_writes, i as isPlainNavigationClick, n as init_CampaignHeader, o as registerPendingWrites, t as CampaignHeader } from "./CampaignHeader-Dz-m64BV.js"; 28import { n as init_SettingsAlert, t as SettingsAlert } from "./SettingsAlert-Dfph3VQx.js"; 29//#region app/frontend/pages/SequencesCampaignSettings/settingsWriteQueue.ts 30/** 31* Queue a settings `PUT` behind every write already pending for this page. 32* 33* @param key optional coalescing key. A queued-but-not-yet-started write with 34* the same key is replaced by this one (its callers share the returned 35* promise); only pass it when the newest payload fully supersedes the older 36* one and dropping the older write's callbacks is harmless. 37* @returns a promise that settles when the visit finishes â success, validation 38* error, cancellation or network failure alike. It never rejects, so a failed 39* write cannot wedge the queue and callers need no `catch`. 40*/ 41function enqueueSettingsPut(url, data, options = {}, key) { 42 if (key) { 43 const superseded = waiting.get(key); 44 if (superseded?.promise) { 45 superseded.url = url; 46 superseded.data = data; 47 superseded.options = options; 48 return superseded.promise; 49 } 50 } 51 const job = { 52 url, 53 data, 54 options, 55 promise: null,
56 discarded: false 57 }; 58 if (key) waiting.set(key, job); 59 pending.add(job); 60 const promise = tail.then(() => { 61 if (key) waiting.delete(key); 62 pending.delete(job); 63 if (job.discarded) return; 64 return runVisit(job); 65 }); 66 job.promise = promise; 67 tail = promise.catch(() => {}); 68 return promise; 69} 70/** 71* Whether this page still owes the server anything at all â keyed or not, 72* queued or already in flight. 73* 74* `hasPendingSettingsWrite` answers for one coalescing key, which is the wrong 75* question for a caller that only wants to know whether it is safe to leave. 76*/ 77function hasOutstandingSettingsWrites() { 78 return pending.size > 0 || activeCancelToken !== null; 79} 80/** 81* Flush every debounced writer into the queue, then resolve once the queue has 82* drained. 83* 84* Dispatching is synchronous, so a listener's `enqueueSettingsPut` has already 85* extended `tail` by the time it is read here â which is what makes awaiting 86* the queue cover the debounce window too. Never rejects: `tail` is kept 87* rejection-free by `enqueueSettingsPut`, and a write that fails still settles. 88*/ 89function flushSettingsWrites() { 90 document.dispatchEvent(new CustomEvent(SETTINGS_WRITES_FLUSH_EVENT)); 91 return tail.then(() => {}); 92} 93/** 94* Give up every write this page owns, queued or in flight, because the user is 95* leaving. 96* 97* Two separate ways a settings write can drag the user back here: 98* 99* - **Queued.** A waiting write is released by the *previous* visit's 100* `onFinish`, which also fires when that visit is cancelled â so leaving the 101* page while one write is in flight and another is waiting would start the 102* waiting `router.put` right on top of the user's navigation, cancel it, and 103* follow the settings redirect back here. 104* - **Running.** Inertia cancels an in-flight visit when *it* starts the next 105* one, but the header's Close, breadcrumb and History links are Turbo's 106* (`data-turbo="true"`), and Turbo knows nothing about the Inertia request. 107* The settings `PUT` keeps going, and if its redirect resolves after the 108* destination has rendered, Inertia swaps the settings page back in â the 109* user watches the page they just left reappear. So the running visit is 110* cancelled too. 111* 112* Cancelling only aborts the *client* side: the request is already on the wire 113* by the time `onCancelToken` has fired, so the server still receives it and 114* still saves. What we throw away is the response â which is exactly the part 115* that navigates. The alternative, letting the response arrive and suppressing 116* only the page swap, is not available: Inertia swaps before `onSuccess` runs 117* and offers no "apply nothing" hook, so cancellation is the only lever. The 118* cost is the `onSuccess` reconcile the sections do, which is worthless here 119* anyway â the page it would reconcile is gone. 120* 121* Discarded jobs still settle their promise (without a visit), so nothing that 122* awaited one is left hanging. 123*/ 124function abandonSettingsWrites() { 125 for (const job of pending) job.discarded = true; 126 pending.clear(); 127 waiting.clear(); 128 const token = activeCancelToken; 129 activeCancelToken = null; 130 try { 131 token?.cancel(); 132 } catch {} 133} 134/** 135* Whether the Inertia visit starting right now was started by this queue. 136* 137* `router.put` fires the router's `before` event synchronously, so a `before` 138* listener can read this to tell one of the page's own saves apart from a visit 139* that is taking the user somewhere else. 140*/ 141function isDispatchingSettingsWrite() { 142 return dispatching; 143} 144function runVisit(job) { 145 return new Promise((resolve) => { 146 let settled = false; 147 let token = null; 148 const release = () => { 149 if (activeCancelToken === token) activeCancelToken = null; 150 if (settled) return; 151 settled = true; 152 resolve(); 153 }; 154 try { 155 dispatching = true; 156 router.put(job.url, job.data, { 157 ...job.options, 158 onCancelToken: (cancelToken) => { 159 token = cancelToken; 160 activeCancelToken = cancelToken; 161 job.options.onCancelToken?.(cancelToken); 162 }, 163 onFinish: (visit) => { 164 release(); 165 job.options.onFinish?.(visit); 166 } 167 }); 168 } catch { 169 release(); 170 } finally { 171 dispatching = false;
172 } 173 }); 174} 175var tail, waiting, pending, dispatching, activeCancelToken, SETTINGS_WRITES_FLUSH_EVENT; 176function init_settingsWriteQueue() { 177 return (init_settingsWriteQueue = __esmMin((() => { 178 init_dist$1(); 179 init_pending_writes(); 180 tail = Promise.resolve(); 181 waiting = /* @__PURE__ */ new Map(); 182 pending = /* @__PURE__ */ new Set(); 183 dispatching = false; 184 activeCancelToken = null; 185 registerPendingWrites({ 186 has: hasOutstandingSettingsWrites, 187 flush: () => flushSettingsWrites(), 188 isDispatching: isDispatchingSettingsWrite 189 }); 190 SETTINGS_WRITES_FLUSH_EVENT = "campaign:settings-flush-writes"; 191 })))(); 192} 193//#endregion 194//#region app/frontend/pages/SequencesCampaignSettings/accountStatus.ts 195function reconnectAction(info, accountId, label, connectPaths) { 196 if (info.provider === "smtp_imap") return { 197 kind: "reconnect", 198 label, 199 url: smtp_imap_reconnect_index_path(accountId), 200 turboMethod: "post" 201 }; 202 if (info.provider !== "gmail" && info.provider !== "outlook") return { kind: "none" }; 203 return { 204 kind: "reconnect", 205 label, 206 url: connectPaths[info.provider], 207 turboMethod: null 208 }; 209} 210function accountStatus(info, { accountId, freePlan, canEditAccount, connectPaths }) { 211 if (info.status === "inactive") return { 212 tag: { 213 variant: "warning", 214 icon: "exclamation-triangle", 215 label: t$6("inactive"), 216 tooltip: t$6("inactive_tooltip") 217 }, 218 blocksSelection: true, 219 action: { kind: "none" } 220 }; 221 if (info.connected === false) return { 222 tag: { 223 variant: "danger", 224 icon: "exclamation-circle", 225 label: t$6("disconnected"), 226 tooltip: t$6("disconnected_tooltip") 227 }, 228 blocksSelection: true, 229 action: canEditAccount ? reconnectAction(info, accountId, t$6("reconnect"), connectPaths) : { kind: "none" } 230 }; 231 if (info.authorized === false) return { 232 tag: { 233 variant: "warning", 234 icon: "exclamation-triangle", 235 label: t$6("permissions_required"), 236 tooltip: t$6("permissions_required_tooltip") 237 }, 238 blocksSelection: true, 239 action: canEditAccount ? reconnectAction(info, accountId, t$6("grant_permissions"), connectPaths) : { kind: "none" } 240 }; 241 if (info.inbox_protection_pending_restore && info.provider === "smtp_imap" && canEditAccount) return { 242 tag: { 243 variant: "warning", 244 icon: "exclamation-triangle", 245 label: t$6("activation_required"), 246 tooltip: t$6("activation_required_tooltip") 247 }, 248 blocksSelection: false, 249 action: { kind: "activateInboxProtection" } 250 }; 251 if (info.protection === "warming_up" || info.protection === "paused_sending_limit" || info.protection === "paused_provider_restriction") { 252 const locked = !info.sending_unlocked; 253 const canUnlock = locked && info.protection === "warming_up"; 254 return { 255 tag: { 256 variant: "info", 257 icon: locked ? "lock" : null, 258 label: t$6("warming_up"), 259 tooltip: locked ? t$6("warming_up_sending_locked") : t$6("warming_up_tooltip") 260 }, 261 blocksSelection: locked, 262 action: canUnlock ? { 263 kind: "unlockSending", 264 dailyMessagesLimit: info.daily_messages_limit 265 } : { kind: "none" } 266 }; 267 } 268 if (info.over_daily_rate_limit) return { 269 tag: { 270 variant: null, 271 icon: null, 272 label: t$6("daily_limit_reached"), 273 tooltip: t$6("daily_limit_reached_tooltip") 274 }, 275 blocksSelection: false, 276 action: { kind: "none" } 277 }; 278 if (info.over_provider_rate_limit) return { 279 tag: { 280 variant: null, 281 icon: null, 282 label: t$6("provider_limit_reached"), 283 tooltip: t$6("provider_limit_reached_tooltip") 284 }, 285 blocksSelection: false, 286 action: { kind: "none" } 287 }; 288 if (info.status === "paused") return { 289 tag: { 290 variant: null, 291 icon: null, 292 label: t$6("paused"), 293 tooltip: info.paused_reason ? t$6("paused_tooltip", { error: info.paused_reason }) : t$6("paused_default_tooltip") 294 }, 295 blocksSelection: false, 296 action: { kind: "none" } 297 }; 298 if (info.ramp_up?.status === "in_progress") return { 299 tag: { 300 variant: null, 301 icon: null, 302 label: t$6("progressive_sending"), 303 tooltip: t$6("progressive_sending_tooltip") 304 }, 305 blocksSelection: false, 306 action: { kind: "none" } 307 }; 308 if (info.needs_protection) return { 309 tag: { 310 variant: "warning", 311 icon: "exclamation-triangle", 312 label: t$6("needs_protection"), 313 tooltip: t$6("needs_protection_tooltip") 314 }, 315 blocksSelection: false, 316 action: !canEditAccount ? { kind: "none" } : freePlan ? {
317 kind: "rampUpProtect", 318 accountId, 319 providerLimit: info.maximum_daily_messages_limit ?? void 0 320 } : { 321 kind: "protectInbox", 322 provider: info.provider ?? "", 323 email: info.email ?? "", 324 providerLimit: info.maximum_daily_messages_limit ?? null, 325 paidUntil: info.inbox_protection_paid_until ?? null 326 } 327 }; 328 return { 329 tag: { 330 variant: "success", 331 icon: null, 332 label: t$6("ready_to_send"), 333 tooltip: t$6("ready_to_send_tooltip") 334 }, 335 blocksSelection: false, 336 action: { kind: "none" } 337 }; 338} 339function capacitySegments(sent, total, scheduled) { 340 const safeTotal = Math.max(0, total); 341 const rawSent = safeTotal > 0 ? sent / safeTotal * 100 : 0; 342 const rawScheduled = safeTotal > 0 ? scheduled / safeTotal * 100 : 0; 343 const sentPercent = Math.min(100, rawSent); 344 const scheduledPercent = Math.max(0, Math.min(100 - sentPercent, rawScheduled)); 345 const availablePercent = Math.max(0, 100 - sentPercent - scheduledPercent); 346 const isFull = sent >= safeTotal && scheduled === 0 && safeTotal > 0; 347 return { 348 safeTotal, 349 isFull, 350 sentPercent: isFull ? 100 : sentPercent, 351 scheduledPercent: isFull ? 0 : scheduledPercent, 352 availablePercent, 353 available: Math.max(0, safeTotal - sent - scheduled), 354 primaryOnly: isFull || scheduled === 0 && sent === 0 355 }; 356} 357var t$6; 358function init_accountStatus() { 359 return (init_accountStatus = __esmMin((() => { 360 init_routes(); 361 t$6 = (key, options) => i18n.t(`js.back.campaigns.settings.${key}`, options); 362 })))(); 363} 364//#endregion 365//#region app/frontend/pages/SequencesCampaignSettings/SmtpImapReconnectButton.tsx 366function openCredentialEditor(url, from) { 367 const visit$1 = () => visit(url, { frame: EDIT_FRAME }); 368 const opener = from?.closest(".modal"); 369 const instance = opener ? window.bootstrap.Modal.getInstance(opener) : null; 370 if (!opener || !instance) { 371 visit$1(); 372 return; 373 } 374 opener.addEventListener("hidden.bs.modal", visit$1, { once: true }); 375 instance.hide(); 376} 377function SmtpImapReconnectButton({ url, editUrl, label, size = "xs", onReconnected }) { 378 const [submitting, setSubmitting] = (0, import_react$9.useState)(false); 379 const buttonRef = (0, import_react$9.useRef)(null); 380 const mounted = (0, import_react$9.useRef)(true); 381 (0, import_react$9.useEffect)(() => { 382 mounted.current = true; 383 return () => { 384 mounted.current = false; 385 }; 386 }, []); 387 async function reconnect() { 388 setSubmitting(true); 389 try { 390 const response = await fetch(url, { 391 method: "POST", 392 credentials: "same-origin", 393 headers: { 394 Accept: "application/json", 395 "Content-Type": "application/json", 396 "X-CSRF-Token": csrfToken$1() 397 } 398 }); 399 const payload = unsafeCast(await response.json().catch(() => ({}))); 400 if (!mounted.current) return; 401 if (response.ok) { 402 App.displayFlash("success", payload.message ?? i18n.t("js.saved")); 403 onReconnected(); 404 } else if (editUrl && response.status === 422) openCredentialEditor(editUrl, buttonRef.current); 405 else App.displayFlash("error", payload.error ?? i18n.t("js.unknown_error")); 406 } catch { 407 if (mounted.current) App.displayFlash("error", i18n.t("js.unknown_error")); 408 } finally { 409 setSubmitting(false); 410 } 411 } 412 return /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Button, { 413 ref: buttonRef, 414 variant: "default", 415 size, 416 disabled: submitting, 417 onClick: () => void reconnect(), 418 children: submitting ? i18n.t("js.reconnecting") : label 419 }); 420} 421var import_react$9, import_jsx_runtime$9, EDIT_FRAME, csrfToken$1; 422function init_SmtpImapReconnectButton() { 423 return (init_SmtpImapReconnectButton = __esmMin((() => { 424 import_react$9 = require_react(); 425 init_turbo(); 426 init_button(); 427 import_jsx_runtime$9 = require_jsx_runtime(); 428 EDIT_FRAME = "edit_smtp_imap_connection"; 429 csrfToken$1 = () => document.querySelector("meta[name=\"csrf-token\"]")?.content ?? ""; 430 })))(); 431} 432//#endregion 433//#region app/frontend/pages/SequencesCampaignSettings/EmailAccountRow.tsx 434function StatusTag(t0) { 435 const $ = (0, import_compiler_runtime$7.c)(12); 436 const { tag } = t0; 437 const t1 = tag.variant ?? "default"; 438 const t2 = tag.icon ?? void 0; 439 let t3; 440 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 441 t3 = { cursor: "help" }; 442 $[0] = t3; 443 } else t3 = $[0]; 444 let t4; 445 if ($[1] !== t1 || $[2] !== t2 || $[3] !== tag.label) { 446 t4 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Tag, { 447 variant: t1, 448 icon: t2, 449 label: tag.label, 450 style: t3 451 }); 452 $[1] = t1; 453 $[2] = t2; 454 $[3] = tag.label; 455 $[4] = t4; 456 } else t4 = $[4]; 457 const content = t4; 458 if (!tag.tooltip) return content; 459 let t5; 460 if ($[5] !== content) { 461 t5 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TooltipTrigger, { 462 asChild: true, 463 children: content 464 }); 465 $[5] = content; 466 $[6] = t5; 467 } else t5 = $[6]; 468 let t6; 469 if ($[7] !== tag.tooltip) { 470 t6 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TooltipContent, { 471 side: "top", 472 children: tag.tooltip 473 }); 474 $[7] = tag.tooltip; 475 $[8] = t6; 476 } else t6 = $[8]; 477 let t7; 478 if ($[9] !== t5 || $[10] !== t6) { 479 t7 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TooltipProvider, { 480 delayDuration: 100, 481 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)(Tooltip, { children: [t5, t6] }) 482 }); 483 $[9] = t5; 484 $[10] = t6; 485 $[11] = t7; 486 } else t7 = $[11]; 487 return t7; 488} 489function isSendingLocked(info) { 490 return info.sending_unlocked === false && (info.protection === "warming_up" || info.protection === "paused_sending_limit" || info.protection === "paused_provider_restriction"); 491} 492function SendingLocked() { 493 const $ = (0, import_compiler_runtime$7.c)(1); 494 let t0; 495 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 496 t0 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Hint, { children: t$5("sending_locked") }); 497 $[0] = t0; 498 } else t0 = $[0]; 499 return t0; 500} 501function Capacity(t0) { 502 const $ = (0, import_compiler_runtime$7.c)(42); 503 const { info } = t0; 504 const sent = info.sent_in_current_daily_limit ?? 0; 505 const scheduled = info.pending_messages ?? 0; 506 const t1 = info.limit ?? 0; 507 let t2; 508 if ($[0] !== scheduled || $[1] !== sent || $[2] !== t1) { 509 t2 = capacitySegments(sent, t1, scheduled); 510 $[0] = scheduled; 511 $[1] = sent; 512 $[2] = t1; 513 $[3] = t2; 514 } else t2 = $[3]; 515 const segments = t2; 516 let t3; 517 if ($[4] === Symbol.for("react.memo_cache_sentinel")) { 518 t3 = i18n.t("js.email_accounts_center.capacity.legend.sent"); 519 $[4] = t3; 520 } else t3 = $[4]; 521 let t4; 522 if ($[5] !== sent) { 523 t4 = { 524 variant: "sent", 525 label: t3, 526 value: sent 527 }; 528 $[5] = sent; 529 $[6] = t4; 530 } else t4 = $[6]; 531 let t5; 532 if ($[7] === Symbol.for("react.memo_cache_sentinel")) { 533 t5 = i18n.t("js.email_accounts_center.capacity.legend.scheduled"); 534 $[7] = t5; 535 } else t5 = $[7]; 536 let t6; 537 if ($[8] !== scheduled) { 538 t6 = { 539 variant: "scheduled", 540 label: t5, 541 value: scheduled 542 }; 543 $[8] = scheduled; 544 $[9] = t6; 545 } else t6 = $[9]; 546 let t7; 547 if ($[10] === Symbol.for("react.memo_cache_sentinel")) { 548 t7 = i18n.t("js.email_accounts_center.capacity.legend.available"); 549 $[10] = t7; 550 } else t7 = $[10]; 551 let t8; 552 if ($[11] !== segments.available) { 553 t8 = { 554 variant: "available", 555 label: t7, 556 value: segments.available 557 }; 558 $[11] = segments.available; 559 $[12] = t8; 560 } else t8 = $[12]; 561 let t9; 562 if ($[13] !== t4 || $[14] !== t6 || $[15] !== t8) { 563 t9 = [ 564 t4, 565 t6, 566 t8 567 ]; 568 $[13] = t4; 569 $[14] = t6; 570 $[15] = t8; 571 $[16] = t9; 572 } else t9 = $[16]; 573 const tooltipItems = t9; 574 let t10; 575 if ($[17] !== sent) { 576 t10 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("strong", { 577 className: "fw-medium", 578 children: sent 579 }); 580 $[17] = sent; 581 $[18] = t10; 582 } else t10 = $[18]; 583 let t11; 584 if ($[19] !== segments.safeTotal) { 585 t11 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("strong", { 586 className: "fw-medium", 587 children: segments.safeTotal 588 }); 589 $[19] = segments.safeTotal; 590 $[20] = t11; 591 } else t11 = $[20]; 592 let t12; 593 if ($[21] !== t10 || $[22] !== t11) { 594 t12 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("span", { 595 className: "text-xs", 596 children: [ 597 t10, 598 "/", 599 t11 600 ] 601 }); 602 $[21] = t10; 603 $[22] = t11; 604 $[23] = t12; 605 } else t12 = $[23]; 606 const t13 = segments.primaryOnly || void 0; 607 let t14; 608 if ($[24] !== segments.sentPercent || $[25] !== sent) { 609 t14 = sent >
609 0 && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(CapacityBarSegment, { 610 variant: "sent", 611 percent: segments.sentPercent 612 }); 613 $[24] = segments.sentPercent; 614 $[25] = sent; 615 $[26] = t14; 616 } else t14 = $[26]; 617 let t15; 618 if ($[27] !== scheduled || $[28] !== segments.isFull || $[29] !== segments.scheduledPercent) { 619 t15 = scheduled > 0 && !segments.isFull && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(CapacityBarSegment, { 620 variant: "scheduled", 621 percent: segments.scheduledPercent 622 }); 623 $[27] = scheduled; 624 $[28] = segments.isFull; 625 $[29] = segments.scheduledPercent; 626 $[30] = t15; 627 } else t15 = $[30]; 628 let t16; 629 if ($[31] !== segments.availablePercent) { 630 t16 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(CapacityBarSegment, { 631 variant: "available", 632 percent: segments.availablePercent 633 }); 634 $[31] = segments.availablePercent; 635 $[32] = t16; 636 } else t16 = $[32]; 637 let t17; 638 if ($[33] !== t13 || $[34] !== t14 || $[35] !== t15 || $[36] !== t16 || $[37] !== tooltipItems) { 639 t17 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)(CapacityBar, { 640 size: "sm", 641 primaryOnly: t13, 642 tooltipItems, 643 children: [ 644 t14, 645 t15, 646 t16 647 ] 648 }); 649 $[33] = t13; 650 $[34] = t14; 651 $[35] = t15; 652 $[36] = t16; 653 $[37] = tooltipItems; 654 $[38] = t17; 655 } else t17 = $[38]; 656 let t18; 657 if ($[39] !== t12 || $[40] !== t17) { 658 t18 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("div", { 659 className: "d-flex align-items-center justify-content-end gap-2", 660 children: [t12, t17] 661 }); 662 $[39] = t12; 663 $[40] = t17; 664 $[41] = t18; 665 } else t18 = $[41]; 666 return t18; 667} 668function EmailAccountRow(t0) { 669 const $ = (0, import_compiler_runtime$7.c)(75); 670 const { account, info, failed, loading, allocating, selected, rowEditable, freePlan, currentUserId, connectPaths, onToggle, onRetry, onOpenSidepanel, onProtectInbox, onUnlocked, onActivateInboxProtection, onRampUpProtect, onReconnected } = t0; 671 let t1; 672 if ($[0] !== account.editable || $[1] !== account.id || $[2] !== connectPaths || $[3] !== freePlan || $[4] !== info) { 673 t1 = info ? accountStatus(info, { 674 accountId: account.id, 675 freePlan, 676 canEditAccount: account.editable, 677 connectPaths 678 }) : null; 679 $[0] = account.editable; 680 $[1] = account.id; 681 $[2] = connectPaths; 682 $[3] = freePlan; 683 $[4] = info; 684 $[5] = t1; 685 } else t1 = $[5]; 686 const status = t1; 687 const disabled = !rowEditable || Boolean(status?.blocksSelection && !selected); 688 const t2 = `outreach_settings_email_account_ids_${account.id}`; 689 let t3; 690 if ($[6] !== onToggle) { 691 t3 = (event) => onToggle(event.target.checked); 692 $[6] = onToggle; 693 $[7] = t3; 694 } else t3 = $[7]; 695 let t4; 696 if ($[8] !== account.id || $[9] !== disabled || $[10] !== selected || $[11] !== t2 || $[12] !== t3) { 697 t4 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("td", { 698 className: "h-table__checkbox", 699 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Checkbox, { 700 id: t2, 701 name: "outreach_settings[email_account_ids][]", 702 value: account.id, 703 checked: selected, 704 disabled, 705 onChange: t3 706 }) 707 }); 708 $[8] = account.id; 709 $[9] = disabled; 710 $[10] = selected; 711 $[11] = t2; 712 $[12] = t3; 713 $[13] = t4; 714 } else t4 = $[13]; 715 let t5; 716 if ($[14] !== account.providerEmail) { 717 t5 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("strong", { 718 className: "text-sm fw-medium", 719 children: account.providerEmail 720 }); 721 $[14] = account.providerEmail; 722 $[15] = t5; 723 } else t5 = $[15]; 724 let t6; 725 if ($[16] !== account.secondaryLabel) { 726 t6 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("div", { 727 className: "text-body-secondary", 728 children: account.secondaryLabel 729 }); 730 $[16] = account.secondaryLabel; 731 $[17] = t6; 732 } else t6 = $[17]; 733 let t7; 734 if ($[18] !== t5 || $[19] !== t6) { 735 t7 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("td", { children: [t5, t6] }); 736 $[18] = t5; 737 $[19] = t6; 738 $[20] = t7; 739 } else t7 = $[20]; 740 let t8; 741 if ($[21] !== loading) { 742 t8 = loading && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Icon, { loading: true }) }); 743 $[21] = loading; 744 $[22] = t8; 745 } else t8 = $[22]; 746 let t9; 747 if ($[23] !== failed || $[24] !== onRetry) {
748 t9 = failed && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TooltipProvider, { 749 delayDuration: 100, 750 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TooltipTrigger, { 751 asChild: true, 752 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)(TagPrimitive.TagRoot, { 753 variant: "danger", 754 children: [ 755 /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TagPrimitive.TagIcon, { icon: "exclamation-circle" }), 756 /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TagPrimitive.TagLabel, { children: t$5("info_load_failed_short") }), 757 /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 758 variant: "link", 759 size: "xs", 760 onClick: onRetry, 761 children: t$5("retry") 762 }) 763 ] 764 }) 765 }), /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(TooltipContent, { 766 side: "top", 767 children: t$5("info_load_failed") 768 })] }) 769 }); 770 $[23] = failed; 771 $[24] = onRetry; 772 $[25] = t9; 773 } else t9 = $[25]; 774 let t10; 775 if ($[26] !== status) { 776 t10 = status && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(StatusTag, { tag: status.tag }); 777 $[26] = status; 778 $[27] = t10; 779 } else t10 = $[27]; 780 let t11; 781 if ($[28] !== t10 || $[29] !== t8 || $[30] !== t9) { 782 t11 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("td", { 783 className: "text-center", 784 children: [ 785 t8, 786 t9, 787 t10 788 ] 789 }); 790 $[28] = t10; 791 $[29] = t8; 792 $[30] = t9; 793 $[31] = t11; 794 } else t11 = $[31]; 795 let t12; 796 if ($[32] !== allocating || $[33] !== info) {
797 t12 = allocating ? /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Icon, { loading: true }) : info?.allocated_recipients != null ? t$5("allocated_recipients", { 798 count: info.allocated_recipients, 799 formatted_count: CurrencyHelper.numberWithDelimiter(info.allocated_recipients) 800 }) : info ? "-" : null; 801 $[32] = allocating; 802 $[33] = info; 803 $[34] = t12; 804 } else t12 = $[34]; 805 let t13; 806 if ($[35] !== t12) { 807 t13 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("td", { 808 className: "text-end", 809 children: t12 810 }); 811 $[35] = t12; 812 $[36] = t13; 813 } else t13 = $[36]; 814 let t14; 815 if ($[37] !== account.effectiveDailyLimit || $[38] !== info) { 816 t14 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("td", { 817 className: "text-end", 818 children: info ? isSendingLocked(info) ? /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(SendingLocked, {}) : /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Capacity, { info }) : /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)(import_jsx_runtime$8.Fragment, { children: [ 819 /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("strong", { 820 className: "fw-medium", 821 children: "-" 822 }), 823 "â",
824 /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("strong", { 825 className: "fw-medium", 826 children: account.effectiveDailyLimit 827 }) 828 ] }) 829 }); 830 $[37] = account.effectiveDailyLimit; 831 $[38] = info; 832 $[39] = t14; 833 } else t14 = $[39]; 834 let t15; 835 if ($[40] !== account.ownerId || $[41] !== currentUserId || $[42] !== onReconnected || $[43] !== status) { 836 t15 = status?.action.kind === "reconnect" && currentUserId === account.ownerId && (status.action.turboMethod === "post" ? /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(SmtpImapReconnectButton, { 837 url: status.action.url, 838 label: status.action.label, 839 onReconnected 840 }) : /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("a", { 841 className: "h-button h-button--xs", 842 href: status.action.url, 843 "data-turbo-frame": "_top", 844 children: status.action.label 845 })); 846 $[40] = account.ownerId; 847 $[41] = currentUserId; 848 $[42] = onReconnected; 849 $[43] = status; 850 $[44] = t15; 851 } else t15 = $[44]; 852 let t16; 853 if ($[45] !== account.id || $[46] !== onUnlocked || $[47] !== status) { 854 t16 = status?.action.kind === "unlockSending" && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(UnlockSending, { 855 accountId: account.id, 856 dailyLimit: status.action.dailyMessagesLimit, 857 onUnlocked, 858 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 859 variant: "default", 860 size: "xs", 861 iconOnly: true, 862 title: t$5("unlock_sending_during_warmup"), 863 "aria-label": t$5("unlock_sending_during_warmup"), 864 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Icon, { 865 icon: "lock", 866 variant: "regular" 867 }) 868 }) 869 }); 870 $[45] = account.id; 871 $[46] = onUnlocked; 872 $[47] = status; 873 $[48] = t16; 874 } else t16 = $[48]; 875 let t17; 876 if ($[49] !== onActivateInboxProtection || $[50] !== status?.action.kind) { 877 t17 = status?.action.kind === "activateInboxProtection" && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 878 variant: "default", 879 size: "xs", 880 onClick: onActivateInboxProtection, 881 children: t$5("activate_inbox_protection") 882 }); 883 $[49] = onActivateInboxProtection; 884 $[50] = status?.action.kind; 885 $[51] = t17; 886 } else t17 = $[51]; 887 let t18; 888 if ($[52] !== onProtectInbox || $[53] !== status) { 889 t18 = status?.action.kind === "protectInbox" && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 890 variant: "default", 891 size: "xs", 892 onClick: () => status.action.kind === "protectInbox" && onProtectInbox(status.action.provider, status.action.email, status.action.providerLimit, status.action.paidUntil), 893 children: t$5("protect_email_account") 894 }); 895 $[52] = onProtectInbox; 896 $[53] = status; 897 $[54] = t18; 898 } else t18 = $[54]; 899 let t19; 900 if ($[55] !== onRampUpProtect || $[56] !== status) { 901 t19 = status?.action.kind === "rampUpProtect" && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(RampUpConfiguration, { 902 accountId: status.action.accountId, 903 providerLimit: status.action.providerLimit, 904 onRampUpChanged: onRampUpProtect, 905 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 906 variant: "default", 907 size: "xs", 908 children: t$5("protect_email_account") 909 }) 910 }); 911 $[55] = onRampUpProtect; 912 $[56] = status; 913 $[57] = t19; 914 } else t19 = $[57]; 915 let t20; 916 if ($[58] !== account.editable || $[59] !== onOpenSidepanel) { 917 t20 = account.editable && /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 918 variant: "default", 919 size: "xs", 920 iconOnly: true, 921 title: t$5("edit_email_account_tooltip"), 922 "aria-label": i18n.t("js.sequences_campaign_settings.actions.edit"), 923 onClick: onOpenSidepanel, 924 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Icon, { 925 icon: "pen", 926 variant: "regular" 927 }) 928 }); 929 $[58] = account.editable; 930 $[59] = onOpenSidepanel; 931 $[60] = t20; 932 } else t20 = $[60]; 933 let t21; 934 if ($[61] !== t15 || $[62] !== t16 || $[63] !== t17 || $[64] !== t18 || $[65] !== t19 || $[66] !== t20) { 935 t21 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("td", { 936 className: "text-end", 937 children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("div", { 938 className: "h-table__actions", 939 children: [ 940 t15, 941 t16, 942 t17, 943 t18, 944 t19, 945 t20 946 ] 947 }) 948 }); 949 $[61] = t15; 950 $[62] = t16; 951 $[63] = t17; 952 $[64] = t18; 953 $[65] = t19; 954 $[66] = t20; 955 $[67] = t21; 956 } else t21 = $[67]; 957 let t22; 958 if ($[68] !== t11 || $[69] !== t13 || $[70] !== t14 || $[71] !== t21 || $[72] !== t4 || $[73] !== t7) { 959 t22 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("tr", { children: [ 960 t4, 961 t7, 962 t11, 963 t13, 964 t14, 965 t21 966 ] }); 967 $[68] = t11; 968 $[69] = t13; 969 $[70] = t14; 970 $[71] = t21; 971 $[72] = t4; 972 $[73] = t7; 973 $[74] = t22; 974 } else t22 = $[74]; 975 return t22; 976} 977var import_compiler_runtime$7, import_jsx_runtime$8, t$5; 978function init_EmailAccountRow() { 979 return (init_EmailAccountRow = __esmMin((() => { 980 import_compiler_runtime$7 = require_compiler_runtime(); 981 init_button(); 982 init_checkbox(); 983 init_ramp_up_configuration(); 984 init_tag(); 985 init_icon(); 986 init_unlock_sending(); 987 init_capacity_bar(); 988 init_hint(); 989 init_tooltip(); 990 init_accountStatus(); 991 init_SmtpImapReconnectButton(); 992 init_currency_helper(); 993 import_jsx_runtime$8 = require_jsx_runtime(); 994 t$5 = (key, options) => i18n.t(`js.back.campaigns.settings.${key}`, options); 995 })))(); 996} 997//#endregion 998//#region app/frontend/pages/SequencesCampaignSettings/LaunchModal.tsx 999function LaunchModal({ campaignId, launchModal, senderAccountIds, connectionCheckPath, currentUserId }) { 1000 const [check, setCheck] = (0, import_react$8.useState)(null); 1001 (0, import_react$8.useEffect)(() => { 1002 if (!launchModal.connectionCheckEnqueued) return; 1003 setCheck(null); 1004 }, [launchModal]); 1005 const senderKey = checkedKey(senderAccountIds); 1006 (0, import_react$8.useEffect)(() => { 1007 setCheck(null); 1008 }, [senderKey]); 1009 const senderKeyRef = (0, import_react$8.useRef)(senderKey); 1010 senderKeyRef.current = senderKey; 1011 const connectionCheckPathRef = (0, import_react$8.useRef)(connectionCheckPath); 1012 connectionCheckPathRef.current = connectionCheckPath; 1013 const recheckRef = (0, import_react$8.useRef)({ 1014 key: senderKey, 1015 requests: 0 1016 }); 1017 const requestRecheck = (0, import_react$8.useRef)(() => { 1018 const state = recheckRef.current; 1019 if (state.key !== senderKeyRef.current) { 1020 state.key = senderKeyRef.current; 1021 state.requests = 0; 1022 } 1023 if (state.requests >= MAX_RECHECK_REQUESTS_PER_SENDER_SET) return; 1024 state.requests += 1; 1025 setTimeout(() => { 1026 fetch(connectionCheckPathRef.current, { 1027 credentials: "same-origin", 1028 headers: { Accept: "text/html" } 1029 }).catch(() => {}); 1030 }, RECHECK_LOCK_RELEASE_DELAY_MS); 1031 }); 1032 const cableParams = (0, import_react$8.useMemo)(() => ({
1033 channel: "CampaignConnectionCheckChannel", 1034 campaign_id: campaignId 1035 }), [campaignId]); 1036 const cableCallbacks = (0, import_react$8.useMemo)(() => ({ received: (data) => { 1037 const payload = data; 1038 if (!payload || !isBoolean(payload.ok)) return; 1039 if (Array.isArray(payload.account_ids) && checkedKey(payload.account_ids) !== senderKeyRef.current) { 1040 requestRecheck.current(); 1041 return; 1042 } 1043 setCheck(payload); 1044 } }), []); 1045 (0, import_react$8.useEffect)(() => { 1046 const onReset = () => setCheck(null); 1047 document.addEventListener(CONNECTION_CHECK_RESET_EVENT, onReset); 1048 return () => document.removeEventListener(CONNECTION_CHECK_RESET_EVENT, onReset); 1049 }, []); 1050 useActionCable({ 1051 params: cableParams, 1052 callbacks: cableCallbacks, 1053 enabled: launchModal.pendingConnectionCheck 1054 }); 1055 const formRef = (0, import_react$8.useRef)(null); 1056 const [flushingWrites, setFlushingWrites] = (0, import_react$8.useState)(false); 1057 function handleLaunchSubmit(event) { 1058 event.preventDefault(); 1059 if (flushingWrites) return; 1060 setFlushingWrites(true); 1061 flushSettingsWrites().then(() => { 1062 formRef.current?.submit(); 1063 }); 1064 } 1065 const blocked = launchModal.blockingReasons.length > 0; 1066 const awaitingCheck = launchModal.pendingConnectionCheck && !check?.ok; 1067 const { summary } = launchModal; 1068 return /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("div", { 1069 className: "modal fade schedule-modal", 1070 id: "schedule-modal", 1071 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("div", { 1072 className: "modal-dialog", 1073 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("div", { 1074 className: "modal-content", 1075 children: [ 1076 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("div", { 1077 className: "modal-header", 1078 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("h2", { 1079 className: "modal-title", 1080 children: launchModal.title 1081 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("button", { 1082 type: "button", 1083 className: "modal-close", 1084 "data-bs-dismiss": "modal", 1085 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("span", { children: "Ã" }) 1086 })] 1087 }), 1088 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("div", { 1089 className: "modal-body", 1090 children: [
1091 blocked && /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SettingsAlert, { 1092 variant: "danger", 1093 icon: "exclamation-triangle", 1094 className: "mb-3", 1095 html: launchModal.blockingReasons.join("<br>") 1096 }), 1097 launchModal.pendingConnectionCheck && /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(ConnectionCheckAlert, { 1098 check, 1099 currentUserId 1100 }), 1101 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("ul", { 1102 className: "list-with-icons", 1103 children: [ 1104 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("li", { 1105 className: "list-with-icons__item", 1106 children: [ 1107 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1108 icon: "envelope", 1109 variant: "regular", 1110 className: "list-with-icons__icon" 1111 }), 1112 t$4("sent_from"), 1113 " ", 1114 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("strong", { children: summary.senderEmails.join(", ") }), 1115 summary.moreEmailsHtml && /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("span", { dangerouslySetInnerHTML: { __html: ` ${summary.moreEmailsHtml}` } }) 1116 ] 1117 }), 1118 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("li", { 1119 className: "list-with-icons__item", 1120 children: [ 1121 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1122 icon: "users", 1123 variant: "regular", 1124 className: "list-with-icons__icon" 1125 }), 1126 t$4("sent_to"), 1127 " ", 1128 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("strong", { children: summary.recipientsText }) 1129 ] 1130 }), 1131 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("li", { 1132 className: "list-with-icons__item", 1133 children: [ 1134 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1135 icon: "calendar", 1136 variant: "regular", 1137 className: "list-with-icons__icon" 1138 }), 1139 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("span", { dangerouslySetInnerHTML: { __html: summary.dailyLimitHtml } }), 1140 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(Popover, { children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(PopoverTrigger, { 1141 asChild: true, 1142 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("button", { 1143 type: "button", 1144 className: "question-circle", 1145 "aria-label": t$4("daily_limit_help"), 1146 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1147 icon: "question-circle", 1148 variant: "regular" 1149 }) 1150 }) 1151 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(PopoverContent, { 1152 side: "right", 1153 className: "popover-body", 1154 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("p", { dangerouslySetInnerHTML: { __html: summary.dailyLimitPopoverHtml } }
1154), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("a", { 1155 href: summary.settingsPath, 1156 className: "h-button", 1157 children: i18n.t("js.sequences_campaign_settings.actions.settings") 1158 })] 1159 })] }) 1160 ] 1161 }), 1162 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("li", { 1163 className: "list-with-icons__item", 1164 children: [ 1165 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1166 icon: "clock", 1167 variant: "regular", 1168 className: "list-with-icons__icon" 1169 }), 1170 t$4("starts"), 1171 " ", 1172 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("strong", { children: summary.startsText }), 1173 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(Popover, { children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(PopoverTrigger, { 1174 asChild: true, 1175 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("button", { 1176 type: "button", 1177 className: "question-circle", 1178 "aria-label": t$4("sending_help"), 1179 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1180 icon: "question-circle", 1181 variant: "regular" 1182 }) 1183 }) 1184 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(PopoverContent, { 1185 side: "right", 1186 className: "popover-body", 1187 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("p", { children: summary.sendingPopover }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("a", { 1188 href: summary.sendingWindowSettingsPath, 1189 className: "h-button", 1190 children: i18n.t("js.sequences_campaign_settings.actions.settings") 1191 })] 1192 })] }) 1193 ] 1194 }), 1195 summary.followUpsHtml && /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("li", { 1196 className: "list-with-icons__item", 1197 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1198 icon: "repeat", 1199 variant: "regular", 1200 className: "list-with-icons__icon" 1201 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("span", { dangerouslySetInnerHTML: { __html: summary.followUpsHtml } })] 1202 }) 1203 ] 1204 }),
1205 launchModal.hasPendingLeads && /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("div", { 1206 className: "schedule-modal-info", 1207 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 1208 icon: "info-circle", 1209 variant: "regular", 1210 className: "schedule-modal-info__icon" 1211 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("p", { children: t$4("pending_description") })] 1212 }) 1213 ] 1214 }), 1215 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("div", { 1216 className: "modal-footer", 1217 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Button, { 1218 variant: "default", 1219 type: "button", 1220 "data-bs-dismiss": "modal", 1221 children: i18n.t("js.cancel") 1222 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("form", { 1223 ref: formRef, 1224 action: launchModal.startPath, 1225 method: "post", 1226 "data-turbo-frame": "_top", 1227 onSubmit: handleLaunchSubmit, 1228 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("input", { 1229 type: "hidden", 1230 name: "authenticity_token", 1231 value: csrfToken() 1232 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Button, { 1233 variant: "primary", 1234 type: "submit", 1235 disabled: blocked || awaitingCheck || flushingWrites, 1236 "aria-busy": flushingWrites, 1237 children: flushingWrites ? t$4("saving_settings") : launchModal.launchButtonLabel 1238 })] 1239 })] 1240 }) 1241 ] 1242 }) 1243 }) 1244 }); 1245} 1246function ConnectionCheckAlert(t0) { 1247 const $ = (0, import_compiler_runtime$6.c)(19); 1248 const { check, currentUserId } = t0; 1249 if (!check) { 1250 let t1; 1251 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 1252 t1 = /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SettingsAlert, { 1253 variant: "info", 1254 iconLoading: true, 1255 className: "mb-3", 1256 html: `<strong>${t$4("connection_check.checking_title")}</strong> ${t$4("connection_check.checking_description")}` 1257 }); 1258 $[0] = t1; 1259 } else t1 = $[0]; 1260 return t1; 1261 } 1262 if (check.ok) { 1263 let t1; 1264 if ($[1] !== check.note) { 1265 t1 = check.note ? noteAlert(check.note) : null; 1266 $[1] = check.note; 1267 $[2] = t1; 1268 } else t1 = $[2]; 1269 const note = t1; 1270 if (note) { 1271 const t2 = `<strong>${note.title}</strong> ${note.description}`; 1272 let t3; 1273 if ($[3] !== note.icon || $[4] !== note.variant || $[5] !== t2) { 1274 t3 = /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SettingsAlert, { 1275 variant: note.variant, 1276 icon: note.icon, 1277 className: "mb-3", 1278 html: t2 1279 }); 1280 $[3] = note.icon; 1281 $[4] = note.variant; 1282 $[5] = t2; 1283 $[6] = t3; 1284 } else t3 = $[6]; 1285 return t3; 1286 } 1287 let t2; 1288 if ($[7] === Symbol.for("react.memo_cache_sentinel")) { 1289 t2 = /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SettingsAlert, { 1290 variant: "success", 1291 icon: "check-circle", 1292 className: "mb-3", 1293 html: t$4("connection_check.success_description") 1294 }); 1295 $[7] = t2; 1296 } else t2 = $[7]; 1297 return t2; 1298 } 1299 let t1; 1300 if ($[8] !== check.emails) { 1301 t1 = t$4("connection_check.error_description", { emails: check.emails.join(", ") }); 1302 $[8] = check.emails; 1303 $[9] = t1; 1304 } else t1 = $[9]; 1305 const description = t1; 1306 let t2; 1307 if ($[10] === Symbol.for("react.memo_cache_sentinel")) { 1308 t2 = i18n.t("js.sequences_campaign_settings.actions.reconnect"); 1309 $[10] = t2; 1310 } else t2 = $[10]; 1311 const reconnectLabel = t2; 1312 const canReconnect = check.reconnect_owner_id == null || check.reconnect_owner_id === currentUserId; 1313 const smtpImapRetry = canReconnect && check.reconnect_url && check.reconnect_method === "post"; 1314 let t3; 1315 if ($[11] === Symbol.for("react.memo_cache_sentinel")) { 1316 t3 = t$4("connection_check.error_title"); 1317 $[11] = t3; 1318 } else t3 = $[11]; 1319 const t4 = `<strong>${t3}</strong> ${description}${check.reconnect_url && canReconnect && !smtpImapRetry ? ` <a href="${check.reconnect_url}" class="h-button h-button--sm" data-turbo-frame="_top">${reconnectLabel}</a>` : ""}`; 1320 let t5; 1321 if ($[12] !== check.reconnect_edit_url || $[13] !== check.reconnect_url || $[14] !== smtpImapRetry) { 1322 t5 = smtpImapRetry ? /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SmtpImapReconnectButton, { 1323 url: unsafeCast(check.reconnect_url), 1324 editUrl: check.reconnect_edit_url, 1325 label: reconnectLabel, 1326 size: "sm", 1327 onReconnected: _temp$2 1328 }) : void 0; 1329 $[12] = check.reconnect_edit_url; 1330 $[13] = check.reconnect_url; 1331 $[14] = smtpImapRetry; 1332 $[15] = t5; 1333 } else t5 = $[15]; 1334 let t6; 1335 if ($[16] !== t4 || $[17] !== t5) { 1336 t6 = /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SettingsAlert, { 1337 variant: "danger", 1338 icon: "exclamation-triangle", 1339 className: "mb-3", 1340 html: t4, 1341 actions: t5 1342 }); 1343 $[16] = t4; 1344 $[17] = t5; 1345 $[18] = t6; 1346 } else t6 = $[18]; 1347 return t6; 1348} 1349function _temp$2() { 1350 return document.dispatchEvent(new CustomEvent("email-account:updated")); 1351} 1352function noteAlert(note) { 1353 const info = { 1354 variant: "info", 1355 icon: "info-circle" 1356 }; 1357 const warning = { 1358 variant: "warning", 1359 icon: "exclamation-triangle" 1360 }; 1361 const emails = escapeHtml$1(note.email ?? ""); 1362 switch (note.kind) { 1363 case "warming": return { 1364 ...info, 1365 title: t$4("connection_check.warming_title", { emails }), 1366 description: t$4("connection_check.warming_description", { date: escapeHtml$1(note.date ?? "") }) 1367 }; 1368 case "paused": return { 1369 ...info, 1370 title: t$4("connection_check.paused_title", { emails }),
1371 description: t$4("connection_check.paused_description", { error: escapeHtml$1(note.error ?? "") }) 1372 }; 1373 case "paused_no_reason": return { 1374 ...info, 1375 title: t$4("connection_check.paused_title", { emails }), 1376 description: t$4("connection_check.paused_no_reason_description") 1377 }; 1378 case "restricted": return { 1379 ...warning, 1380 title: t$4("connection_check.provider_restricted_title", { emails }), 1381 description: t$4("connection_check.provider_restricted_description", { emails }) 1382 }; 1383 case "none_ready": return { 1384 ...info, 1385 title: t$4("connection_check.none_ready_title"), 1386 description: t$4("connection_check.none_ready_description") 1387 }; 1388 case "none_ready_restricted": return { 1389 ...warning, 1390 title: t$4("connection_check.none_ready_title"), 1391 description: t$4("connection_check.none_ready_restricted_description") 1392 }; 1393 default: return null; 1394 } 1395} 1396var import_compiler_runtime$6, import_react$8, import_jsx_runtime$7, t$4, csrfToken, CONNECTION_CHECK_RESET_EVENT, checkedKey, MAX_RECHECK_REQUESTS_PER_SENDER_SET, RECHECK_LOCK_RELEASE_DELAY_MS; 1397function init_LaunchModal() { 1398 return (init_LaunchModal = __esmMin((() => { 1399 import_compiler_runtime$6 = require_compiler_runtime(); 1400 import_react$8 = require_react(); 1401 init_button(); 1402 init_icon(); 1403 init_popover(); 1404 init_utils(); 1405 init_SmtpImapReconnectButton(); 1406 init_useActionCable(); 1407 init_SettingsAlert(); 1408 init_settingsWriteQueue(); 1409 import_jsx_runtime$7 = require_jsx_runtime(); 1410 t$4 = (key, options) => i18n.t(`js.sequences_campaign_settings.launch_modal.${key}`, options); 1411 csrfToken = () => document.querySelector("meta[name=\"csrf-token\"]")?.content ?? ""; 1412 CONNECTION_CHECK_RESET_EVENT = "campaign:connection-check-reset"; 1413 checkedKey = (ids) => [...ids].sort((a, b) => a - b).join(","); 1414 MAX_RECHECK_REQUESTS_PER_SENDER_SET = 2; 1415 RECHECK_LOCK_RELEASE_DELAY_MS = 3e3; 1416 })))(); 1417} 1418//#endregion 1419//#region app/frontend/pages/SequencesCampaignSettings/SettingToggle.tsx 1420function SettingToggle(t0) { 1421 const $ = (0, import_compiler_runtime$5.c)(25); 1422 const { id, label, hint, checked, disabled, disabledTooltip, onCheckedChange, children } = t0; 1423 const hintId = `${id}-hint`; 1424 let t1; 1425 if ($[0] !== checked || $[1] !== disabled || $[2] !== hintId || $[3] !== id || $[4] !== onCheckedChange) { 1426 t1 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(Switch, { 1427 id, 1428 checked, 1429 disabled, 1430 "aria-describedby": hintId, 1431 onCheckedChange 1432 }); 1433 $[0] = checked; 1434 $[1] = disabled; 1435 $[2] = hintId; 1436 $[3] = id; 1437 $[4] = onCheckedChange; 1438 $[5] = t1; 1439 } else t1 = $[5]; 1440 const control = t1; 1441 let t2; 1442 if ($[6] !== id || $[7] !== label) { 1443 t2 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(Label, { 1444 className: "h-label--md mb-1", 1445 htmlFor: id, 1446 children: label 1447 }); 1448 $[6] = id; 1449 $[7] = label; 1450 $[8] = t2; 1451 } else t2 = $[8]; 1452 let t3; 1453 if ($[9] !== hint || $[10] !== hintId) { 1454 t3 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 1455 className: "h-hint", 1456 id: hintId, 1457 children: hint 1458 }); 1459 $[9] = hint; 1460 $[10] = hintId; 1461 $[11] = t3; 1462 } else t3 = $[11]; 1463 let t4; 1464 if ($[12] !== t2 || $[13] !== t3) { 1465 t4 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsxs)("div", { 1466 className: "h-form-group__infos", 1467 children: [t2, t3] 1468 }); 1469 $[12] = t2; 1470 $[13] = t3; 1471 $[14] = t4; 1472 } else t4 = $[14]; 1473 let t5; 1474 if ($[15] !== control || $[16] !== disabled || $[17] !== disabledTooltip) { 1475 t5 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 1476 className: "h-form-group__input pr-2", 1477 children: disabled && disabledTooltip ? /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(TooltipProvider, { 1478 delayDuration: 100, 1479 children: /* @__PURE__ */ (0, import_jsx_runtime$6.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(TooltipTrigger, { 1480 asChild: true, 1481 children: /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("span", { children: control }) 1482 }), /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(TooltipContent, { 1483 side: "top", 1484 children: disabledTooltip 1485 })] }) 1486 }) : control 1487 }); 1488 $[15] = control; 1489 $[16] = disabled; 1490 $[17] = disabledTooltip; 1491 $[18] = t5; 1492 } else t5 = $[18]; 1493 let t6; 1494 if ($[19] !== t4 || $[20] !== t5) { 1495 t6 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsxs)("div", { 1496 className: "h-form-group align-items-center", 1497 children: [t4, t5] 1498 }); 1499 $[19] = t4; 1500 $[20] = t5; 1501 $[21] = t6; 1502 } else t6 = $[21]; 1503 let t7; 1504 if ($[22] !== children || $[23] !== t6) { 1505 t7 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsxs)(import_jsx_runtime$6.Fragment, { children: [t6, children] }); 1506 $[22] = children; 1507 $[23] = t6; 1508 $[24] = t7; 1509 } else t7 = $[24]; 1510 return t7; 1511} 1512var import_compiler_runtime$5, import_jsx_runtime$6; 1513function init_SettingToggle() { 1514 return (init_SettingToggle = __esmMin((() => { 1515 import_compiler_runtime$5 = require_compiler_runtime(); 1516 init_label(); 1517 init_switch(); 1518 init_tooltip(); 1519 import_jsx_runtime$6 = require_jsx_runtime(); 1520 })))(); 1521} 1522//#endregion 1523//#region app/frontend/pages/SequencesCampaignSettings/hooks/useAllocationStatus.ts 1524function useAllocationStatus(allocationStatusPath, seedAllocating, onComplete) { 1525 const [allocating, setAllocating] = (0, import_react$7.useState)(seedAllocating); 1526 const timerRef = (0, import_react$7.useRef)(null); 1527 const abortRef = (0, import_react$7.useRef)(null); 1528 const mountedRef = (0, import_react$7.useRef)(true); 1529 const onCompleteRef = (0, import_react$7.useRef)(onComplete); 1530 onCompleteRef.current = onComplete; 1531 (0, import_react$7.useEffect)(() => { 1532 if (seedAllocating) setAllocating(true); 1533 }, [seedAllocating]); 1534 const start = (0, import_react$7.useCallback)(() => setAllocating(true), []); 1535 (0, import_react$7.useEffect)(() => { 1536 mountedRef.current = true; 1537 return () => { 1538 mountedRef.current = false;
1539 abortRef.current?.abort(); 1540 if (timerRef.current) clearTimeout(timerRef.current); 1541 }; 1542 }, []); 1543 (0, import_react$7.useEffect)(() => { 1544 if (!allocating) return; 1545 let cancelled = false; 1546 const poll = async () => { 1547 abortRef.current?.abort(); 1548 const controller = new AbortController(); 1549 abortRef.current = controller; 1550 try { 1551 const response = await fetch(allocationStatusPath, { 1552 credentials: "same-origin", 1553 headers: { Accept: "application/json" }, 1554 signal: controller.signal 1555 }); 1556 if (!response.ok) { 1557 if (response.status >= 400 && response.status < 500) { 1558 if (!cancelled && mountedRef.current) setAllocating(false); 1559 return; 1560 } 1561 throw new Error(`HTTP ${response.status}`); 1562 } 1563 const data = await response.json(); 1564 if (cancelled || !mountedRef.current) return; 1565 if (data?.allocating) timerRef.current = setTimeout(poll, POLL_INTERVAL_MS); 1566 else { 1567 setAllocating(false); 1568 onCompleteRef.current?.(); 1569 } 1570 } catch (error) { 1571 if (error.name === "AbortError" || cancelled || !mountedRef.current) return; 1572 timerRef.current = setTimeout(poll, POLL_INTERVAL_MS); 1573 } 1574 }; 1575 poll(); 1576 return () => { 1577 cancelled = true; 1578 abortRef.current?.abort(); 1579 if (timerRef.current) clearTimeout(timerRef.current); 1580 }; 1581 }, [allocating, allocationStatusPath]); 1582 return { 1583 allocating, 1584 startAllocationPolling: start 1585 }; 1586} 1587var import_react$7, POLL_INTERVAL_MS; 1588function init_useAllocationStatus() { 1589 return (init_useAllocationStatus = __esmMin((() => { 1590 import_react$7 = require_react(); 1591 POLL_INTERVAL_MS = 3e3; 1592 })))(); 1593} 1594//#endregion 1595//#region app/frontend/pages/SequencesCampaignSettings/hooks/useEmailAccountsInfo.ts 1596function useEmailAccountsInfo(batchInfoPath, enabled) { 1597 const [infoById, setInfoById] = (0, import_react$6.useState)(null); 1598 const [failed, setFailed] = (0, import_react$6.useState)(false); 1599 const abortRef = (0, import_react$6.useRef)(null); 1600 const debounceRef = (0, import_react$6.useRef)(null); 1601 const mountedRef = (0, import_react$6.useRef)(true); 1602 const fetchNow = (0, import_react$6.useCallback)(async () => { 1603 abortRef.current?.abort(); 1604 const controller = new AbortController(); 1605 abortRef.current = controller; 1606 setFailed(false); 1607 let timedOut = false; 1608 const timeoutId = setTimeout(() => { 1609 timedOut = true; 1610 controller.abort(); 1611 }, REQUEST_TIMEOUT_MS); 1612 try { 1613 const response = await fetch(batchInfoPath, { 1614 credentials: "same-origin", 1615 headers: { Accept: "application/json" }, 1616 signal: controller.signal 1617 }); 1618 if (!response.ok) throw new Error(`HTTP ${response.status}`); 1619 const data = await response.json(); 1620 if (!mountedRef.current) return; 1621 setInfoById(data ?? {}); 1622 setFailed(false); 1623 } catch (error) { 1624 if (!mountedRef.current) return; 1625 if (error.name === "AbortError" && !timedOut) return; 1626 setInfoById(null); 1627 setFailed(true); 1628 } finally { 1629 clearTimeout(timeoutId); 1630 } 1631 }, [batchInfoPath]); 1632 const refresh = (0, import_react$6.useCallback)(() => { 1633 if (debounceRef.current) clearTimeout(debounceRef.current); 1634 debounceRef.current = setTimeout(() => { 1635 if (mountedRef.current) fetchNow(); 1636 }, REFRESH_DEBOUNCE_MS); 1637 }, [fetchNow]); 1638 (0, import_react$6.useEffect)(() => { 1639 mountedRef.current = true; 1640 return () => { 1641 mountedRef.current = false; 1642 abortRef.current?.abort(); 1643 if (debounceRef.current) clearTimeout(debounceRef.current); 1644 }; 1645 }, []); 1646 (0, import_react$6.useEffect)(() => { 1647 if (!enabled) return; 1648 fetchNow(); 1649 }, [enabled, fetchNow]); 1650 return { 1651 infoById, 1652 failed, 1653 refresh, 1654 retry: fetchNow 1655 }; 1656} 1657var import_react$6, REFRESH_DEBOUNCE_MS, REQUEST_TIMEOUT_MS; 1658function init_useEmailAccountsInfo() { 1659 return (init_useEmailAccountsInfo = __esmMin((() => { 1660 import_react$6 = require_react(); 1661 REFRESH_DEBOUNCE_MS = 300; 1662 REQUEST_TIMEOUT_MS = 2e4; 1663 })))(); 1664} 1665//#endregion 1666//#region app/frontend/pages/SequencesCampaignSettings/hooks/useSettingsWriteQueueTeardown.ts 1667/** 1668* Run `onNavigate` the moment the user starts leaving this page, whichever 1669* router takes them. 1670* 1671* Two of them do, and either can strand a write the page has armed: 1672* 1673* - **Inertia.** `router.on("before")` fires synchronously as any visit starts. 1674* The page's own saves are visits too, so `isDispatchingSettingsWrite()` 1675* keeps them from being mistaken for the user leaving. 1676* - **Turbo.** The header's Close, breadcrumb and History links sit inside 1677* `data-turbo="true"`, and `useNavigationBridge` deliberately hands that 1678* subtree back to Turbo Drive â so those clicks never reach the Inertia 1679* listener above. `turbo:before-visit` is Turbo's equivalent synchronous 1680* hook; `turbo:visit` covers restoration visits, which skip `before-visit`. 1681* Both are page-level navigation events, so a turbo-frame load (the SMTP/IMAP 1682* modal, the email-accounts status frame) does not trip them â which matters, 1683* because those are not the user leaving and must not drop a queued edit. 1684* - **Turbo mutations.** The header's Pause, Duplicate, Rename, Archive and 1685* Delete all reach the server as Turbo *form submissions* (Rename is a real 1686* form; the rest are `data-turbo-method` links, which Turbo â or, for the 1687* body-appended confirm-deletion modal, `useNavigationBridge` â replays as a 1688* hidden form). Turbo only proposes the visit once that response comes back 1689* as a redirect, so `turbo:before-visit` fires far too late: in the meantime
1690* a queued PUT can start, or the in-flight PUT's own redirect can land and 1691* race the action the user asked for. `turbo:submit-start` is the moment the 1692* request leaves, so that is the real boundary â scoped by 1693* `submissionLeavesThePage` below. 1694* 1695* Unmount alone is too late for either: a navigation is cancelled before its 1696* response arrives, so the page can stay mounted with the damage already done. 1697*/ 1698function useSettingsNavigationStart(onNavigate) { 1699 const handlerRef = (0, import_react$5.useRef)(onNavigate); 1700 handlerRef.current = onNavigate; 1701 (0, import_react$5.useEffect)(() => { 1702 const fire = () => handlerRef.current(); 1703 const fireIfLeavingThePage = (event) => { 1704 if (submissionLeavesThePage(event)) fire(); 1705 }; 1706 const stopListening = router.on("before", () => { 1707 if (!isDispatchingSettingsWrite()) fire(); 1708 }); 1709 document.addEventListener("turbo:before-visit", fire); 1710 document.addEventListener("turbo:visit", fire); 1711 document.addEventListener("turbo:submit-start", fireIfLeavingThePage); 1712 return () => { 1713 stopListening(); 1714 document.removeEventListener("turbo:before-visit", fire); 1715 document.removeEventListener("turbo:visit", fire); 1716 document.removeEventListener("turbo:submit-start", fireIfLeavingThePage); 1717 }; 1718 }, []); 1719} 1720/** 1721* Whether a `turbo:submit-start` is a submission that navigates the whole page. 1722* 1723* Unlike `turbo:before-visit`, this event fires for *every* Turbo form 1724* submission in the document, and this page has submissions that are not the 1725* user leaving: the SMTP/IMAP connect and edit forms are rendered inside 1726* `<turbo-frame id="new_smtp_imap_connection">` / `"edit_smtp_imap_connection"` 1727* and replace only that frame. Abandoning the settings writes for those would 1728* silently drop the edit the user was in the middle of making. 1729* 1730* The rule is Turbo's own frame resolution (`FormSubmitObserver`): the submitter's 1731* `data-turbo-frame` wins over the form's, `_top` means the page, and a form with 1732* no override is scoped to the `<turbo-frame>` it is rendered in, if any. 1733*/ 1734function submissionLeavesThePage(event) { 1735 const form = event.target; 1736 if (!(form instanceof HTMLFormElement)) return false; 1737 const frameId = (event.detail?.formSubmission?.submitter)?.getAttribute("data-turbo-frame") ?? form.getAttribute("data-turbo-frame"); 1738 if (frameId) return frameId === "_top"; 1739 return form.closest("turbo-frame") === null; 1740} 1741/** 1742* Keep the settings write queue from outliving the page. 1743* 1744* The queue is module-level, so both a write still waiting its turn and the one 1745* in flight survive the page component, and either can drag the user back: 1746* the waiting one by starting a `router.put` on top of the navigation, the 1747* running one by resolving its redirect after the destination has rendered. 1748* `abandonSettingsWrites` gives up both â see its doc for why cancelling the 1749* running visit does not cost the save. 1750*/ 1751function useSettingsWriteQueueTeardown() { 1752 const $ = (0, import_compiler_runtime$4.c)(1); 1753 useSettingsNavigationStart(abandonSettingsWrites); 1754 let t0; 1755 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 1756 t0 = []; 1757 $[0] = t0; 1758 } else t0 = $[0]; 1759 (0, import_react$5.useEffect)(_temp2$1, t0); 1760} 1761function _temp2$1() { 1762 return _temp$1; 1763} 1764function _temp$1() { 1765 return abandonSettingsWrites(); 1766} 1767var import_compiler_runtime$4, import_react$5; 1768function init_useSettingsWriteQueueTeardown() { 1769 return (init_useSettingsWriteQueueTeardown = __esmMin((() => { 1770 import_compiler_runtime$4 = require_compiler_runtime(); 1771 import_react$5 = require_react(); 1772 init_dist$1(); 1773 init_settingsWriteQueue(); 1774 })))(); 1775} 1776//#endregion 1777//#region app/frontend/pages/SequencesCampaignSettings/EmailAccountsSection.tsx 1778function EmailAccountsSection({ campaignId, emailAccounts, currentUserId }) { 1779 const [selectedIds, setSelectedIds] = (0, import_react$4.useState)(emailAccounts.selectedIds); 1780 const [bccEnabled, setBccEnabled] = (0, import_react$4.useState)(Boolean(emailAccounts.bccRecipient)); 1781 const [bcc, setBcc] = (0, import_react$4.useState)(emailAccounts.bccRecipient ?? ""); 1782 const handledBccRef = (0, import_react$4.useRef)(emailAccounts.bccRecipient ?? ""); 1783 const saveTimer = (0, import_react$4.useRef)(null); 1784 const pendingSaveRef = (0, import_react$4.useRef)(null); 1785 const selectAllRef = (0, import_react$4.useRef)(null); 1786 const bccInputRef = (0, import_react$4.useRef)(null); 1787 const smtpImapLinkRef = (0, import_react$4.useRef)(null); 1788 const handleBccInputRef = (0, import_react$4.useRef)(() => {}); 1789 const hasAccounts = emailAccounts.accounts.length > 0; 1790 const { infoById, failed, refresh, retry } = useEmailAccountsInfo(emailAccounts.batchInfoPath, hasAccounts); 1791 const { allocating, startAllocationPolling } = useAllocationStatus(emailAccounts.allocationStatusPath, emailAccounts.allocating, refresh); 1792 const rowDisabled = (0, import_react$4.useMemo)(() => { 1793 const map = /* @__PURE__ */ new Map(); 1794 for (const account of emailAccounts.accounts) { 1795 const info = infoById?.[account.id]; 1796 const status = info ? accountStatus(info, { 1797 accountId: account.id, 1798 freePlan: emailAccounts.freePlan, 1799 canEditAccount: account.editable, 1800 connectPaths: emailAccounts.connectPaths 1801 }) : null; 1802 const selected = selectedIds.includes(account.id); 1803 map.set(account.id, !emailAccounts.editable || Boolean(status?.blocksSelection && !selected)); 1804 } 1805 return map; 1806 }, [ 1807 emailAccounts.accounts, 1808 emailAccounts.connectPaths, 1809 emailAccounts.editable, 1810 emailAccounts.freePlan, 1811 infoById, 1812 selectedIds 1813 ]); 1814 const selectable = emailAccounts.accounts.filter((account_0) => !rowDisabled.get(account_0.id)); 1815 const checkedSelectable = selectable.filter((account_1) => selectedIds.includes(account_1.id)); 1816 const allChecked = selectable.length > 0 && checkedSelectable.length === selectable.length; 1817 (0, import_react$4.useEffect)(() => { 1818 if (selectAllRef.current) selectAllRef.current.indeterminate = checkedSelectable.length > 0 && !allChecked; 1819 }, [allChecked, checkedSelectable.length]); 1820 const writeKey = `email-accounts:${campaignId}`; 1821 function syncFromProps(next, submitted) { 1822 setSelectedIds((current) => sameIds(current, submitted.ids) ? next.selectedIds : current); 1823 if (handledBccRef.current === submitted.bcc) { 1824 handledBccRef.current = next.bccRecipient ?? ""; 1825 setBcc(next.bccRecipient ?? ""); 1826 } 1827 setBccEnabled((enabled) => enabled || Boolean(next.bccRecipient)); 1828 } 1829 function save(nextIds, nextBcc) { 1830 if (saveTimer.current) clearTimeout(saveTimer.current); 1831 const submit = () => { 1832 if (!isSubmittableBcc(nextBcc)) return; 1833 enqueueSettingsPut(sequence_settings_path(campaignId), { outreach_settings: { 1834 email_account_ids: nextIds, 1835 bcc_recipient: nextBcc 1836 } }, { 1837 preserveScroll: true, 1838 preserveState: true, 1839 onSuccess: (page) => { 1840 const next_0 = unsafeCast(page.props).emailAccounts; 1841 if (next_0.allocating) startAllocationPolling(); 1842 syncFromProps(next_0, { 1843 ids: nextIds, 1844 bcc: nextBcc 1845 }); 1846 } 1847 }, writeKey); 1848 }; 1849 pendingSaveRef.current = submit; 1850 saveTimer.current = setTimeout(() => { 1851 saveTimer.current = null; 1852 pendingSaveRef.current = null; 1853 submit(); 1854 }, SAVE_DEBOUNCE_MS); 1855 } 1856 function cancelPendingSave() { 1857 if (!saveTimer.current) return; 1858 clearTimeout(saveTimer.current); 1859 saveTimer.current = null; 1860 pendingSaveRef.current = null; 1861 } 1862 (0, import_react$4.useEffect)(() => { 1863 const onFlush = () => { 1864 const submit_0 = pendingSaveRef.current; 1865 if (!submit_0) return; 1866 cancelPendingSave(); 1867 submit_0(); 1868 }; 1869 document.addEventListener(SETTINGS_WRITES_FLUSH_EVENT, onFlush); 1870 return () => document.removeEventListener(SETTINGS_WRITES_FLUSH_EVENT, onFlush); 1871 }, []); 1872 (0, import_react$4.useEffect)(() => cancelPendingSave, []); 1873 useSettingsNavigationStart(cancelPendingSave); 1874 (0, import_react$4.useEffect)(() => { 1875 if (!new URLSearchParams(window.location.search).get("connect_smtp")) return; 1876 smtpImapLinkRef.current?.click(); 1877 }, []); 1878 function handleBccInput(value) { 1879 if (value === handledBccRef.current) return; 1880 handledBccRef.current = value; 1881 setBcc(value); 1882 save(selectedIds, value); 1883 } 1884 (0, import_react$4.useEffect)(() => { 1885 handleBccInputRef.current = handleBccInput; 1886 }); 1887 (0, import_react$4.useEffect)(() => { 1888 const input = bccInputRef.current; 1889 if (!input) return; 1890 const onInput = () => handleBccInputRef.current(input.value); 1891 input.addEventListener("input", onInput); 1892 return () => input.removeEventListener("input", onInput); 1893 }, []); 1894 function toggleAccount(accountId, checked) { 1895 const next_1 = checked ? [...selectedIds, accountId] : selectedIds.filter((id) => id !== accountId); 1896 setSelectedIds(next_1); 1897 save(next_1, bcc); 1898 } 1899 function toggleAll(checked_0) { 1900 const selectableIds = selectable.map((account_2) => account_2.id); 1901 const next_2 = checked_0 ? Array.from(/* @__PURE__ */ new Set([...selectedIds, ...selectableIds])) : selectedIds.filter((id_0) => !selectableIds.includes(id_0)); 1902 setSelectedIds(next_2); 1903 save(next_2, bcc); 1904 } 1905 async function activateInboxProtection(accountId_0) { 1906 const headers = { 1907 "Content-Type": "application/json", 1908 Accept: "application/json", 1909 "X-CSRF-Token": document.querySelector("meta[name=\"csrf-token\"]")?.content ?? "" 1910 }; 1911 try {
1912 const response = await fetch(email_accounts_warmup_activation_path(accountId_0), { 1913 method: "POST", 1914 credentials: "same-origin", 1915 headers 1916 }); 1917 if (!response.ok) throw new Error(`HTTP ${response.status}`); 1918 const account_3 = await response.json(); 1919 try { 1920 await fetch(warmup_unlock_email_account_path(accountId_0), { 1921 method: "POST", 1922 credentials: "same-origin", 1923 headers, 1924 body: JSON.stringify({ warmup: { daily_messages_limit: account_3.daily_messages_limit } }) 1925 }); 1926 } catch {} 1927 refresh(); 1928 } catch { 1929 App.displayFlash("error", i18n.t("js.back.campaigns.settings.activate_error")); 1930 } 1931 } 1932 const connectionCheckPath = emailAccounts.connectionCheckPath; 1933 const handleReconnected = (0, import_react$4.useCallback)(() => { 1934 refresh(); 1935 document.dispatchEvent(new CustomEvent(CONNECTION_CHECK_RESET_EVENT)); 1936 fetch(connectionCheckPath, { 1937 credentials: "same-origin", 1938 headers: { Accept: "text/html" } 1939 }).catch(() => {}); 1940 }, [refresh, connectionCheckPath]); 1941 (0, import_react$4.useEffect)(() => { 1942 if (!hasAccounts) return; 1943 const onUpdated = () => handleReconnected(); 1944 document.addEventListener("email-account:updated", onUpdated); 1945 document.addEventListener("dispatch-event:smtp-imap-connection-updated", onUpdated); 1946 return () => { 1947 document.removeEventListener("email-account:updated", onUpdated); 1948 document.removeEventListener("dispatch-event:smtp-imap-connection-updated", onUpdated); 1949 }; 1950 }, [hasAccounts, handleReconnected]); 1951 function openSidepanel(accountId_1, accountEmail) { 1952 document.dispatchEvent(new CustomEvent("email-account:open-sidepanel", { detail: { 1953 accountId: accountId_1, 1954 accountEmail 1955 } })); 1956 } 1957 return /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)(import_jsx_runtime$5.Fragment, { children: [ 1958 emailAccounts.plainTextBanner && /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(SettingsAlert, { 1959 variant: "warning", 1960 icon: "exclamation-triangle", 1961 className: "mb-6", 1962 html: `<strong>${emailAccounts.plainTextBanner.title}</strong> ${emailAccounts.plainTextBanner.contentHtml}` 1963 }), 1964 /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("header", { 1965 className: "settings-box__header mb-3", 1966 children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("h3", { 1967 className: "text-lg-medium my-0", 1968 children: t$3("sender_accounts") 1969 }), /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("p", { 1970 className: "text-xs text-body-secondary", 1971 children: t$3("description") 1972 })] 1973 }), 1974 hasAccounts ? /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)(import_jsx_runtime$5.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("div", { 1975 className: "h-table mt-0 mb-2", 1976 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("div", { 1977 className: "h-table__scrollable", 1978 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("table", { children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("thead", { 1979 className: "h-table__heading", 1980 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("tr", { children: [ 1981 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("th", { 1982 className: "h-table__checkbox", 1983 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Checkbox, { 1984 ref: selectAllRef, 1985 "aria-label": t$3("select_all"), 1986 checked: allChecked, 1987 disabled: !emailAccounts.editable || selectable.length === 0, 1988 onChange: (event) => toggleAll(event.target.checked) 1989 }) 1990 }), 1991 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("th", { children: t$3("account") }), 1992 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("th", { 1993 className: "text-center", 1994 children: t$3("status") 1995 }), 1996 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("th", { 1997 className: "text-end", 1998 children: t$3("allocated") 1999 }), 2000 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("th", { 2001 className: "text-end", 2002 children: t$3("sent_last_day") 2003 }), 2004 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("th", {}) 2005 ] }) 2006 }), /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("tbody", { children: emailAccounts.accounts.map((account_4) => /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(EmailAccountRow, { 2007 connectPaths: emailAccounts.connectPaths, 2008 account: account_4, 2009 info: infoById?.[account_4.id], 2010 failed, 2011 loading: !infoById && !failed, 2012 allocating, 2013 selected: selectedIds.includes(account_4.id), 2014 rowEditable: !rowDisabled.get(account_4.id), 2015 freePlan: emailAccounts.freePlan, 2016 currentUserId, 2017 onToggle: (checked_1) => toggleAccount(account_4.id, checked_1), 2018 onRetry: retry, 2019 onOpenSidepanel: () => openSidepanel(account_4.id, account_4.sendEmailAs), 2020 onProtectInbox: (provider, email, providerLimit, paidUntil) => document.dispatchEvent(new CustomEvent("email-account:open-protect-modal", { detail: { 2021 accountId: account_4.id, 2022 email, 2023 provider, 2024 providerLimit, 2025 protectionPaidUntil: paidUntil 2026 } })), 2027 onUnlocked: refresh, 2028 onActivateInboxProtection: () => void activateInboxProtection(account_4.id), 2029 onRampUpProtect: refresh, 2030 onReconnected: handleReconnected 2031 }, account_4.id)) })] }) 2032 }) 2033 }
2033), emailAccounts.canAddAccount && /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Button, { 2034 variant: "default", 2035 asChild: true, 2036 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("a", { 2037 href: emailAccounts.addAccountPath, 2038 children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Icon, { 2039 icon: "plus", 2040 variant: "regular" 2041 }), t$3("add_account")] 2042 }) 2043 })] }) : /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("div", { children: [ 2044 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("strong", { 2045 className: "h-legend mb-0", 2046 children: t$3("no_accounts") 2047 }), 2048 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("p", { 2049 className: "settings-box__description", 2050 children: t$3("please_connect_account") 2051 }), 2052 /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("div", { 2053 className: "add-email-accounts-choices mt-4", 2054 "data-turbo": "true", 2055 children: [ 2056 /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("a", { 2057 className: "h-button", 2058 href: emailAccounts.connectPaths.gmail, 2059 "data-turbo-frame": "_top", 2060 children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("img", { 2061 src: google_icon_default, 2062 alt: "" 2063 }), i18n.t("js.sign_in_google")] 2064 }), 2065 /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("a", { 2066 className: "h-button", 2067 href: emailAccounts.connectPaths.outlook, 2068 "data-turbo-frame": "_top", 2069 children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("img", { 2070 src: microsoft_icon_default, 2071 alt: "" 2072 }), i18n.t("js.sign_in_microsoft")] 2073 }), 2074 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("hr", { className: "add-email-accounts-choices__separator" }), 2075 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Button, { 2076 asChild: true, 2077 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("a", { 2078 ref: smtpImapLinkRef, 2079 href: emailAccounts.connectPaths.smtpImap, 2080 "data-turbo-frame": "new_smtp_imap_connection", 2081 children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Icon, { 2082 icon: "plus", 2083 variant: "regular" 2084 }), t$3("connect_smtp_imap")] 2085 }) 2086 }), 2087 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("div", { 2088 className: "modal fade", 2089 id: "smtp-imap-modal", 2090 role: "dialog", 2091 tabIndex: -1, 2092 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("turbo-frame", { id: "new_smtp_imap_connection" }) 2093 }) 2094 ] 2095 }) 2096 ] }), 2097 /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("div", { 2098 className: "settings-toggle-card mt-8", 2099 children: [/* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(SettingToggle, { 2100 id: "settings-bcc", 2101 label: t$3("add_bcc"), 2102 hint: t$3("add_bcc_subtitle"), 2103 checked: bccEnabled, 2104 onCheckedChange: setBccEnabled 2105 }), /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)("div", { 2106 className: `form-group mt-4 mb-0${bccEnabled ? "" : " d-none"}`, 2107 id: "settings-bcc-email-group", 2108 children: [ 2109 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Label, { 2110 className: "h-label--block", 2111 htmlFor: "settings-bcc-email", 2112 children: t$3("send_a_copy_to") 2113 }), 2114 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Input, { 2115 ref: bccInputRef, 2116 id: "settings-bcc-email", 2117 name: "outreach_settings[bcc_recipient]", 2118 type: "email", 2119 className: "h-input--block", 2120 placeholder: t$3("bcc_placeholder"), 2121 value: bcc, 2122 disabled: !emailAccounts.editable, 2123 onChange: (event_0) => handleBccInput(event_0.target.value) 2124 }), 2125 /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)("div", { 2126 className: "h-hint mt-1", 2127 children: t$3("bcc_hint") 2128 }) 2129 ] 2130 })] 2131 }) 2132 ] }); 2133} 2134var import_react$4, import_jsx_runtime$5, t$3, SAVE_DEBOUNCE_MS, BCC_EMAIL_REGEX, isSubmittableBcc, sameIds; 2135function init_EmailAccountsSection() { 2136 return (init_EmailAccountsSection = __esmMin((() => { 2137 import_react$4 = require_react(); 2138 init_button(); 2139 init_checkbox(); 2140 init_icon(); 2141 init_input(); 2142 init_label(); 2143 init_routes(); 2144 init_SettingsAlert(); 2145 init_EmailAccountRow(); 2146 init_LaunchModal(); 2147 init_SettingToggle(); 2148 init_useAllocationStatus(); 2149 init_useEmailAccountsInfo(); 2150 init_useSettingsWriteQueueTeardown(); 2151 init_accountStatus(); 2152 init_settingsWriteQueue(); 2153 init_google_icon(); 2154 init_microsoft_icon(); 2155 import_jsx_runtime$5 = require_jsx_runtime(); 2156 t$3 = (key) => i18n.t(`js.sequences_campaign_settings.email_accounts.${key}`); 2157 SAVE_DEBOUNCE_MS = 500; 2158 BCC_EMAIL_REGEX = /^[a-z0-9_][a-z0-9._'%+-]{0,63}@(?:[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?\.){1,8}[a-z]{2,63}$/i; 2159 isSubmittableBcc = (value) => value.trim() === "" || BCC_EMAIL_REGEX.test(value); 2160 sameIds = (a, b) => a.length === b.length && a.every((id) => b.includes(id)); 2161 })))(); 2162} 2163//#endregion 2164//#region app/frontend/pages/SequencesCampaignSettings/TrackingSection.tsx
2165function TrackingSection(t0) { 2166 const $ = (0, import_compiler_runtime$3.c)(33); 2167 const { campaignId, tracking } = t0; 2168 const [tracked, setTracked] = (0, import_react$3.useState)(tracking.tracked); 2169 const [trackedLinks, setTrackedLinks] = (0, import_react$3.useState)(tracking.trackedLinks); 2170 let t1; 2171 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 2172 t1 = function syncFromProps(next, submitted) { 2173 setTracked((current) => current === submitted.tracked ? next.tracked : current); 2174 setTrackedLinks((current_0) => current_0 === submitted.trackedLinks ? next.trackedLinks : current_0); 2175 }; 2176 $[0] = t1; 2177 } else t1 = $[0]; 2178 const syncFromProps = t1; 2179 let t2; 2180 if ($[1] !== campaignId) { 2181 t2 = function save(next_0) { 2182 enqueueSettingsPut(sequence_path(campaignId), { campaign: { 2183 tracked: next_0.tracked, 2184 tracked_links: next_0.trackedLinks 2185 } }, { 2186 preserveScroll: true, 2187 preserveState: true, 2188 onSuccess: (page) => syncFromProps(unsafeCast(page.props).tracking, next_0) 2189 }, `tracking:${campaignId}`); 2190 }; 2191 $[1] = campaignId; 2192 $[2] = t2; 2193 } else t2 = $[2]; 2194 const save = t2; 2195 let t3; 2196 if ($[3] === Symbol.for("react.memo_cache_sentinel")) { 2197 t3 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("h3", { 2198 className: "text-lg-medium my-0", 2199 children: t$2("title") 2200 }); 2201 $[3] = t3; 2202 } else t3 = $[3]; 2203 let t4; 2204 if ($[4] === Symbol.for("react.memo_cache_sentinel")) { 2205 t4 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)("header", { 2206 className: "settings-box__header mb-3", 2207 children: [t3, /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("p", { 2208 className: "text-xs text-body-secondary", 2209 children: t$2("description") 2210 })] 2211 }); 2212 $[4] = t4; 2213 } else t4 = $[4]; 2214 let t5; 2215 let t6; 2216 if ($[5] === Symbol.for("react.memo_cache_sentinel")) { 2217 t5 = t$2("track_opens"); 2218 t6 = t$2("track_opens_hint"); 2219 $[5] = t5; 2220 $[6] = t6; 2221 } else { 2222 t5 = $[5]; 2223 t6 = $[6]; 2224 } 2225 const t7 = !tracking.canUpdateTracking; 2226 let t8; 2227 if ($[7] !== save || $[8] !== trackedLinks) { 2228 t8 = (checked) => { 2229 setTracked(checked); 2230 save({ 2231 tracked: checked, 2232 trackedLinks 2233 }); 2234 }; 2235 $[7] = save; 2236 $[8] = trackedLinks; 2237 $[9] = t8; 2238 } else t8 = $[9]; 2239 let t9; 2240 if ($[10] !== t7 || $[11] !== t8 || $[12] !== tracked || $[13] !== tracking.trackOpensTooltip) { 2241 t9 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(SettingToggle, { 2242 id: "settings-tracked", 2243 label: t5, 2244 hint: t6, 2245 checked: tracked, 2246 disabled: t7, 2247 disabledTooltip: tracking.trackOpensTooltip, 2248 onCheckedChange: t8 2249 }); 2250 $[10] = t7; 2251 $[11] = t8; 2252 $[12] = tracked; 2253 $[13] = tracking.trackOpensTooltip; 2254 $[14] = t9; 2255 } else t9 = $[14]; 2256 let t10; 2257 if ($[15] !== tracked || $[16] !== tracking.consentHtml) {
2258 t10 = tracked && /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("div", { 2259 className: "tracking-consent", 2260 children: /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(SettingsAlert, { 2261 icon: "info-circle", 2262 html: tracking.consentHtml 2263 }) 2264 }); 2265 $[15] = tracked; 2266 $[16] = tracking.consentHtml; 2267 $[17] = t10; 2268 } else t10 = $[17]; 2269 let t11; 2270 if ($[18] === Symbol.for("react.memo_cache_sentinel")) { 2271 t11 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(Separator, { className: "my-5" }); 2272 $[18] = t11; 2273 } else t11 = $[18]; 2274 let t12; 2275 let t13; 2276 if ($[19] === Symbol.for("react.memo_cache_sentinel")) { 2277 t12 = t$2("track_links"); 2278 t13 = t$2("track_links_hint"); 2279 $[19] = t12; 2280 $[20] = t13; 2281 } else { 2282 t12 = $[19]; 2283 t13 = $[20]; 2284 } 2285 const t14 = !tracking.allowTrackLinks; 2286 let t15; 2287 if ($[21] !== save || $[22] !== tracked) { 2288 t15 = (checked_0) => { 2289 setTrackedLinks(checked_0); 2290 save({ 2291 tracked, 2292 trackedLinks: checked_0 2293 }); 2294 }; 2295 $[21] = save; 2296 $[22] = tracked; 2297 $[23] = t15; 2298 } else t15 = $[23]; 2299 let t16; 2300 if ($[24] !== t14 || $[25] !== t15 || $[26] !== trackedLinks || $[27] !== tracking.trackLinksTooltip) { 2301 t16 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(SettingToggle, { 2302 id: "settings-tracked-links", 2303 label: t12, 2304 hint: t13, 2305 checked: trackedLinks, 2306 disabled: t14, 2307 disabledTooltip: tracking.trackLinksTooltip, 2308 onCheckedChange: t15 2309 }); 2310 $[24] = t14; 2311 $[25] = t15; 2312 $[26] = trackedLinks; 2313 $[27] = tracking.trackLinksTooltip; 2314 $[28] = t16; 2315 } else t16 = $[28]; 2316 let t17; 2317 if ($[29] !== t10 || $[30] !== t16 || $[31] !== t9) { 2318 t17 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)("div", { 2319 className: "settings-box", 2320 children: [t4, /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)("div", { 2321 className: "settings-toggle-card", 2322 children: [ 2323 t9, 2324 t10, 2325 t11, 2326 t16 2327 ] 2328 })] 2329 }); 2330 $[29] = t10; 2331 $[30] = t16; 2332 $[31] = t9; 2333 $[32] = t17; 2334 } else t17 = $[32]; 2335 return t17; 2336} 2337var import_compiler_runtime$3, import_react$3, import_jsx_runtime$4, t$2; 2338function init_TrackingSection() { 2339 return (init_TrackingSection = __esmMin((() => { 2340 import_compiler_runtime$3 = require_compiler_runtime(); 2341 import_react$3 = require_react(); 2342 init_separator(); 2343 init_routes(); 2344 init_SettingsAlert(); 2345 init_SettingToggle(); 2346 init_settingsWriteQueue(); 2347 import_jsx_runtime$4 = require_jsx_runtime(); 2348 t$2 = (key) => i18n.t(`js.sequences_campaign_settings.tracking.${key}`); 2349 })))(); 2350} 2351//#endregion 2352//#region app/frontend/pages/SequencesCampaignSettings/SendingWindowSection.tsx 2353function StartDateField({ value, earliestDate, disabled, onChange }) { 2354 const inputRef = (0, import_react$2.useRef)(null); 2355 const pickerRef = (0, import_react$2.useRef)(null); 2356 const onChangeRef = (0, import_react$2.useRef)(onChange); 2357 onChangeRef.current = onChange; 2358 (0, import_react$2.useEffect)(() => { 2359 const element = inputRef.current; 2360 if (!element) return; 2361 const picker = new Datepicker(element, { 2362 format: "yyyy-mm-dd", 2363 minDate: earliestDate, 2364 pickLevel: 0, 2365 prevArrow: "â¹", 2366 nextArrow: "âº", 2367 autohide: true, 2368 todayHighlight: true 2369 }); 2370 pickerRef.current = picker; 2371 const handleChange = () => onChangeRef.current(element.value); 2372 element.addEventListener("changeDate", handleChange); 2373 element.addEventListener("change", handleChange); 2374 return () => { 2375 element.removeEventListener("changeDate", handleChange); 2376 element.removeEventListener("change", handleChange); 2377 picker.destroy(); 2378 pickerRef.current = null; 2379 }; 2380 }, [earliestDate]); 2381 (0, import_react$2.useEffect)(() => { 2382 const element_0 = inputRef.current; 2383 if (element_0 && value !== element_0.value) element_0.value = value; 2384 }, [value]); 2385 return /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("div", { 2386 className: "h-input-group schedule-startdate", 2387 children: [/* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("span", { 2388 className: "h-input-group__text", 2389 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Icon, { 2390 icon: "calendar", 2391 variant: "regular" 2392 }) 2393 }), /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Input, { 2394 ref: inputRef, 2395 id: "outreach_settings_start_at", 2396 name: "outreach_settings[start_at]", 2397 defaultValue: value, 2398 onChange: (event) =>
2398 onChangeRef.current(event.target.value), 2399 disabled, 2400 readOnly: disabled 2401 })] 2402 }); 2403} 2404function defaultStartDate(now = /* @__PURE__ */ new Date()) { 2405 const month = String(now.getMonth() + 1).padStart(2, "0"); 2406 const day = String(now.getDate()).padStart(2, "0"); 2407 const viewerToday = `${now.getFullYear()}-${month}-${day}`; 2408 const utcToday = now.toISOString().slice(0, 10); 2409 return viewerToday > utcToday ? viewerToday : utcToday; 2410} 2411function SendingWindowSection(t0) { 2412 const $ = (0, import_compiler_runtime$2.c)(104); 2413 const { campaignId, sendingWindow, isDrip, canUpdate } = t0; 2414 const [scheduleDays, setScheduleDays] = (0, import_react$2.useState)(sendingWindow.scheduleDays); 2415 const [timeStart, setTimeStart] = (0, import_react$2.useState)(sendingWindow.scheduleTimeStart); 2416 const [timeEnd, setTimeEnd] = (0, import_react$2.useState)(sendingWindow.scheduleTimeEnd); 2417 const [hasStartDate, setHasStartDate] = (0, import_react$2.useState)(Boolean(sendingWindow.startAt)); 2418 const [startAt, setStartAt] = (0, import_react$2.useState)(sendingWindow.startAt ?? ""); 2419 const [processing, setProcessing] = (0, import_react$2.useState)(false); 2420 const [timeZoneHelpOpen, setTimeZoneHelpOpen] = (0, import_react$2.useState)(false); 2421 const timeZoneHelpCloseTimer = (0, import_react$2.useRef)(null); 2422 let t1; 2423 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 2424 t1 = function openTimeZoneHelp() { 2425 if (timeZoneHelpCloseTimer.current) clearTimeout(timeZoneHelpCloseTimer.current); 2426 setTimeZoneHelpOpen(true); 2427 }; 2428 $[0] = t1; 2429 } else t1 = $[0]; 2430 const openTimeZoneHelp = t1; 2431 let t2; 2432 if ($[1] === Symbol.for("react.memo_cache_sentinel")) { 2433 t2 = function closeTimeZoneHelp() { 2434 timeZoneHelpCloseTimer.current = setTimeout(() => setTimeZoneHelpOpen(false), 100); 2435 }; 2436 $[1] = t2; 2437 } else t2 = $[1]; 2438 const closeTimeZoneHelp = t2; 2439 let t3; 2440 if ($[2] === Symbol.for("react.memo_cache_sentinel")) { 2441 t3 = function toggleDay(day) { 2442 setScheduleDays((days) => days.includes(day) ? days.filter((value) => value !== day) : [...days, day].sort(_temp)); 2443 }; 2444 $[2] = t3; 2445 } else t3 = $[2]; 2446 const toggleDay = t3; 2447 let t4; 2448 if ($[3] !== startAt) { 2449 t4 = function toggleStartDate(checked) { 2450 setHasStartDate(checked); 2451 if (checked) { 2452 if (!startAt) setStartAt(defaultStartDate()); 2453 } else setStartAt(""); 2454 }; 2455 $[3] = startAt; 2456 $[4] = t4; 2457 } else t4 = $[4]; 2458 const toggleStartDate = t4; 2459 let t5; 2460 if ($[5] === Symbol.for("react.memo_cache_sentinel")) { 2461 t5 = function syncFromProps(next, submitted) { 2462 setScheduleDays((current) => sameDays(current, submitted.scheduleDays) ? next.scheduleDays : current); 2463 setTimeStart((current_0) => current_0 === submitted.timeStart ? next.scheduleTimeStart : current_0); 2464 setTimeEnd((current_1) => current_1 === submitted.timeEnd ? next.scheduleTimeEnd : current_1); 2465 setStartAt((current_2) => { 2466 if (current_2 !== submitted.startAt) return current_2; 2467 setHasStartDate(Boolean(next.startAt)); 2468 return next.startAt ?? ""; 2469 }); 2470 }; 2471 $[5] = t5; 2472 } else t5 = $[5]; 2473 const syncFromProps = t5; 2474 let t6; 2475 if ($[6] !== campaignId || $[7] !== hasStartDate || $[8] !== scheduleDays || $[9] !== startAt || $[10] !== timeEnd || $[11] !== timeStart) { 2476 t6 = function handleSubmit(event) { 2477 event.preventDefault(); 2478 setProcessing(true); 2479 const submitted_0 = { 2480 scheduleDays: [...scheduleDays], 2481 timeStart, 2482 timeEnd, 2483 startAt: hasStartDate ? startAt : "" 2484 }; 2485 enqueueSettingsPut(sequence_settings_path(campaignId), { outreach_settings: { 2486 schedule_days: scheduleDays, 2487 schedule_time_start: timeStart, 2488 schedule_time_end: timeEnd, 2489 start_at: hasStartDate ? startAt : "" 2490 } }, { 2491 preserveScroll: true, 2492 preserveState: true, 2493 onSuccess: (page) => syncFromProps(unsafeCast(page.props).sendingWindow, submitted_0), 2494 onFinish: () => setProcessing(false) 2495 }); 2496 }; 2497 $[6] = campaignId; 2498 $[7] = hasStartDate; 2499 $[8] = scheduleDays; 2500 $[9] = startAt; 2501 $[10] = timeEnd; 2502 $[11] = timeStart; 2503 $[12] = t6; 2504 } else t6 = $[12]; 2505 const handleSubmit = t6; 2506 let t7; 2507 if ($[13] === Symbol.for("react.memo_cache_sentinel")) { 2508 t7 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("h3", { 2509 className: "text-lg-medium my-0", 2510 children: t$1("title") 2511 }); 2512 $[13] = t7; 2513 } else t7 = $[13]; 2514 let t8; 2515 if ($[14] === Symbol.for("react.memo_cache_sentinel")) { 2516 t8 = t$1("start_date_description"); 2517 $[14] = t8; 2518 } else t8 = $[14]; 2519 let t9; 2520 if ($[15] !== sendingWindow.startOfNextSendingText) { 2521 t9 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("header", { 2522 className: "settings-box__header mb-3",
2523 children: [t7, /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("p", { 2524 className: "text-xs text-body-secondary", 2525 children: [ 2526 t8, 2527 " ", 2528 sendingWindow.startOfNextSendingText 2529 ] 2530 })] 2531 }); 2532 $[15] = sendingWindow.startOfNextSendingText; 2533 $[16] = t9; 2534 } else t9 = $[16]; 2535 let t10; 2536 if ($[17] !== sendingWindow.noTimeZoneHtml) { 2537 t10 = sendingWindow.noTimeZoneHtml && /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(SettingsAlert, { 2538 icon: "info-circle", 2539 className: "mb-6", 2540 html: sendingWindow.noTimeZoneHtml 2541 }); 2542 $[17] = sendingWindow.noTimeZoneHtml; 2543 $[18] = t10; 2544 } else t10 = $[18]; 2545 let t11; 2546 if ($[19] !== isDrip || $[20] !== sendingWindow.followUpAlert) { 2547 t11 = isDrip && sendingWindow.followUpAlert && /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(SettingsAlert, { 2548 icon: "info-circle", 2549 className: "mb-6", 2550 html: sendingWindow.followUpAlert 2551 }); 2552 $[19] = isDrip; 2553 $[20] = sendingWindow.followUpAlert; 2554 $[21] = t11; 2555 } else t11 = $[21]; 2556 let t12; 2557 if ($[22] === Symbol.for("react.memo_cache_sentinel")) { 2558 t12 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("legend", { 2559 className: "h-legend h-legend--sm mb-2", 2560 children: t$1("days_label") 2561 }); 2562 $[22] = t12; 2563 } else t12 = $[22]; 2564 let t13; 2565 if ($[23] !== canUpdate || $[24] !== scheduleDays || $[25] !== sendingWindow.dayOptions) { 2566 let t14; 2567 if ($[27] !== canUpdate || $[28] !== scheduleDays) { 2568 t14 = (day_0) => /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("label", { 2569 className: "schedule-day", 2570 children: [/* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Checkbox, { 2571 id: `schedule-day-${day_0.value}-field`, 2572 name: "outreach_settings[schedule_days][]", 2573 value: day_0.value, 2574 disabled: !canUpdate, 2575 checked: scheduleDays.includes(day_0.value), 2576 onChange: () => toggleDay(day_0.value) 2577 }), /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("span", { 2578 className: "schedule-day__name text-xs", 2579 children: day_0.label 2580 })] 2581 }, day_0.value); 2582 $[27] = canUpdate; 2583 $[28] = scheduleDays; 2584 $[29] = t14; 2585 } else t14 = $[29]; 2586 t13 = sendingWindow.dayOptions.map(t14); 2587 $[23] = canUpdate; 2588 $[24] = scheduleDays; 2589 $[25] = sendingWindow.dayOptions; 2590 $[26] = t13; 2591 } else t13 = $[26]; 2592 let t14; 2593 if ($[30] !== t13) { 2594 t14 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("fieldset", { 2595 className: "h-fieldset", 2596 children: [t12, /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("div", { 2597 className: "schedule-days", 2598 children: t13 2599 })] 2600 }); 2601 $[30] = t13; 2602 $[31] = t14; 2603 } else t14 = $[31]; 2604 let t15; 2605 if ($[32] === Symbol.for("react.memo_cache_sentinel")) { 2606 t15 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("legend", { 2607 className: "h-legend h-legend--sm mb-2", 2608 children: t$1("hours_label") 2609 }); 2610 $[32] = t15; 2611 } else t15 = $[32]; 2612 const t16 = !canUpdate; 2613 let t17; 2614 if ($[33] === Symbol.for("react.memo_cache_sentinel")) { 2615 t17 = (event_0) => setTimeStart(Number(event_0.target.value)); 2616 $[33] = t17; 2617 } else t17 = $[33]; 2618 let t18; 2619 if ($[34] !== sendingWindow.startOptions || $[35] !== timeEnd) { 2620 let t19; 2621 if ($[37] !== timeEnd) { 2622 t19 = (t20) => { 2623 const [label, value_0] = t20; 2624 return /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("option", { 2625 value: value_0, 2626 disabled: value_0 > timeEnd, 2627 children: label 2628 }, value_0); 2629 }; 2630 $[37] = timeEnd; 2631 $[38] = t19; 2632 } else t19 = $[38]; 2633 t18 = sendingWindow.startOptions.map(t19); 2634 $[34] = sendingWindow.startOptions; 2635 $[35] = timeEnd; 2636 $[36] = t18; 2637 } else t18 = $[36]; 2638 let t19; 2639 if ($[39] !== t16 || $[40] !== t18 || $[41] !== timeStart) { 2640 t19 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("select", { 2641 className: "h-select", 2642 id: "outreach_settings_schedule_time_start", 2643 name: "outreach_settings[schedule_time_start]", 2644 disabled: t16, 2645 value: timeStart, 2646 onChange: t17, 2647 children: t18 2648 }); 2649 $[39] = t16; 2650 $[40] = t18; 2651 $[41] = timeStart; 2652 $[42] = t19; 2653 } else t19 = $[42]; 2654 let t20; 2655 if ($[43] === Symbol.for("react.memo_cache_sentinel")) { 2656 t20 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("span", { 2657 className: "text-sm text-body-secondary", 2658 children: t$1("to") 2659 }); 2660 $[43] = t20; 2661 } else t20 = $[43]; 2662 const t21 = !canUpdate; 2663 let t22; 2664 if ($[44] === Symbol.for("react.memo_cache_sentinel")) { 2665 t22 = (event_1) => setTimeEnd(Number(event_1.target.value)); 2666 $[44] = t22; 2667 } else t22 = $[44]; 2668 let t23; 2669 if ($[45] !== sendingWindow.endOptions || $[46] !== timeStart) { 2670 let t24; 2671 if ($[48] !== timeStart) { 2672 t24 = (t25) => { 2673 const [label_0, value_1] = t25; 2674 return /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("option", { 2675 value: value_1, 2676 disabled: value_1 < timeStart, 2677 children: label_0 2678 }, value_1); 2679 }; 2680 $[48] = timeStart; 2681 $[49] = t24; 2682 } else t24 = $[49]; 2683 t23 = sendingWindow.endOptions.map(t24); 2684 $[45] = sendingWindow.endOptions; 2685 $[46] = timeStart; 2686 $[47] = t23; 2687 } else t23 = $[47]; 2688 let t24; 2689 if ($[50] !== t21 || $[51] !== t23 || $[52] !== timeEnd) { 2690 t24 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("select", { 2691 className: "h-select", 2692 id: "outreach_settings_schedule_time_end", 2693 name: "outreach_settings[schedule_time_end]", 2694 disabled: t21, 2695 value: timeEnd, 2696 onChange: t22, 2697 children: t23 2698 }); 2699 $[50] = t21; 2700 $[51] = t23; 2701 $[52] = timeEnd; 2702 $[53] = t24; 2703 } else t24 = $[53]; 2704 let t25; 2705 if ($[54] === Symbol.for("react.memo_cache_sentinel")) { 2706 t25 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("span", { 2707 className: "text-xs text-body-secondary", 2708 children: t$1("timezone_prefix") 2709 }); 2710 $[54] = t25; 2711 } else t25 = $[54]; 2712 let t26; 2713 if ($[55] !== sendingWindow.timeZoneLabel) { 2714 t26 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("span", { 2715 className: "text-xs-medium", 2716 children: sendingWindow.timeZoneLabel 2717 }); 2718 $[55] = sendingWindow.timeZoneLabel; 2719 $[56] = t26; 2720 } else t26 = $[56]; 2721 let t27; 2722 if ($[57] !== sendingWindow.currentlyLabel) { 2723 t27 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("span", { 2724 className: "text-xs text-body-secondary", 2725 children: sendingWindow.currentlyLabel 2726 }); 2727 $[57] = sendingWindow.currentlyLabel; 2728 $[58] = t27; 2729 } else t27 = $[58]; 2730 let t28; 2731 if ($[59] === Symbol.for("react.memo_cache_sentinel")) { 2732 t28 = t$1("change_time_zone"); 2733 $[59] = t28; 2734 } else t28 = $[59]; 2735 let t29; 2736 if ($[60] === Symbol.for("react.memo_cache_sentinel")) { 2737 t29 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(PopoverTrigger, { 2738 asChild: true, 2739 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("button", { 2740 type: "button", 2741 className: "question-circle", 2742 "aria-label": t28, 2743 onMouseEnter: openTimeZoneHelp, 2744 onMouseLeave: closeTimeZoneHelp, 2745 onClick: _temp2, 2746 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Icon, { 2747 icon: "question-circle", 2748 variant: "regular", 2749 size: "sm" 2750 }) 2751 }) 2752 }); 2753 $[60] = t29; 2754 } else t29 = $[60]; 2755 let t30; 2756 if ($[61] === Symbol.for("react.memo_cache_sentinel")) { 2757 t30 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("p", { 2758 className: "black", 2759 children: t$1("popover") 2760 }); 2761 $[61] = t30; 2762 } else t30 = $[61]; 2763 const t31 = sendingWindow.changeTimeZonePath; 2764 let t32; 2765 if ($[62] === Symbol.for("react.memo_cache_sentinel")) { 2766 t32 = t$1("change_time_zone"); 2767 $[62] = t32; 2768 } else t32 = $[62]; 2769 let t33; 2770 if ($[63] !== sendingWindow.changeTimeZonePath) { 2771 t33 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(PopoverContent, { 2772 side: "top", 2773 className: "p-3", 2774 onMouseEnter: openTimeZoneHelp, 2775 onMouseLeave: closeTimeZoneHelp,
2776 children: [t30, /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Button, { 2777 size: "sm", 2778 asChild: true, 2779 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("a", { 2780 href: t31, 2781 children: t32 2782 }) 2783 })] 2784 }); 2785 $[63] = sendingWindow.changeTimeZonePath; 2786 $[64] = t33; 2787 } else t33 = $[64]; 2788 let t34; 2789 if ($[65] !== t33 || $[66] !== timeZoneHelpOpen) { 2790 t34 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(Popover, { 2791 open: timeZoneHelpOpen, 2792 onOpenChange: setTimeZoneHelpOpen, 2793 children: [t29, t33] 2794 }); 2795 $[65] = t33; 2796 $[66] = timeZoneHelpOpen; 2797 $[67] = t34; 2798 } else t34 = $[67]; 2799 let t35; 2800 if ($[68] !== t26 || $[69] !== t27 || $[70] !== t34) { 2801 t35 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("div", { 2802 className: "schedule-time__zone", 2803 children: [ 2804 t25, 2805 t26, 2806 t27, 2807 t34 2808 ] 2809 }); 2810 $[68] = t26; 2811 $[69] = t27; 2812 $[70] = t34; 2813 $[71] = t35; 2814 } else t35 = $[71]; 2815 let t36; 2816 if ($[72] !== t19 || $[73] !== t24 || $[74] !== t35) { 2817 t36 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("fieldset", { 2818 className: "h-fieldset mt-4",
2819 children: [t15, /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("div", { 2820 className: "schedule-time", 2821 children: [ 2822 t19, 2823 t20, 2824 t24, 2825 t35 2826 ] 2827 })] 2828 }); 2829 $[72] = t19; 2830 $[73] = t24; 2831 $[74] = t35; 2832 $[75] = t36; 2833 } else t36 = $[75]; 2834 let t37; 2835 let t38; 2836 if ($[76] === Symbol.for("react.memo_cache_sentinel")) { 2837 t37 = t$1("custom_schedule_label"); 2838 t38 = t$1("custom_schedule_hint"); 2839 $[76] = t37; 2840 $[77] = t38; 2841 } else { 2842 t37 = $[76]; 2843 t38 = $[77]; 2844 } 2845 let t39; 2846 if ($[78] !== hasStartDate || $[79] !== toggleStartDate) { 2847 t39 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(SettingToggle, { 2848 id: "schedule-has-specific-startdate", 2849 label: t37, 2850 hint: t38, 2851 checked: hasStartDate, 2852 onCheckedChange: toggleStartDate 2853 }); 2854 $[78] = hasStartDate; 2855 $[79] = toggleStartDate; 2856 $[80] = t39; 2857 } else t39 = $[80]; 2858 const t40 = !hasStartDate; 2859 let t41; 2860 if ($[81] === Symbol.for("react.memo_cache_sentinel")) { 2861 t41 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Label, { 2862 className: "h-label--block", 2863 htmlFor: "outreach_settings_start_at", 2864 children: t$1("custom_schedule_field_label") 2865 }); 2866 $[81] = t41; 2867 } else t41 = $[81]; 2868 const t42 = !canUpdate; 2869 let t43; 2870 if ($[82] !== sendingWindow.earliestDate || $[83] !== startAt || $[84] !== t42) { 2871 t43 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(StartDateField, { 2872 value: startAt, 2873 earliestDate: sendingWindow.earliestDate, 2874 disabled: t42, 2875 onChange: setStartAt 2876 }); 2877 $[82] = sendingWindow.earliestDate; 2878 $[83] = startAt; 2879 $[84] = t42; 2880 $[85] = t43; 2881 } else t43 = $[85]; 2882 let t44; 2883 if ($[86] !== t40 || $[87] !== t43) { 2884 t44 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("div", { 2885 className: "mt-4", 2886 hidden: t40, 2887 children: [t41, t43] 2888 }); 2889 $[86] = t40; 2890 $[87] = t43; 2891 $[88] = t44; 2892 } else t44 = $[88]; 2893 let t45; 2894 if ($[89] !== t39 || $[90] !== t44) { 2895 t45 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("div", { 2896 className: "settings-toggle-card mt-6", 2897 children: [t39, t44] 2898 }); 2899 $[89] = t39; 2900 $[90] = t44; 2901 $[91] = t45; 2902 } else t45 = $[91]; 2903 const t46 = !canUpdate || processing; 2904 let t47; 2905 if ($[92] === Symbol.for("react.memo_cache_sentinel")) { 2906 t47 = t$1("save_schedule"); 2907 $[92] = t47; 2908 } else t47 = $[92]; 2909 let t48; 2910 if ($[93] !== t46) { 2911 t48 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("div", { 2912 className: "mt-6", 2913 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Button, { 2914 type: "submit", 2915 variant: "primary", 2916 disabled: t46, 2917 children: t47 2918 }) 2919 }); 2920 $[93] = t46; 2921 $[94] = t48; 2922 } else t48 = $[94]; 2923 let t49; 2924 if ($[95] !== handleSubmit || $[96] !== t10 || $[97] !== t11 || $[98] !== t14 || $[99] !== t36 || $[100] !== t45 || $[101] !== t48 || $[102] !== t9) { 2925 t49 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("form", { 2926 onSubmit: handleSubmit, 2927 "data-turbo": "false", 2928 children: [ 2929 t9, 2930 t10, 2931 t11, 2932 t14, 2933 t36, 2934 t45, 2935 t48 2936 ] 2937 }); 2938 $[95] = handleSubmit; 2939 $[96] = t10; 2940 $[97] = t11; 2941 $[98] = t14; 2942 $[99] = t36; 2943 $[100] = t45; 2944 $[101] = t48; 2945 $[102] = t9; 2946 $[103] = t49; 2947 } else t49 = $[103]; 2948 return t49; 2949} 2950function _temp2(event_2) { 2951 return event_2.preventDefault(); 2952} 2953function _temp(a, b) { 2954 return a - b; 2955} 2956var import_compiler_runtime$2, import_react$2, import_jsx_runtime$3, t$1, sameDays; 2957function init_SendingWindowSection() { 2958 return (init_SendingWindowSection = __esmMin((() => { 2959 import_compiler_runtime$2 = require_compiler_runtime(); 2960 import_react$2 = require_react(); 2961 init_main(); 2962 init_button(); 2963 init_checkbox(); 2964 init_icon(); 2965 init_input(); 2966 init_label(); 2967 init_popover(); 2968 init_routes(); 2969 init_SettingsAlert(); 2970 init_SettingToggle(); 2971 init_settingsWriteQueue(); 2972 import_jsx_runtime$3 = require_jsx_runtime(); 2973 t$1 = (key) => i18n.t(`js.sequences_campaign_settings.sending_window.${key}`); 2974 sameDays = (a, b) => a.length === b.length && [...a].sort().every((day, index) => day === [...b].sort()[index]); 2975 })))(); 2976} 2977//#endregion 2978//#region app/frontend/pages/SequencesCampaignSettings/UnsubscribeSection.tsx
2979function UnsubscribeSection(t0) { 2980 const $ = (0, import_compiler_runtime$1.c)(44); 2981 const { campaignId, unsubscribe, canUpdate } = t0; 2982 const [addLink, setAddLink] = (0, import_react$1.useState)(unsubscribe.addUnsubscribeLink); 2983 const [sentence, setSentence] = (0, import_react$1.useState)(unsubscribe.unsubscribeSentence); 2984 const [plainText, setPlainText] = (0, import_react$1.useState)(unsubscribe.unsubscribeSentencePlainText ?? ""); 2985 const [processing, setProcessing] = (0, import_react$1.useState)(false); 2986 let t1; 2987 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 2988 t1 = function syncFromProps(next, submitted) { 2989 setSentence((current) => current === submitted.sentence ? next.unsubscribeSentence : current); 2990 setPlainText((current_0) => current_0 === submitted.plainText ? next.unsubscribeSentencePlainText ?? "" : current_0); 2991 }; 2992 $[0] = t1; 2993 } else t1 = $[0]; 2994 const syncFromProps = t1; 2995 let t2; 2996 if ($[1] !== campaignId) { 2997 t2 = function toggleLink(checked) { 2998 setAddLink(checked); 2999 enqueueSettingsPut(sequence_path(campaignId), { campaign: { add_unsubscribe_link: checked } }, { 3000 preserveScroll: true, 3001 preserveState: true, 3002 onSuccess: (page) => setAddLink(unsafeCast(page.props).unsubscribe.addUnsubscribeLink) 3003 }, `unsubscribe-link:${campaignId}`); 3004 }; 3005 $[1] = campaignId; 3006 $[2] = t2; 3007 } else t2 = $[2]; 3008 const toggleLink = t2; 3009 let t3; 3010 if ($[3] !== campaignId || $[4] !== plainText || $[5] !== sentence || $[6] !== unsubscribe.showPlainText) { 3011 t3 = function handleSubmit(event) { 3012 event.preventDefault(); 3013 setProcessing(true); 3014 const submitted_0 = { 3015 sentence, 3016 plainText 3017 }; 3018 enqueueSettingsPut(sequence_settings_path(campaignId), { outreach_settings: { 3019 unsubscribe_sentence: sentence, 3020 ...unsubscribe.showPlainText && { unsubscribe_sentence_plain_text: plainText } 3021 } }, { 3022 preserveScroll: true, 3023 preserveState: true, 3024 onSuccess: (page_0) => syncFromProps(unsafeCast(page_0.props).unsubscribe, submitted_0), 3025 onFinish: () => setProcessing(false) 3026 }); 3027 }; 3028 $[3] = campaignId; 3029 $[4] = plainText; 3030 $[5] = sentence; 3031 $[6] = unsubscribe.showPlainText; 3032 $[7] = t3; 3033 } else t3 = $[7]; 3034 const handleSubmit = t3; 3035 let t4; 3036 if ($[8] === Symbol.for("react.memo_cache_sentinel")) { 3037 t4 = t("hint"); 3038 $[8] = t4; 3039 } else t4 = $[8]; 3040 let t5; 3041 if ($[9] === Symbol.for("react.memo_cache_sentinel")) { 3042 t5 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("i", { children: "{{link}}" }); 3043 $[9] = t5; 3044 } else t5 = $[9]; 3045 let t6; 3046 if ($[10] === Symbol.for("react.memo_cache_sentinel")) { 3047 t6 = i18n.t("js.and"); 3048 $[10] = t6; 3049 } else t6 = $[10]; 3050 let t7; 3051 if ($[11] === Symbol.for("react.memo_cache_sentinel")) { 3052 t7 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("i", { children: "{{/link}}" }); 3053 $[11] = t7; 3054 } else t7 = $[11]; 3055 let t8; 3056 if ($[12] === Symbol.for("react.memo_cache_sentinel")) { 3057 t8 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 3058 className: "h-hint mt-1", 3059 children: [ 3060 t4, 3061 " ", 3062 t5, 3063 " ", 3064 t6, 3065 " ", 3066 t7, 3067 " ", 3068 i18n.t("js.sequences_campaign_settings.actions.tags") 3069 ] 3070 }); 3071 $[12] = t8; 3072 } else t8 = $[12]; 3073 const linkTagHint = t8; 3074 let t9; 3075 if ($[13] === Symbol.for("react.memo_cache_sentinel")) { 3076 t9 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("h3", { 3077 className: "text-lg-medium my-0", 3078 children: t("title") 3079 }); 3080 $[13] = t9; 3081 } else t9 = $[13]; 3082 let t10; 3083 if ($[14] === Symbol.for("react.memo_cache_sentinel")) { 3084 t10 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("header", { 3085 className: "settings-box__header mb-3", 3086 children: [t9, /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("p", { 3087 className: "text-xs text-body-secondary", 3088 children: t("subtitle") 3089 })] 3090 }); 3091 $[14] = t10; 3092 } else t10 = $[14]; 3093 let t11; 3094 let t12; 3095 if ($[15] === Symbol.for("react.memo_cache_sentinel")) { 3096 t11 = t("label"); 3097 t12 = t("subtitle_toggle"); 3098 $[15] = t11; 3099 $[16] = t12; 3100 } else { 3101 t11 = $[15]; 3102 t12 = $[16]; 3103 } 3104 const t13 = !unsubscribe.canToggle; 3105 let t14; 3106 if ($[17] !== addLink || $[18] !== t13 || $[19] !== toggleLink || $[20] !== unsubscribe.disabledTooltip) { 3107 t14 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SettingToggle, { 3108 id: "settings-unsubscribe-link", 3109 label: t11, 3110 hint: t12, 3111 checked: addLink, 3112 disabled: t13, 3113 disabledTooltip: unsubscribe.disabledTooltip, 3114 onCheckedChange: toggleLink 3115 }); 3116 $[17] = addLink; 3117 $[18] = t13; 3118 $[19] = toggleLink; 3119 $[20] = unsubscribe.disabledTooltip; 3120 $[21] = t14; 3121 } else t14 = $[21]; 3122 const t15 = addLink ? void 0 : "none"; 3123 let t16; 3124 if ($[22] !== t15) { 3125 t16 = { display: t15 }; 3126 $[22] = t15; 3127 $[23] = t16; 3128 } else t16 = $[23]; 3129 let t17; 3130 if ($[24] !== canUpdate || $[25] !== plainText || $[26] !== sentence || $[27] !== unsubscribe.placeholder || $[28] !== unsubscribe.placeholderPlainText || $[29] !== unsubscribe.showPlainText) {
3131 t17 = unsubscribe.showPlainText ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [ 3132 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Label, { 3133 className: "h-label--block", 3134 children: t("label_plain_text") 3135 }), 3136 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("div", { 3137 className: "h-hint mb-2", 3138 children: t("hint_plain_text") 3139 }), 3140 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Input, { 3141 className: "h-input--block", 3142 id: "outreach_settings_unsubscribe_sentence_plain_text", 3143 name: "outreach_settings[unsubscribe_sentence_plain_text]", 3144 disabled: !canUpdate, 3145 placeholder: unsubscribe.placeholderPlainText, 3146 value: plainText, 3147 onChange: (event_0) => setPlainText(event_0.target.value) 3148 }), 3149 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 3150 className: "mt-6", 3151 children: [ 3152 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Label, { 3153 className: "h-label--block", 3154 children: t("label_html") 3155 }), 3156 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Input, { 3157 id: "outreach_settings_unsubscribe_sentence", 3158 name: "outreach_settings[unsubscribe_sentence]", 3159 placeholder: unsubscribe.placeholder, 3160 value: sentence, 3161 onChange: (event_1) => setSentence(event_1.target.value) 3162 }), 3163 linkTagHint 3164 ] 3165 }) 3166 ] }) : /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [ 3167 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Label, { 3168 className: "h-label--block", 3169 children: t("label_sentence") 3170 }), 3171 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Input, { 3172 id: "outreach_settings_unsubscribe_sentence", 3173 name: "outreach_settings[unsubscribe_sentence]", 3174 placeholder: unsubscribe.placeholder, 3175 value: sentence, 3176 onChange: (event_2) => setSentence(event_2.target.value) 3177 }), 3178 linkTagHint 3179 ] }); 3180 $[24] = canUpdate; 3181 $[25] = plainText; 3182 $[26] = sentence; 3183 $[27] = unsubscribe.placeholder; 3184 $[28] = unsubscribe.placeholderPlainText; 3185 $[29] = unsubscribe.showPlainText; 3186 $[30] = t17; 3187 } else t17 = $[30]; 3188 let t18; 3189 if ($[31] !== t16 || $[32] !== t17) { 3190 t18 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("div", { 3191 className: "mt-4", 3192 style: t16, 3193 children: t17 3194 }); 3195 $[31] = t16; 3196 $[32] = t17; 3197 $[33] = t18; 3198 } else t18 = $[33]; 3199 let t19; 3200 if ($[34] !== t14 || $[35] !== t18) { 3201 t19 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 3202 className: "settings-toggle-card", 3203 children: [t14, t18] 3204 }); 3205 $[34] = t14; 3206 $[35] = t18; 3207 $[36] = t19; 3208 } else t19 = $[36]; 3209 const t20 = !canUpdate || processing; 3210 let t21; 3211 if ($[37] === Symbol.for("react.memo_cache_sentinel")) { 3212 t21 = t("save"); 3213 $[37] = t21; 3214 } else t21 = $[37]; 3215 let t22; 3216 if ($[38] !== t20) { 3217 t22 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("div", { 3218 className: "mt-6", 3219 children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Button, { 3220 type: "submit", 3221 variant: "primary", 3222 disabled: t20, 3223 children: t21 3224 }) 3225 }); 3226 $[38] = t20; 3227 $[39] = t22; 3228 } else t22 = $[39]; 3229 let t23; 3230 if ($[40] !== handleSubmit || $[41] !== t19 || $[42] !== t22) { 3231 t23 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [t10, /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("form", { 3232 onSubmit: handleSubmit, 3233 "data-turbo": "false", 3234 children: [t19, t22] 3235 })] }); 3236 $[40] = handleSubmit; 3237 $[41] = t19; 3238 $[42] = t22; 3239 $[43] = t23; 3240 } else t23 = $[43]; 3241 return t23; 3242} 3243var import_compiler_runtime$1, import_react$1, import_jsx_runtime$2, t; 3244function init_UnsubscribeSection() { 3245 return (init_UnsubscribeSection = __esmMin((() => { 3246 import_compiler_runtime$1 = require_compiler_runtime(); 3247 import_react$1 = require_react(); 3248 init_button(); 3249 init_input(); 3250 init_label(); 3251 init_routes(); 3252 init_SettingToggle(); 3253 init_settingsWriteQueue(); 3254 import_jsx_runtime$2 = require_jsx_runtime(); 3255 t = (key) => i18n.t(`js.sequences_campaign_settings.unsubscribe.${key}`); 3256 })))(); 3257} 3258//#endregion
3259//#region app/frontend/pages/SequencesCampaignSettings/EmailAccountsStatusFrame.tsx 3260function escapeHtml(value) { 3261 return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); 3262} 3263function EmailAccountsStatusFrame(t0) { 3264 const $ = (0, import_compiler_runtime.c)(6); 3265 const { frame } = t0; 3266 if (!frame) return null; 3267 let t1; 3268 if ($[0] !== frame.id) { 3269 t1 = escapeHtml(frame.id); 3270 $[0] = frame.id; 3271 $[1] = t1; 3272 } else t1 = $[1]; 3273 let t2; 3274 if ($[2] !== frame.src) { 3275 t2 = escapeHtml(frame.src); 3276 $[2] = frame.src; 3277 $[3] = t2; 3278 } else t2 = $[3]; 3279 const t3 = `<turbo-frame id="${t1}" src="${t2}"></turbo-frame>`; 3280 let t4; 3281 if ($[4] !== t3) { 3282 t4 = /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("div", { 3283 "data-turbo": "true", 3284 dangerouslySetInnerHTML: { __html: t3 } 3285 }); 3286 $[4] = t3; 3287 $[5] = t4; 3288 } else t4 = $[5]; 3289 return t4; 3290} 3291var import_compiler_runtime, import_jsx_runtime$1; 3292function init_EmailAccountsStatusFrame() { 3293 return (init_EmailAccountsStatusFrame = __esmMin((() => { 3294 import_compiler_runtime = require_compiler_runtime(); 3295 import_jsx_runtime$1 = require_jsx_runtime(); 3296 })))(); 3297} 3298//#endregion 3299//#region app/frontend/pages/SequencesCampaignSettings/Index.tsx 3300function Index() { 3301 const { props } = usePage(); 3302 const { campaign, canUpdate, header, emailAccountsStatus, emailAccounts, tracking, sendingWindow, unsubscribe, launchModal, sidepanel, currentUserId, flash } = props; 3303 useSettingsWriteQueueTeardown(); 3304 const shownFlashRef = (0, import_react.useRef)(null); 3305 (0, import_react.useEffect)(() => { 3306 if (shownFlashRef.current === flash) return; 3307 shownFlashRef.current = flash; 3308 for (const [key, message] of Object.entries(flash ?? {})) if (message) App.displayFlash(dictLookup(FLASH_TYPE, key) ?? "info", message); 3309 }, [flash]); 3310 const navigationPendingRef = (0, import_react.useRef)(false);
3311 const [navigationPending, setNavigationPending] = (0, import_react.useState)(false); 3312 const releaseNavigationLock = (0, import_react.useCallback)(() => { 3313 navigationPendingRef.current = false; 3314 setNavigationPending(false); 3315 }, []); 3316 const supersededRef = (0, import_react.useRef)(false); 3317 useSettingsNavigationStart((0, import_react.useCallback)(() => { 3318 supersededRef.current = true; 3319 }, [])); 3320 /** 3321 * Leave this step by a header link without losing the write it armed. 3322 * 3323 * The header's wrapper is `data-turbo="true"`, so an unguarded link is a 3324 * Turbo visit across the Turbo/Inertia seam â which bounced the user out of 3325 * the wizard (HUN-22147). Taking the click here makes it an Inertia visit, 3326 * and draining the queue first is what keeps the setting the user just 3327 * changed: `useSettingsWriteQueueTeardown` cancels whatever is still pending 3328 * when the visit starts, so navigating on top of a debounced save would throw 3329 * that save away. 3330 * 3331 * The lock deliberately outlives the flush: this page stays mounted while 3332 * Inertia fetches, so it is released only on a navigation that never 3333 * happened â a rejected flush here, or Inertia's `onError` / `onCancel`. 3334 */ 3335 const handleNavigate = (0, import_react.useCallback)(async (event, destination) => { 3336 if (!isPlainNavigationClick(event)) return; 3337 event.preventDefault(); 3338 if (navigationPendingRef.current) return; 3339 navigationPendingRef.current = true; 3340 setNavigationPending(true); 3341 supersededRef.current = false; 3342 try { 3343 await flushSettingsWrites(); 3344 for (let pass = 0; pass < 2 && hasOutstandingSettingsWrites(); pass++) await flushSettingsWrites(); 3345 } catch { 3346 releaseNavigationLock(); 3347 App.displayFlash("error", i18n.t("js.unknown_error")); 3348 return; 3349 } 3350 if (supersededRef.current) { 3351 releaseNavigationLock(); 3352 return; 3353 } 3354 visitCohortAware(destination, { 3355 onError: releaseNavigationLock, 3356 onCancel: releaseNavigationLock, 3357 onFinish: releaseNavigationLock 3358 }); 3359 }, [releaseNavigationLock]); 3360 return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { 3361 className: "dashboard-main", 3362 children: [ 3363 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Head_default, { title: i18n.t("js.sequences_campaign_settings.meta_title", { name: campaign.name }) }), 3364 /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { 3365 className: "dashboard-main__content", 3366 children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { 3367 className: "master-details", 3368 children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { 3369 className: "master-details__master", 3370 children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { 3371 className: "campaign-grid campaign-grid--white", 3372 children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CampaignHeader, { 3373 header, 3374 onNavigate: handleNavigate, 3375 nextPending: navigationPending 3376 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { 3377 className: "campaign-grid__main", 3378 children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { 3379 className: "p-8 campaign-grid__main--settings", 3380 children: [ 3381 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailAccountsStatusFrame, { frame: emailAccountsStatus }), 3382 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailAccountsSection, { 3383 campaignId: campaign.id, 3384 emailAccounts, 3385 currentUserId 3386 }), 3387 /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "my-10" }), 3388 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TrackingSection, { 3389 campaignId: campaign.id, 3390 tracking 3391 }), 3392 /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "my-10" }), 3393 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendingWindowSection, { 3394 campaignId: campaign.id, 3395 sendingWindow, 3396 isDrip: campaign.drip, 3397 canUpdate 3398 }), 3399 /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "my-10" }), 3400 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnsubscribeSection, { 3401 campaignId: campaign.id, 3402 unsubscribe,
3403 canUpdate 3404 }) 3405 ] 3406 }) 3407 })] 3408 }) 3409 }) 3410 }) 3411 }), 3412 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SequenceEmailAccountSidepanel, { ...sidepanel }), 3413 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SequenceProtectEmailAccountModal, { ...sidepanel }), 3414 /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { 3415 className: "modal fade", 3416 id: "edit-smtp-imap-modal", 3417 role: "dialog", 3418 tabIndex: -1, 3419 "data-turbo": "true", 3420 children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("turbo-frame", { id: "edit_smtp_imap_connection" }) 3421 }), 3422 launchModal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LaunchModal, { 3423 campaignId: campaign.id, 3424 launchModal, 3425 senderAccountIds: emailAccounts.selectedIds, 3426 connectionCheckPath: emailAccounts.connectionCheckPath, 3427 currentUserId 3428 }) 3429 ] 3430 }); 3431} 3432var import_react, import_jsx_runtime, FLASH_TYPE; 3433function init_Index() { 3434 return (init_Index = __esmMin((() => { 3435 import_react = require_react(); 3436 init_dist$1(); 3437 init_AppLayout(); 3438 init_inertia_bridge(); 3439 init_CampaignHeader(); 3440 init_settingsWriteQueue(); 3441 init_EmailAccountsSection(); 3442 init_TrackingSection(); 3443 init_SendingWindowSection(); 3444 init_UnsubscribeSection(); 3445 init_LaunchModal(); 3446 init_useSettingsWriteQueueTeardown(); 3447 init_sequence_email_account_sidepanel(); 3448 init_sequence_protect_email_account_modal(); 3449 init_EmailAccountsStatusFrame(); 3450 import_jsx_runtime = require_jsx_runtime(); 3451 FLASH_TYPE = { 3452 success: "success", 3453 notice: "success", 3454 error: "danger", 3455 alert: "danger", 3456 warning: "warning" 3457 }; 3458 Index.layout = (page) => (0, import_react.createElement)(AppLayout, { fullscreen: true }, page); 3459 })))(); 3460} 3461//#endregion 3462init_Index(); 3463export { Index as default };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.