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] = "84d51045-0373-44eb-b296-0148998683e9", e._sentryDebugIdIdentifier = "sentry-dbid-84d51045-0373-44eb-b296-0148998683e9"); 7 } catch (e) {} 8})(); 9import { a as __toESM, n as __esmMin } from "./rolldown-runtime-sLwMD6dS.js"; 10import { Aa as multi_domain_search_counts_search_index_path, Dt as DropdownSearchInput, Et as DropdownMenuTrigger, F as prefersReducedMotion, M as init_currency_helper, Mi as init_routes, Os as Checkbox, P as init_reduced_motion, Pa as multi_domain_search_search_index_path, Tt as DropdownMenuTitle, Zn as checkout_upgrade_plan_path, _ as init_animate, at as AlertDescription, bt as DropdownMenuItem, ct as AlertTitle, ei as edit_subscriptions_path, et as Skeleton, ft as DropdownMenu, g as animate, h as init_costCalculator, ht as DropdownMenuDivider, it as AlertContent, j as CurrencyHelper, ja as multi_domain_search_emails_search_index_path, ks as init_checkbox, kt as init_dropdown_menu, lt as init_alert, m as CostCalculator, ma as leads_path, mt as DropdownMenuContent, nt as Alert, pt as DropdownMenuAction, st as AlertIcon, tt as init_skeleton, ur as company_list_path } from "./controllers-jdPQjaCB.js"; 11import { a as isNumber, o as isObject, s as isString } from "./type-guards-DVyG5ljW.js"; 12import { i as require_compiler_runtime, l as getCsrfToken, 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 { n as ButtonSuffix, r as init_button, t as Button } from "./button-DOiPZHfy.js"; 16import { a as init_tooltip, r as TooltipProvider } from "./tooltip-HHfiWInP.js"; 17import { c as SnackbarLink, l as SnackbarText, n as useAsyncQueuer, s as Snackbar, t as init_useAsyncQueuer, u as init_snackbar } from "./useAsyncQueuer-DxA58iaB.js"; 18import { $ as init_seniority, A as filterFunction, F as sortFunction, N as orderSeniorityLevels, O as Filters, X as init_esm, Y as usePositionTaxonomy, Z as useVirtualizer, et as orderClassifiedSeniorityLevels, i as useLocationMappings, j as init_filters, k as clearFilters, n as useEmailSearch, q as init_usePositionTaxonomy, r as init_useLocationMappings, t as init_useEmailSearch } from "./useEmailSearch-D71GAJgY.js"; 19import { c as captureException, s as init_esm$1 } from "./sentry-error-boundary-riAWv_XB.js"; 20import { i as init_fetch_with_retry, n as init_reveal_save_dropdown, r as fetchWithRetry, t as RevealSaveDropdown } from "./reveal-save-dropdown-abkRRssE.js"; 21import { a as init_useJobTitleSuggestions, c as useCampaignsSave, i as useSplittedResults, l as init_upgrade_illustration, n as useLeadsListSave, o as useJobTitleSuggestions, r as init_useSplittedResults, s as init_useCampaignsSave, t as init_useLeadsListSave, u as upgrade_illustration_default } from "./useLeadsListSave-sUF_wqjF.js"; 22import { n as init_cost_estimation, t as CostEstimation } from "./cost-estimation-Jej0Ph3J.js"; 23import { n as init_credit_indicator, t as CreditIndicator } from "./credit-indicator-5uLMMjJi.js"; 24import { a as PillTabsTriggerHint, i as PillTabsTrigger, n as PillTabsContent, o as PillTabsTriggerName, r as PillTabsList, s as init_pill_tabs, t as PillTabs } from "./pill-tabs-BfmSgv8F.js"; 25import { f as init_sheet, i as SheetBody, n as SheetAlert } from "./sheet-2lP39BDr.js"; 26import { n as init_cost_indicator, t as CostIndicator } from "./cost-indicator-DKlBRQzL.js"; 27import { r as init_separator, t as Separator } from "./separator-D5aQTBY-.js"; 28import { n as init_virtualized_results, t as VirtualizedResults } from "./virtualized-results-DU9TO_qY.js"; 29import { a as DialogFooter, i as DialogContent, n as DialogBody, o as DialogHeader, r as DialogCloseButton, s as DialogTitle, t as Dialog, u as init_dialog } from "./dialog-XgiZyeRA.js"; 30import { i as init_select_count_menu, n as SelectCountMenu } from "./select-count-menu-CyKZwGL-.js"; 31import { n as useDialog, t as init_useDialog } from "./useDialog-CwjC07YA.js"; 32//#region app/frontend/react/components/search/generic-email-reveal-alert.tsx 33function GenericEmailRevealAlert(t0) { 34 const $ = (0, import_compiler_runtime$7.c)(16); 35 const { results, numberOfCompanies, revealingIds, onReveal } = t0; 36 let t1; 37 let target; 38 if ($[0] !== numberOfCompanies || $[1] !== results || $[2] !== revealingIds) { 39 const genericResults = results.filter(_temp$3); 40 target = genericResults.find(_temp2); 41 let t2; 42 if ($[5] !== revealingIds) { 43 t2 = new Set(revealingIds); 44 $[5] = revealingIds; 45 $[6] = t2; 46 } else t2 = $[6]; 47 const revealingIdsSet = t2; 48 t1 = numberOfCompanies !== 1 || !target || genericResults.some((result_1) => revealingIdsSet.has(result_1.id)); 49 $[0] = numberOfCompanies; 50 $[1] = results; 51 $[2] = revealingIds; 52 $[3] = t1; 53 $[4] = target; 54 } else { 55 t1 = $[3]; 56 target = $[4]; 57 } 58 if (t1) return null; 59 let t2; 60 if ($[7] === Symbol.for("react.memo_cache_sentinel")) { 61 t2 = /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(AlertIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(Icon, { icon: "info-circle" }) }); 62 $[7] = t2; 63 } else t2 = $[7]; 64 let t3; 65 if ($[8] === Symbol.for("react.memo_cache_sentinel")) { 66 t3 = /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(AlertContent, { 67 className: "text-xs", 68 children: i18n.t("js.search.generic_email_badge.all_description") 69 }); 70 $[8] = t3; 71 } else t3 = $[8]; 72 let t4; 73 if ($[9] !== onReveal || $[10] !== target.id) { 74 t4 = () => onReveal(target.id); 75 $[9] = onReveal; 76 $[10] = target.id; 77 $[11] = t4; 78 } else t4 = $[11]; 79 let t5; 80 if ($[12] === Symbol.for("react.memo_cache_sentinel")) { 81 t5 = i18n.t("js.search.generic_email_badge.cta"); 82 $[12] = t5; 83 } else t5 = $[12]; 84 let t6; 85 if ($[13] === Symbol.for("react.memo_cache_sentinel")) { 86 t6 = /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(ButtonSuffix, { children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(CostIndicator, { cost: 1 }) }); 87 $[13] = t6; 88 } else t6 = $[13]; 89 let t7; 90 if ($[14] !== t4) { 91 t7 = /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)(Alert, { 92 variant: "info", 93 size: "small", 94 className: "mb-2 align-items-center", 95 children: [ 96 t2, 97 t3,
98 /* @__PURE__ */ (0, import_jsx_runtime$5.jsxs)(Button, { 99 size: "sm", 100 onClick: t4, 101 children: [t5, t6] 102 }) 103 ] 104 }); 105 $[14] = t4; 106 $[15] = t7; 107 } else t7 = $[15]; 108 return t7; 109} 110function _temp2(result_0) { 111 return !result_0.revealed; 112} 113function _temp$3(result) { 114 return result.type === "generic"; 115} 116var import_compiler_runtime$7, import_jsx_runtime$5; 117function init_generic_email_reveal_alert() { 118 return (init_generic_email_reveal_alert = __esmMin((() => { 119 import_compiler_runtime$7 = require_compiler_runtime(); 120 init_alert(); 121 init_button(); 122 init_icon(); 123 init_cost_indicator(); 124 import_jsx_runtime$5 = require_jsx_runtime(); 125 })))(); 126} 127//#endregion 128//#region app/frontend/react/hooks/useCappedPeopleCounts.ts 129function parseCountPage(value) { 130 if (!isObject(value)) throw new Error("Invalid people count response"); 131 const { all_people, decision_makers, next_after_domain } = value; 132 if (!isNumber(all_people) || !Number.isSafeInteger(all_people) || all_people < 0 || !isNumber(decision_makers) || !Number.isSafeInteger(decision_makers) || decision_makers < 0 || decision_makers > all_people || next_after_domain !== null && (!isString(next_after_domain) || next_after_domain.length === 0)) throw new Error("Invalid people count response"); 133 return { 134 all_people, 135 decision_makers, 136 next_after_domain 137 }; 138} 139function useCappedPeopleCounts(requestBody, includeGrandTotals) { 140 const $ = (0, import_compiler_runtime$6.c)(8); 141 let t0; 142 if ($[0] !== includeGrandTotals || $[1] !== requestBody) { 143 t0 = JSON.stringify([requestBody, includeGrandTotals]); 144 $[0] = includeGrandTotals; 145 $[1] = requestBody; 146 $[2] = t0; 147 } else t0 = $[2]; 148 const requestKey = t0; 149 const [result, setResult] = (0, import_react$8.useState)(null); 150 let t1; 151 let t2; 152 if ($[3] !== includeGrandTotals || $[4] !== requestBody || $[5] !== requestKey) { 153 t1 = () => { 154 if (requestBody === null) return; 155 const controller = new AbortController(); 156 const body = JSON.parse(requestBody); 157 const countPopulation = async (population) => { 158 const total = { 159 all_people: 0, 160 decision_makers: 0 161 }; 162 let afterDomain = null; 163 do { 164 const response = await fetch(multi_domain_search_counts_search_index_path(), { 165 method: "POST", 166 headers: { 167 "Content-Type": "application/json", 168 "X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content ?? "" 169 }, 170 body: JSON.stringify({ 171 ...body, 172 population, 173 after_domain: afterDomain 174 }), 175 signal: controller.signal 176 }); 177 if (!response.ok) throw new Error(`People count request failed: ${response.status}`); 178 const page = parseCountPage(await response.json()); 179 if (controller.signal.aborted) throw new DOMException("Aborted", "AbortError"); 180 if (page.next_after_domain !== null && afterDomain !== null && page.next_after_domain <= afterDomain) throw new Error("Invalid people count response"); 181 total.all_people = total.all_people + page.all_people; 182 total.decision_makers = total.decision_makers + page.decision_makers; 183 afterDomain = page.next_after_domain; 184 } while (afterDomain !== null); 185 return total; 186 }; 187 const hasPersonFilters = Object.values(body.email_filters ?? {}).some(_temp$2); 188 const filteredCounts = countPopulation("filtered"); 189 const unfilteredCounts = !includeGrandTotals ? Promise.resolve(null) : !hasPersonFilters ? filteredCounts : countPopulation("unfiltered"); 190 Promise.all([filteredCounts, unfilteredCounts]).then((t3) => { 191 const [filtered, unfiltered] = t3; 192 if (!controller.signal.aborted) setResult({ 193 key: requestKey, 194 counts: { 195 filtered, 196 unfiltered 197 } 198 }); 199 }).catch((error) => { 200 if (!controller.signal.aborted) { 201 controller.abort(); 202 captureException(error, { tags: { 203 component: "multi_domain_search", 204 stage: "capped_counts" 205 } }); 206 } 207 }); 208 return () => controller.abort(); 209 }; 210 t2 = [ 211 requestBody, 212 includeGrandTotals, 213 requestKey 214 ]; 215 $[3] = includeGrandTotals; 216 $[4] = requestBody; 217 $[5] = requestKey; 218 $[6] = t1; 219 $[7] = t2; 220 } else { 221 t1 = $[6]; 222 t2 = $[7]; 223 } 224 (0, import_react$8.useEffect)(t1, t2); 225 return result?.key === requestKey ? result.counts : null; 226} 227function _temp$2(value) { 228 return Array.isArray(value) ? value.length > 0 : value != null && value !== ""; 229} 230var import_compiler_runtime$6, import_react$8; 231function init_useCappedPeopleCounts() { 232 return (init_useCappedPeopleCounts = __esmMin((() => { 233 import_compiler_runtime$6 = require_compiler_runtime(); 234 import_react$8 = require_react(); 235 init_esm$1(); 236 init_routes(); 237 })))(); 238} 239//#endregion 240//#region app/frontend/react/hooks/useRevealCost.ts 241function useRevealCost({ selectedIds, tabSelected, results, allPeopleResults, decisionMakersResults, genericResults, numberOfCreditsLeft, singleCreditsBucket = true, serverTabCount = null }) { 242 return (0, import_react$7.useMemo)(() => { 243 let numberOfEmailsToReveal = 0; 244 if (selectedIds.length > 0) numberOfEmailsToReveal = results.filter((result) => selectedIds.includes(result.id) && !result.revealed).length; 245 else if (serverTabCount != null) numberOfEmailsToReveal = serverTabCount; 246 else numberOfEmailsToReveal = tabSelected === "all_people" ? allPeopleResults.filter((result_0) => !result_0.revealed).length : tabSelected === "decision_makers" ? decisionMakersResults.filter((result_1) => !result_1.revealed).length : new Set(genericResults.filter((result_2) => !result_2.revealed).map((result_3) => result_3.domain)).size; 247 const numberOfCreditsNeeded = CostCalculator.calculate("search", numberOfEmailsToReveal, singleCreditsBucket); 248 let neededCredits = numberOfCreditsNeeded - numberOfCreditsLeft; 249 if (neededCredits < 0) neededCredits = 0; 250 const revealAllOperationCost = CostCalculator.calculateVisualRepresentation("search", numberOfEmailsToReveal, singleCreditsBucket); 251 const missingCreditsVisual = singleCreditsBucket ? neededCredits / CostCalculator.UNIFIED_CREDITS_BASE : neededCredits; 252 return { 253 numberOfEmailsToReveal, 254 numberOfCreditsNeeded, 255 neededCredits, 256 revealAllOperationCost, 257 missingCreditsVisual 258 }; 259 }, [ 260 selectedIds, 261 tabSelected, 262 results, 263 allPeopleResults, 264 decisionMakersResults, 265 genericResults, 266 numberOfCreditsLeft, 267 singleCreditsBucket, 268 serverTabCount 269 ]); 270} 271var import_react$7; 272function init_useRevealCost() { 273 return (init_useRevealCost = __esmMin((() => { 274 import_react$7 = require_react(); 275 init_costCalculator(); 276 })))(); 277} 278//#endregion 279//#region app/frontend/react/helpers/signals-analytics.ts 280var import_compiler_runtime$5, import_react$6, SIGNAL_SOURCE, captureSignalsEvent, ageHours, useSignalsSheetAnalytics;
281function init_signals_analytics() { 282 return (init_signals_analytics = __esmMin((() => { 283 import_compiler_runtime$5 = require_compiler_runtime(); 284 import_react$6 = /* @__PURE__ */ __toESM(require_react()); 285 SIGNAL_SOURCE = { 286 hunterSuggestion: "hunter_suggestion", 287 savedSearchUpdate: "saved_search_update", 288 existingLeadsSignal: "existing_leads_signal" 289 }; 290 captureSignalsEvent = (name, properties = {}) => { 291 if ("App" in globalThis && App.analytics?.posthog_capture) App.analytics.posthog_capture(name, properties.signal_id == null ? properties : { 292 ...properties, 293 signal_id: String(properties.signal_id) 294 }); 295 }; 296 ageHours = (at) => { 297 if (at == null) return null; 298 const time = at instanceof Date ? at.getTime() : new Date(at).getTime(); 299 if (!Number.isFinite(time)) return null; 300 return Math.max(0, Math.round((Date.now() - time) / 36e5)); 301 }; 302 useSignalsSheetAnalytics = (userLookalikeId) => { 303 const $ = (0, import_compiler_runtime$5.c)(17); 304 let t0; 305 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 306 t0 = /* @__PURE__ */ new Set(); 307 $[0] = t0; 308 } else t0 = $[0]; 309 const createdListIds = import_react$6.useRef(t0); 310 let t1; 311 if ($[1] !== userLookalikeId) { 312 t1 = userLookalikeId != null && Number.isFinite(userLookalikeId); 313 $[1] = userLookalikeId; 314 $[2] = t1; 315 } else t1 = $[2]; 316 const enabled = t1; 317 const t2 = userLookalikeId ?? null; 318 let t3; 319 if ($[3] !== t2) { 320 t3 = { 321 signal_source: SIGNAL_SOURCE.hunterSuggestion, 322 signal_id: t2, 323 entity: "lead" 324 }; 325 $[3] = t2; 326 $[4] = t3; 327 } else t3 = $[4]; 328 const base = t3; 329 let t4; 330 if ($[5] !== base || $[6] !== enabled) { 331 t4 = async (request, revealCount, mode, list) => { 332 const listCreated = list?.leadsListId != null && createdListIds.current.delete(list.leadsListId); 333 const succeeded = await request; 334 if (!enabled || !succeeded) return succeeded; 335 if (revealCount > 0) captureSignalsEvent("signals-reveal", { 336 ...base, 337 count: revealCount, 338 mode 339 }); 340 if (list?.leadsListId != null && list.savedCount > 0) captureSignalsEvent("signals-saved", { 341 ...base, 342 target: "list", 343 count: list.savedCount, 344 list_created: listCreated 345 }); 346 return succeeded; 347 }; 348 $[5] = base; 349 $[6] = enabled; 350 $[7] = t4; 351 } else t4 = $[7]; 352 let t5; 353 if ($[8] === Symbol.for("react.memo_cache_sentinel")) { 354 t5 = (listId) => { 355 createdListIds.current.add(listId); 356 }; 357 $[8] = t5; 358 } else t5 = $[8]; 359 let t6; 360 let t7; 361 if ($[9] !== base || $[10] !== enabled) { 362 t6 = (save) => async (name, listId_0, emailId, folderId) => { 363 const saved = await save(name, listId_0, emailId, folderId); 364 if (enabled && saved) captureSignalsEvent("signals-saved", { 365 ...base, 366 target: "list", 367 count: 1, 368 list_created: listId_0 == null 369 }); 370 return saved; 371 }; 372 t7 = (save_0) => async (name_0, campaignId, emailId_0) => { 373 const result = await save_0(name_0, campaignId, emailId_0); 374 if (enabled && result.status === "success") captureSignalsEvent("signals-saved", { 375 ...base, 376 target: "campaign", 377 count: 1, 378 list_created: campaignId == null 379 }); 380 return result; 381 }; 382 $[9] = base; 383 $[10] = enabled; 384 $[11] = t6; 385 $[12] = t7; 386 } else { 387 t6 = $[11]; 388 t7 = $[12]; 389 } 390 let t8; 391 if ($[13] !== t4 || $[14] !== t6 || $[15] !== t7) { 392 t8 = { 393 reveal: t4, 394 markListCreated: t5, 395 wrapLeadsListSave: t6, 396 wrapCampaignsSave: t7 397 }; 398 $[13] = t4; 399 $[14] = t6; 400 $[15] = t7; 401 $[16] = t8; 402 } else t8 = $[16]; 403 return t8; 404 }; 405 })))(); 406} 407//#endregion 408//#region app/frontend/react/hooks/useMultiDomainSearchFiltersStorage.ts 409function buildStorageKey(source) { 410 return `${STORAGE_KEY_PREFIX}:${source}`; 411} 412function readStoredFilters(source) { 413 try { 414 const raw = sessionStorage.getItem(buildStorageKey(source)); 415 if (!raw) return null; 416 return JSON.parse(raw); 417 } catch { 418 return null; 419 } 420} 421function recognizedPeoplePerCompany(value) { 422 const cap = Number(value?.specificNumber); 423 if (!Number.isInteger(cap) || cap < 1) return void 0; 424 return { 425 type: "specific_number", 426 specificNumber: cap 427 }; 428} 429function normalizeFilters(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled) { 430 const recognizedDepartments = Array.isArray(filters.departments) ? filters.departments.filter((department) => typeof department === "string" && (!positionTaxonomy || positionTaxonomy.departments.some((entry) => entry.value === department))) : []; 431 const recognizedLevels = Array.isArray(filters.levels) ? filters.levels.filter((level) => typeof level === "string") : []; 432 return { 433 ...filters, 434 departments: recognizedDepartments, 435 levels: seniorityFilterEnabled && !nineLevelSeniorityEnabled ? orderSeniorityLevels(rec
435ognizedLevels, positionTaxonomy) : [], 436 seniorityLevels: nineLevelSeniorityEnabled ? orderClassifiedSeniorityLevels(Array.isArray(filters.seniorityLevels) ? filters.seniorityLevels : []) : void 0, 437 peoplePerCompany: recognizedPeoplePerCompany(filters.peoplePerCompany) 438 }; 439} 440function writeStoredFilters(source, filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled) { 441 try { 442 sessionStorage.setItem(buildStorageKey(source), JSON.stringify(normalizeFilters(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled))); 443 } catch {} 444} 445function removeStoredFilters(source) { 446 try { 447 sessionStorage.removeItem(buildStorageKey(source)); 448 } catch {} 449} 450function useMultiDomainSearchFiltersStorage({ source, filters, setFilters, skipRestore = false, seniorityFilterEnabled = false, nineLevelSeniorityEnabled = false, positionTaxonomy = null }) { 451 const hasRestoredRef = (0, import_react$5.useRef)(false); 452 const restoredConfigurationRef = (0, import_react$5.useRef)(null); 453 const skipNextPersistRef = (0, import_react$5.useRef)(false); 454 (0, import_react$5.useEffect)(() => { 455 const configuration = `${source}:${seniorityFilterEnabled}:${nineLevelSeniorityEnabled}:${skipRestore}`; 456 if (restoredConfigurationRef.current === configuration) return; 457 restoredConfigurationRef.current = configuration; 458 hasRestoredRef.current = false; 459 if (!skipRestore) { 460 const stored = readStoredFilters(source); 461 if (stored) setFilters(normalizeFilters(stored, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled)); 462 } 463 setTimeout(() => { 464 hasRestoredRef.current = true; 465 }, 0); 466 }, [ 467 source, 468 seniorityFilterEnabled, 469 nineLevelSeniorityEnabled, 470 setFilters, 471 skipRestore, 472 positionTaxonomy 473 ]); 474 (0, import_react$5.useEffect)(() => { 475 const normalized = normalizeFilters(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled); 476 const departments = Array.isArray(filters.departments) ? filters.departments : []; 477 const levels = Array.isArray(filters.levels) ? filters.levels : []; 478 if (departments.length !== normalized.departments.length || departments.some((department, index_0) => department !== normalized.departments[index_0]) || levels.length !== normalized.levels?.length || levels.some((level, index) => level !== normalized.levels?.[index]) || JSON.stringify(filters.seniorityLevels ?? []) !== JSON.stringify(normalized.seniorityLevels ?? [])) setFilters(normalized); 479 }, [ 480 filters, 481 positionTaxonomy, 482 seniorityFilterEnabled, 483 nineLevelSeniorityEnabled, 484 setFilters 485 ]); 486 (0, import_react$5.useEffect)(() => { 487 if (!hasRestoredRef.current) return; 488 if (skipNextPersistRef.current) { 489 skipNextPersistRef.current = false; 490 return; 491 } 492 writeStoredFilters(source, filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled); 493 }, [ 494 source, 495 filters, 496 seniorityFilterEnabled, 497 nineLevelSeniorityEnabled, 498 positionTaxonomy 499 ]); 500 return { clearPersistedFilters: (0, import_react$5.useCallback)(() => { 501 skipNextPersistRef.current = true; 502 removeStoredFilters(source); 503 }, [source]) }; 504} 505var import_react$5, STORAGE_KEY_PREFIX; 506function init_useMultiDomainSearchFiltersStorage() { 507 return (init_useMultiDomainSearchFiltersStorage = __esmMin((() => { 508 import_react$5 = require_react(); 509 init_filters(); 510 init_seniority(); 511 STORAGE_KEY_PREFIX = "multi_domain_search_filters"; 512 })))(); 513} 514//#endregion 515//#region app/frontend/react/components/multi_domain_search/credits_options_modal.tsx 516function CreditsOptionsModal(t0) { 517 const $ = (0, import_compiler_runtime$4.c)(29); 518 const { open, onClose, onProceedWithAvailable, onGetMoreCredits, costEstimationData, numberOfEmailsToReveal } = t0; 519 const affordableCount = Math.floor(costEstimationData.availableCredits); 520 if (!open) return null; 521 let t1; 522 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 523 t1 = { backgroundColor: "rgba(var(--colors-neutral-1000--rgb), 0.5)" }; 524 $[0] = t1; 525 } else t1 = $[0]; 526 let t2; 527 if ($[1] === Symbol.for("react.memo_cache_sentinel")) { 528 t2 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(DialogTitle, { children: i18n.t("js.multi_domain_search.credits_options_modal.title") }); 529 $[1] = t2; 530 } else t2 = $[1]; 531 let t3; 532 if ($[2] !== onClose) { 533 t3 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)(DialogHeader, { children: [t2, /* @__P
533URE__ */ (0, import_jsx_runtime$4.jsx)(DialogCloseButton, { onClick: onClose })] }); 534 $[2] = onClose; 535 $[3] = t3; 536 } else t3 = $[3]; 537 let t4; 538 if ($[4] === Symbol.for("react.memo_cache_sentinel")) { 539 t4 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("p", { 540 className: "text-sm mb-6", 541 children: i18n.t("js.multi_domain_search.credits_options_modal.message") 542 }); 543 $[4] = t4; 544 } else t4 = $[4]; 545 let t5; 546 if ($[5] !== costEstimationData) { 547 t5 = { 548 ...costEstimationData, 549 showAlert: false 550 }; 551 $[5] = costEstimationData; 552 $[6] = t5; 553 } else t5 = $[6]; 554 let t6; 555 if ($[7] !== numberOfEmailsToReveal || $[8] !== t5) { 556 t6 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)(DialogBody, { children: [t4, /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(CostEstimation, { 557 data: t5, 558 numberOfEmails: numberOfEmailsToReveal 559 })] }); 560 $[7] = numberOfEmailsToReveal; 561 $[8] = t5; 562 $[9] = t6; 563 } else t6 = $[9]; 564 let t7; 565 if ($[10] !== onGetMoreCredits) { 566 t7 = onGetMoreCredits && /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(Button, { 567 variant: "primary", 568 fullWidth: true, 569 className: "mb-2", 570 onClick: onGetMoreCredits, 571 children: i18n.t("js.multi_domain_search.credits_options_modal.get_more_credits") 572 }); 573 $[10] = onGetMoreCredits; 574 $[11] = t7; 575 } else t7 = $[11]; 576 const t8 = affordableCount === 0; 577 let t9; 578 if ($[12] !== affordableCount) { 579 t9 = i18n.t(affordableCount === 0 ? "js.multi_domain_search.credits_options_modal.proceed_tooltip_no_credits" : "js.multi_domain_search.credits_options_modal.proceed_tooltip"); 580 $[12] = affordableCount; 581 $[13] = t9; 582 } else t9 = $[13]; 583 let t10; 584 if ($[14] !== affordableCount) { 585 t10 = i18n.t("js.multi_domain_search.credits_options_modal.proceed_with_available", { count: CurrencyHelper.numberWithDelimiter(affordableCount) }); 586 $[14] = affordableCount; 587 $[15] = t10; 588 } else t10 = $[15]; 589 let t11; 590 if ($[16] === Symbol.for("react.memo_cache_sentinel")) { 591 t11 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(Icon, { 592 icon: "question-circle", 593 size: "sm" 594 }); 595 $[16] = t11; 596 } else t11 = $[16]; 597 let t12; 598 if ($[17] !== onProceedWithAvailable || $[18] !== t10 || $[19] !== t8 || $[20] !== t9) { 599 t12 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)(Button, { 600 variant: "default", 601 fullWidth: true, 602 disabled: t8, 603 onClick: onProceedWithAvailable, 604 "data-controller": "tooltip", 605 "data-bs-placement": "bottom", 606 "data-bs-custom-class": "tooltip-md", 607 title: t9, 608 children: [t10, t11] 609 }); 610 $[17] = onProceedWithAvailable; 611 $[18] = t10; 612 $[19] = t8; 613 $[20] = t9; 614 $[21] = t12; 615 } else t12 = $[21]; 616 let t13; 617 if ($[22] !== t12 || $[23] !== t7) { 618 t13 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)(DialogFooter, { children: [t7, t12] }); 619 $[22] = t12; 620 $[23] = t7; 621 $[24] = t13; 622 } else t13 = $[24]; 623 let t14; 624 if ($[25] !== t13 || $[26] !== t3 || $[27] !== t6) { 625 t14 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("div", { 626 className: "modal fade show upgrade-modal d-block", 627 tabIndex: -1, 628 role: "dialog", 629 style: t1, 630 children: /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(Dialog, { 631 open: true, 632 className: "modal-dialog", 633 children: /* @__PURE__ */ (0, import_jsx_runtime$4.jsxs)(DialogContent, { children: [ 634 t3, 635 t6, 636 t13 637 ] }) 638 }) 639 }); 640 $[25] = t13; 641 $[26] = t3; 642 $[27] = t6; 643 $[28] = t14; 644 } else t14 = $[28]; 645 return t14; 646} 647var import_compiler_runtime$4, import_jsx_runtime$4; 648function init_credits_options_modal() { 649 return (init_credits_options_modal = __esmMin((() => { 650 import_compiler_runtime$4 = require_compiler_runtime(); 651 init_button(); 652 init_icon(); 653 init_currency_helper(); 654 init_dialog(); 655 init_cost_estimation(); 656 import_jsx_runtime$4 = require_jsx_runtime(); 657 })))(); 658} 659//#endregion 660//#region app/frontend/react/components/multi_domain_search/select-all-control.tsx 661/** 662* Master select-all checkbox on the Find People count line. The checkbox is the menu trigger (caret on 663* hover); the menu offers Select all / Select a custom number / Deselect all via `SelectCountMenu`. 664*/ 665function MultiDomainSearchSelectAll(t0) { 666 const $ = (0, import_compiler_runtime$3.c)(16); 667 const { checked, indeterminate, onSelectAll, onSelectCount, onDeselectAll, selectAllCount } = t0; 668 const checkboxRef = import_react$4.useRef(null); 669 let t1; 670 let t2; 671 if ($[0] !== indeterminate) { 672 t1 = () => { 673 if (checkboxRef.current) checkboxRef.current.indeterminate = indeterminate; 674 }; 675 t2 = [indeterminate]; 676 $[0] = indeterminate; 677 $[1] = t1; 678 $[2] = t2; 679 } else { 680 t1 = $[1]; 681 t2 = $[2]; 682 } 683 import_react$4.useEffect(t1, t2); 684 let t3; 685 if ($[3] === Symbol.for("react.memo_cache_sentinel")) { 686 t3 = t$1("aria_label"); 687 $[3] = t3; 688 } else t3 = $[3]; 689 let t4; 690 if ($[4] !== checked) { 691 t4 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Checkbox, { 692 ref: checkboxRef, 693 checked, 694 readOnly: true, 695 "aria-label": t3, 696 onClick: _temp$1 697 }); 698 $[4] = checked; 699 $[5] = t4; 700 } else t4 = $[5]; 701 let t5; 702 if ($[6] !== checked || $[7] !== indeterminate || $[8] !== onDeselectAll || $[9] !== onSelectAll || $[10] !== selectAllCount) { 703 t5 = (t6) => { 704 const { openCustom } = t6; 705 return /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(import_jsx_runtime$3.Fragment, { children: [
706 /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(DropdownMenuItem, { 707 closeOnClick: true, 708 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(DropdownMenuAction, { 709 type: "button", 710 onClick: onSelectAll, 711 children: [t$1("select_all"), selectAllCount != null && /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)("small", { 712 className: "grey ml-auto", 713 children: [ 714 "(", 715 selectAllCount.toLocaleString(), 716 ")" 717 ] 718 })] 719 }) 720 }), 721 /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(DropdownMenuItem, { children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(DropdownMenuAction, { 722 type: "button", 723 onClick: openCustom, 724 children: t$1("select_custom") 725 }) }), 726 (checked || indeterminate) && /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(import_jsx_runtime$3.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(DropdownMenuDivider, {}), /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(DropdownMenuItem, { 727 closeOnClick: true, 728 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(DropdownMenuAction, { 729 type: "button", 730 onClick: onDeselectAll, 731 children: t$1("deselect_all") 732 }) 733 })] }) 734 ] }); 735 }; 736 $[6] = checked; 737 $[7] = indeterminate; 738 $[8] = onDeselectAll; 739 $[9] = onSelectAll; 740 $[10] = selectAllCount; 741 $[11] = t5; 742 } else t5 = $[11]; 743 let t6; 744 if ($[12] !== onSelectCount || $[13] !== t4 || $[14] !== t5) { 745 t6 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(SelectCountMenu, { 746 className: "multi-domain-search-select-all", 747 align: "start", 748 onSelectCount, 749 trigger: t4, 750 renderItems: t5 751 }); 752 $[12] = onSelectCount; 753 $[13] = t4; 754 $[14] = t5; 755 $[15] = t6; 756 } else t6 = $[15]; 757 return t6; 758} 759function _temp$1(event) { 760 return event.preventDefault(); 761} 762var import_compiler_runtime$3, import_react$4, import_jsx_runtime$3, t$1; 763function init_select_all_control() { 764 return (init_select_all_control = __esmMin((() => { 765 import_compiler_runtime$3 = require_compiler_runtime(); 766 import_react$4 = /* @__PURE__ */ __toESM(require_react()); 767 init_select_count_menu(); 768 init_checkbox(); 769 init_dropdown_menu(); 770 import_jsx_runtime$3 = require_jsx_runtime(); 771 t$1 = (key) => window.i18n.t(`js.multi_domain_search.select_all.${key}`); 772 })))(); 773} 774//#endregion 775//#region app/frontend/react/hooks/useAnimatedNumber.ts 776/** 777* Tween a numeric value toward `value` whenever it changes, returning the floored 778* intermediate values for display. Mirrors the credit indicator's animation pattern 779* (motion's `animate` + `circOut`), with a short/long duration heuristic based on 780* the magnitude of the change. 781* 782* Passing `null`/`undefined` animates toward 0. 783* 784* Respects `prefers-reduced-motion`: snaps straight to the value (no tween). 785*/ 786function useAnimatedNumber(value) { 787 const $ = (0, import_compiler_runtime$2.c)(3); 788 const target = value ?? 0; 789 const [displayed, setDisplayed] = (0, import_react$3.useState)(target); 790 const prevRef = (0, import_react$3.useRef)(target); 791 const animationRef = (0, import_react$3.useRef)(null); 792 let t0; 793 let t1; 794 if ($[0] !== target) { 795 t0 = () => { 796 animationRef.current?.cancel?.(); 797 const from = prevRef.current; 798 const to = target; 799 prevRef.current = to; 800 if (from === to) return; 801 if (prefersReducedMotion()) { 802 setDisplayed(to); 803 return; 804 } 805 const duration = Math.abs(to - from) <= SHORT_DURATION_THRESHOLD ? SHORT_DURATION : LONG_DURATION; 806 animationRef.current = animate(from, to, { 807 duration, 808 ease: "circOut", 809 onUpdate: (latest) => setDisplayed(Math.floor(latest)) 810 }); 811 return () => animationRef.current?.cancel?.(); 812 }; 813 t1 = [target]; 814 $[0] = target; 815 $[1] = t0; 816 $[2] = t1; 817 } else { 818 t0 = $[1]; 819 t1 = $[2]; 820 } 821 (0, import_react$3.useEffect)(t0, t1); 822 return displayed; 823} 824var import_compiler_runtime$2, import_react$3, SHORT_DURATION, LONG_DURATION, SHORT_DURATION_THRESHOLD; 825function init_useAnimatedNumber() { 826 return (init_useAnimatedNumber = __esmMin((() => { 827 import_compiler_runtime$2 = require_compiler_runtime(); 828 import_react$3 = require_react(); 829 init_animate(); 830 init_reduced_motion(); 831 SHORT_DURATION = .4; 832 LONG_DURATION = .8; 833 SHORT_DURATION_THRESHOLD = 7; 834 })))(); 835} 836//#endregion 837//#region app/frontend/react/components/multi_domain_search.tsx 838function MultiDomainSearchLegacy({ numberOfCompanies, numberOfResults, genericEmailsCount: _genericEmailsCount, personalEmailsCount: _personalEmailsCount, allowDiscoverPaging, filterParams, numberOfCreditsLeft, leadsLists: initialLeadsLists, folders = [], campaigns: initialCampaigns, type, source, clickToCopy = false, costEstimationContext, initialJobTitles, initialLevels, initialSeniorityLevels, queryType, hideHeader = false, userLookalikeId, seniorityFilterEnabled = false, nineLevelSeniorityEnabled = false }) { 839 const [domains, setDomains] = import_react$2.useState([]); 840 const [nextSearchAfter, setNextSearchAfter] = import_react$2.useState(null); 841 const [searchError, setSearchError] = import_react$2.useState(null); 842 const requestControllerRef = import_react$2.useRef(null); 843 const failedBatchesRef = import_react$2.useRef([]); 844 const [serverFiltered, setServerFiltered] = import_react$2.useState(false); 845 const [serverCount, setServerCount] = import_react$2.useState(null); 846 const [serverCounts, setServerCounts] = import_react$2.useState(null); 847 const [serverCountsIdentity, setServerCountsIdentity] = import_react$2.useState(null); 848 const [serverGrandTotals, setServerGrandTotals] = import_react$2.useState(null); 849 const [cappedCountsRequest, setCappedCountsRequest] = import_react$2.useState(null); 850 const [laterPageFailed, setLaterPageFailed] = import_react$2.useState(false); 851 const [hydrationFailed, setHydrationFailed] = import_react$2.useState(false); 852 const [serverJobTitleCounts, setServerJobTitleCounts] = import_react$2.useState(null); 853 const [serverlessCursor, setServerlessCursor] = import_react$2.useState(null); 854 const [serverlessLoadedCount, setServerlessLoadedCount] = import_react$2.useState(0); 855 const serverlessLoadedCountRef = import_react$2.useRef(0); 856 const loadingMoreRef = import_react$2.useRef(false); 857 const emptyWindowLoadsRef = import_react$2.useRef({ 858 searchKey: "", 859 counts: {} 860 }); 861 const serverFilteredRef = import_react$2.useRef(false); 862 const pathEstablishedRef = import_react$2.useRef(false); 863 const pathFlipRestartsRef = import_react$2.useRef(0); 864 const [searchEpoch, setSearchEpoch] = import_react$2.useState(0); 865 const didMountFiltersRef = import_react$2.useRef(false); 866 const companyScopeKey = JSON.stringify({ 867 type, 868 filterParams 869 }); 870 const companyScopeKeyRef = import_react$2.useRef(companyScopeKey); 871 const searchVersionRef = import_react$2.useRef(0); 872 import_react$2.useEffect(() => { 873 const controller = new AbortController(); 874 requestControllerRef.current = controller; 875 return () => { 876 searchVersionRef.current += 1; 877 requestControllerRef.current?.abort(); 878 }; 879 }, []); 880 const completedSearchVersionRef = import_react$2.useRef(null); 881 const activeFetchesRef = import_react$2.useRef(0); 882 const pendingBatchesRef = import_react$2.useRef(0); 883 const hasMorePagesRef = import_react$2.useRef(false); 884 const [isSearchActive, setIsSearchActive] = import_react$2.useState(false); 885 const recomputeSearchActive = () => { 886 setIsSearchActive(activeFetchesRef.current > 0 || pendingBatchesRef.current > 0 || hasMorePagesRef.current); 887 }; 888 const [leadsLists, setLeadsLists] = import_react$2.useState(initialLeadsLists); 889 const [campaigns, setCampaigns] = import_react$2.useState(initialCampaigns); 890 const [tabSelected, setTabSelected] = import_react$2.useState("all_people"); 891 const locationsMappings = useLocationMappings(); 892 const { taxonomy: positionTaxonomy } = usePositionTaxonomy(); 893 const [filters, setFilters] = import_react$2.useState({ 894 requiredFields: [], 895 departments: [], 896 levels: seniorityFilterEnabled && !nineLevelSeniorityEnabled ? initialLevels ?? [] : [], 897 seniorityLevels: nineLevelSeniorityEnabled ? orderClassifiedSeniorityLevels(initialSeniorityLevels) : void 0, 898 jobTitles: initialJobTitles ?? [], 899 customJobTitleQueries: initialJobTitles ?? [], 900 verificationStatus: [], 901 hideAlreadySaved: true, 902 location: [] 903 }); 904 const filtersRef = import_react$2.useRef(filters); 905 filtersRef.current = filters; 906 const searchIdentityKey = buildSearchIdentityKey(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled); 907 const perCompanyLimit = peoplePerCompanyParam(filters); 908 const cappedCounts = useCappedPeopleCounts(serverFiltered && perCompanyLimit > 1 && cappedCountsRequest?.identity === JSON.stringify([ 909 companyScopeKey, 910 searchIdentityKey, 911 searchVersionRef.current 912 ]) ? JSON.stringify({ 913 ...cappedCountsRequest.body, 914 people_per_company: perCompanyLimit 915 }) : null, serverGrandTotals !== null); 916 const classifiedSeniorityKey = JSON.stringify(nineLevelSeniorityEnabled ? orderClassifiedSeniorityLevels(filters.seniorityLevels) : []); 917 const previousClassifiedSeniorityKeyRef = import_react$2.useRef(classifiedSeniorityKey); 918 const { clearPersistedFilters } = useMultiDomainSearchFiltersStorage({ 919 source, 920 filters, 921 setFilters, 922 skipRestore: !!initialJobTitles?.length || !!initialLevels?.length && seniorityFilterEnabled && !nineLevelSeniorityEnabled || !!initialSeniorityLevels?.length && nineLevelSeniorityEnabled, 923 seniorityFilterEnabled, 924 nineLevelSeniorityEnabled, 925 positionTaxonomy 926 }); 927 const handleClearFilters = import_react$2.useCallback(() => { 928 clearFilters(setFilters); 929 clearPersistedFilters(); 930 }, [clearPersistedFilters]); 931 const { results, setResults, revealingIds, revealedIds, revealedLeadsListId, operationId, lastRevealedIds, revealAllInFlight, filterRevealOutcome, handleReveal: baseHandleReveal, handleRevealMultiple: baseHandleRevealMultiple, handleRevealByFilters, handleSelect, clearFilterRevealOutcome, selectedIds, setSelectedIds } = useEmailSearch({ 932 source, 933 context: "multi_domain_search", 934 revealContext: `${searchEpoch}:${tabSelected}`, 935 userLookalikeId, 936 filterState: { 937 filters, 938 tab: tabSelected, 939 ...type === "companies" ? { company_filters: filterParams } : { domain_filters: filterParams } 940 } 941 }); 942 const signalsAnalytics = useSignalsSheetAnalytics(userLookalikeId); 943 const revealedIdSet = import_react$2.useMemo(() => { 944 const ids = /* @__PURE__ */ new Set(); 945 for (const result of results) if (result.revealed) ids.add(result.id); 946 return ids; 947 }, [results]); 948 const handleReveal = import_react$2.useCallback((id) => { 949 if (source === "discover" && queryType) App.analytics.posthog_capture("Contact revealed", { query_type: queryType }); 950 return signalsAnalytics.reveal(baseHandleReveal(id), 1, "single"); 951 }, [ 952 baseHandleReveal, 953 source, 954 queryType, 955 signalsAnalytics 956 ]); 957 const handleRevealMultiple = import_react$2.useCallback((ids, options) => { 958 const nonRevealedCount = ids.filter((id) => !revealedIdSet.has(id)).length; 959 if (source === "discover" && queryType && nonRevealedCount > 0) App.analytics.posthog_capture("Contact revealed", { 960 query_type: queryType, 961 contact_count: nonRevealedCount 962 }); 963 return signalsAnalytics.reveal(baseHandleRevealMultiple(ids, options), nonRevealedCount, "bulk", { 964 leadsListId: options?.leads_list_id, 965 savedCount: ids.length 966 }); 967 }, [ 968 baseHandleRevealMultiple, 969 source, 970 queryType, 971 revealedIdSet, 972 signalsAnalytics 973 ]); 974 const startNewSearch = import_react$2.useCallback(() => { 975 searchVersionRef.current += 1; 976 requestControllerRef.current?.abort(); 977 requestControllerRef.current = new AbortController(); 978 failedBatchesRef.current = []; 979 setSearchError(null); 980 completedSearchVersionRef.current = null; 981 loadingMoreRef.current = false;
982 setResults([]); 983 setDomains([]); 984 setServerCount(null); 985 setServerCounts(null); 986 setServerCountsIdentity(null); 987 setServerGrandTotals(null); 988 setLaterPageFailed(false); 989 setHydrationFailed(false); 990 setServerJobTitleCounts(null); 991 setServerlessCursor(null); 992 serverlessLoadedCountRef.current = 0; 993 emptyWindowLoadsRef.current = { 994 searchKey: "", 995 counts: {} 996 }; 997 setServerlessLoadedCount(0); 998 setNextSearchAfter(null); 999 pathEstablishedRef.current = false; 1000 setSearchEpoch((epoch) => epoch + 1); 1001 setSelectedIds([]); 1002 clearFilterRevealOutcome(); 1003 }, [ 1004 setResults, 1005 setSelectedIds, 1006 clearFilterRevealOutcome 1007 ]); 1008 const fetchEmailsFn = async ({ ids, version }) => { 1009 const flagHydrationFailure = () => { 1010 if (version !== searchVersionRef.current) return; 1011 failedBatchesRef.current.push({ 1012 ids, 1013 version 1014 }); 1015 setHydrationFailed(true); 1016 }; 1017 try { 1018 if (version !== searchVersionRef.current) return; 1019 const response = await fetchWithRetry(multi_domain_search_emails_search_index_path(), { 1020 method: "POST", 1021 headers: { 1022 "Content-Type": "application/json", 1023 "X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content 1024 }, 1025 body: JSON.stringify({ email_ids: ids }), 1026 signal: requestControllerRef.current?.signal 1027 }, RETRYABLE_SEARCH_STATUSES); 1028 if (!response.ok) { 1029 console.error(`Failed to fetch emails: ${response.status} ${response.statusText}`); 1030 flagHydrationFailure(); 1031 return; 1032 } 1033 const data = await response.json(); 1034 if (!data.emails || !Array.isArray(data.emails)) { 1035 console.error("Invalid response format: expected emails array"); 1036 flagHydrationFailure(); 1037 return; 1038 } 1039 if (version !== searchVersionRef.current) return; 1040 setResults((prev) => { 1041 const existingIds = new Set(prev.map((result) => result.id)); 1042 const newResults = data.emails.filter((email) => !existingIds.has(email.id)); 1043 return [...prev, ...newResults]; 1044 }); 1045 } catch (error) { 1046 if (version !== searchVersionRef.current) return; 1047 console.error("Error fetching emails:", error); 1048 flagHydrationFailure(); 1049 } finally { 1050 pendingBatchesRef.current -= 1; 1051 recomputeSearchActive(); 1052 } 1053 }; 1054 const queue = useAsyncQueuer(fetchEmailsFn, { 1055 concurrency: 10, 1056 wait: 10 1057 }); 1058 import_react$2.useEffect(() => { 1059 queue.start(); 1060 }, [queue]); 1061 const effectiveTotal = serverFiltered && serverCount != null ? serverCount : numberOfResults; 1062 const loadingTotal = serverFiltered ? Math.min(effectiveTotal, MAX_SERVERLESS_RESULTS) : effectiveTotal; 1063 const loadedResultsCount = serverFiltered ? serverlessLoadedCount : results.length; 1064 const loadingPercentage = import_react$2.useMemo(() => { 1065 if (loadingTotal <= 0) return null; 1066 return Math.min(99, Math.floor(loadedResultsCount / loadingTotal * 100)); 1067 }, [loadedResultsCount, loadingTotal]); 1068 const displayedLoadingPct = useAnimatedNumber(loadingPercentage); 1069 const fetchResults = async (fetchDomains, searchAfter = nextSearchAfter) => { 1070 activeFetchesRef.current += 1; 1071 recomputeSearchActive(); 1072 const batchSize = 1e4; 1073 const version = searchVersionRef.current; 1074 const sentSearchIdentityKey = buildSearchIdentityKey(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled); 1075 const body = { 1076 search_after: searchAfter, 1077 email_filters: buildEmailFilters(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled) 1078 }; 1079 const perCompany = peoplePerCompanyParam(filters); 1080 if (perCompany !== null) body.people_per_company = perCompany; 1081 if (fetchDomains && fetchDomains.length > 0) body.domain_params = { domains: fetchDomains.join("|") }; 1082 else if (type === "companies") body.companies_params = filterParams; 1083 else body.domain_params = filterParams; 1084 try { 1085 const response = await fetchWithRetry(multi_domain_search_search_index_path(), { 1086 method: "POST", 1087 body: JSON.stringify(body), 1088 headers: { 1089 "Content-Type": "application/json", 1090 "X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content 1091 }, 1092 signal: requestControllerRef.current?.signal 1093 }, RETRYABLE_SEARCH_STATUSES); 1094 if (!response.ok) throw new Error(`Search request failed: ${response.status} ${response.statusText}`); 1095 const data = await response.json(); 1096 if (version !== searchVersionRef.current) return; 1097 if (!Array.isArray(data?.email_ids)) throw new Error("Invalid response format: expected email_ids array"); 1098 if (buildSearchIdentityKey(filtersRef.current, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled) !== sentSearchIdentityKey) { 1099 startNewSearch(); 1100 return; 1101 } 1102 if (searchAfter != null) setLaterPageFailed(false); 1103 const pathBefore = serverFilteredRef.current; 1104 const pathWasEstablished = pathEstablishedRef.current; 1105 if (isNumber(data.count)) { 1106 serverFilteredRef.current = true; 1107 setServerFiltered(true); 1108 setServerCount(data.count); 1109 if (data.counts) { 1110 setServerCounts(data.counts); 1111 setServerCountsIdentity(sentSearchIdentityKey); 1112 setCappedCountsRequest(data.capped_counts_available ? { 1113 identity: JSON.stringify([ 1114 companyScopeKey, 1115 sentSearchIdentityKey, 1116 version 1117 ]), 1118 body 1119 } : null); 1120 } 1121 if (data.grand_totals) setServerGrandTotals(data.grand_totals); 1122 if (searchAfter == null && data.job_title_counts) setServerJobTitleCounts(data.job_title_counts); 1123 } else { 1124 serverFilteredRef.current = false;
1125 setServerFiltered(false); 1126 setServerCount(null); 1127 setServerJobTitleCounts(null); 1128 setServerCounts(null); 1129 setCappedCountsRequest(null); 1130 setServerCountsIdentity(null); 1131 setServerGrandTotals(null); 1132 } 1133 if (pathWasEstablished && pathBefore !== serverFilteredRef.current && pathFlipRestartsRef.current < 1) { 1134 pathFlipRestartsRef.current += 1; 1135 startNewSearch(); 1136 return; 1137 } 1138 pathEstablishedRef.current = true; 1139 let emailIds = data.email_ids; 1140 let nextServerlessCursor = data.next_search_after ?? null; 1141 if (serverFilteredRef.current) { 1142 const cappedPage = capServerlessPage(emailIds, serverlessLoadedCountRef.current, data.next_search_after); 1143 emailIds = cappedPage.emailIds; 1144 serverlessLoadedCountRef.current = cappedPage.loadedCount; 1145 setServerlessLoadedCount(cappedPage.loadedCount); 1146 nextServerlessCursor = cappedPage.nextCursor; 1147 } 1148 const fetchedDomains = data.domains || []; 1149 setDomains((prev) => [...prev, ...fetchedDomains]); 1150 for (let i = 0; i < emailIds.length; i += batchSize) { 1151 pendingBatchesRef.current += 1; 1152 queue.addItem({ 1153 ids: emailIds.slice(i, i + batchSize), 1154 version 1155 }); 1156 } 1157 const hasMore = !serverFilteredRef.current && data.next_search_after != null; 1158 hasMorePagesRef.current = hasMore; 1159 if (hasMore) setNextSearchAfter(data.next_search_after); 1160 if (serverFilteredRef.current) setServerlessCursor(nextServerlessCursor); 1161 } catch (err) { 1162 if (version !== searchVersionRef.current) return; 1163 setSearchError({ cursor: searchAfter }); 1164 if (searchAfter != null && version === searchVersionRef.current) setLaterPageFailed(true); 1165 hasMorePagesRef.current = false; 1166 } finally { 1167 activeFetchesRef.current -= 1; 1168 if (version === searchVersionRef.current) loadingMoreRef.current = false; 1169 recomputeSearchActive(); 1170 } 1171 }; 1172 import_react$2.useEffect(() => { 1173 fetchResults(); 1174 }, [nextSearchAfter, searchEpoch]); 1175 import_react$2.useEffect(() => { 1176 if (companyScopeKeyRef.current === companyScopeKey) return; 1177 companyScopeKeyRef.current = companyScopeKey; 1178 pathFlipRestartsRef.current = 0; 1179 startNewSearch(); 1180 }, [companyScopeKey, startNewSearch]); 1181 import_react$2.useEffect(() => { 1182 const classifiedSeniorityChanged = previousClassifiedSeniorityKeyRef.current !== classifiedSeniorityKey; 1183 previousClassifiedSeniorityKeyRef.current = classifiedSeniorityKey; 1184 if (!didMountFiltersRef.current) { 1185 didMountFiltersRef.current = true; 1186 return; 1187 } 1188 if (!serverFilteredRef.current && !classifiedSeniorityChanged) return; 1189 pathFlipRestartsRef.current = 0; 1190 startNewSearch(); 1191 }, [ 1192 searchIdentityKey, 1193 classifiedSeniorityKey, 1194 startNewSearch 1195 ]); 1196 const hasLoadingError = searchError !== null || hydrationFailed; 1197 const retryFailedRequests = () => { 1198 if (activeFetchesRef.current > 0 || pendingBatchesRef.current > 0) return; 1199 const batches = failedBatchesRef.current.splice(0); 1200 setHydrationFailed(false); 1201 setSearchError(null); 1202 for (const batch of batches) { 1203 pendingBatchesRef.current += 1; 1204 queue.addItem(batch); 1205 } 1206 if (searchError) fetchResults(void 0, searchError.cursor); 1207 recomputeSearchActive(); 1208 }; 1209 const handleCampaignsSave = signalsAnalytics.wrapCampaignsSave(useCampaignsSave(results, campaigns, setCampaigns, setResults)); 1210 const handleLeadsListSave = signalsAnalytics.wrapLeadsListSave(useLeadsListSave(results, setLeadsLists, setResults)); 1211 const deferredResults = (0, import_react$2.useDeferredValue)(results); 1212 const allPeopleListRef = import_react$2.useRef(null); 1213 const decisionMakersListRef = import_react$2.useRef(null); 1214 const genericListRef = import_react$2.useRef(null); 1215 const tabsRef = import_react$2.useRef(null); 1216 const footerRef = import_react$2.useRef(null); 1217 const [usesFlexLayout, setUsesFlexLayout] = import_react$2.useState(true); 1218 const [measuredTabsContentHeight, setMeasuredTabsContentHeight] = import_react$2.useState("100vh"); 1219 const measureTabsContentHeight = import_react$2.useCallback(() => { 1220 const tabsContentElement = allPeopleListRef.current || decisionMakersListRef.current || genericListRef.current; 1221 if (!tabsContentElement) return;
1222 if (tabsContentElement.closest(".sidepanel--flex-column, .opportunity-sidepanel, .lead-signals-sidepanel")) { 1223 setUsesFlexLayout(true); 1224 return; 1225 } 1226 setUsesFlexLayout(false); 1227 const windowHeight = window.innerHeight; 1228 const tabsContentTop = tabsContentElement.getBoundingClientRect().top; 1229 const footerHeight = footerRef.current ? footerRef.current.offsetHeight : 0; 1230 setMeasuredTabsContentHeight(`${Math.max(windowHeight - tabsContentTop - footerHeight - 2, 200)}px`); 1231 }, []); 1232 import_react$2.useEffect(() => { 1233 measureTabsContentHeight(); 1234 const handleResize = () => { 1235 measureTabsContentHeight(); 1236 }; 1237 window.addEventListener("resize", handleResize); 1238 const timeoutId = setTimeout(measureTabsContentHeight, 100); 1239 return () => { 1240 window.removeEventListener("resize", handleResize); 1241 clearTimeout(timeoutId); 1242 }; 1243 }, [ 1244 measureTabsContentHeight, 1245 filters, 1246 results, 1247 tabSelected 1248 ]); 1249 const flexRegionClassName = usesFlexLayout ? "sidepanel__flex-region" : void 0; 1250 const tabsContentClassName = usesFlexLayout ? "sidepanel__scroll-region" : void 0; 1251 const tabsContentStyle = usesFlexLayout ? void 0 : { 1252 height: measuredTabsContentHeight, 1253 overflow: "auto" 1254 }; 1255 const anyFiltersSet = filters.departments.length > 0 || (nineLevelSeniorityEnabled ? !!filters.seniorityLevels?.length : seniorityFilterEnabled && !!filters.levels?.length) || filters.jobTitles.length > 0 || filters.requiredFields.length > 0 || filters.hideAlreadySaved || filters.verificationStatus.length > 0 || filters.location.length > 0 || !!filters.peoplePerCompany || !!filters.sourceType; 1256 const filteredResults = import_react$2.useMemo(() => { 1257 return filterFunction(deferredResults, filters, locationsMappings, revealedIds, serverFiltered ? SERVER_HANDLED_FILTERS : [], positionTaxonomy, serverFiltered).sort(sortFunction); 1258 }, [ 1259 deferredResults, 1260 filters, 1261 locationsMappings, 1262 positionTaxonomy, 1263 revealedIds, 1264 serverFiltered 1265 ]); 1266 const { allPeopleResults, decisionMakersResults, genericResults } = useSplittedResults(filteredResults); 1267 const { allPeopleResults: allPeopleTotal, decisionMakersResults: decisionMakersTotal, genericResults: genericTotal } = useSplittedResults(deferredResults); 1268 const currentTabResults = tabSelected === "all_people" ? allPeopleResults : tabSelected === "decision_makers" ? decisionMakersResults : genericResults; 1269 const currentTabFiltered = currentTabResults.length; 1270 const currentTabTotal = tabSelected === "all_people" ? allPeopleTotal.length : tabSelected === "decision_makers" ? decisionMakersTotal.length : genericTotal.length; 1271 const [allSelected, setAllSelected] = import_react$2.useState(false); 1272 const currentTabIds = import_react$2.useMemo(() => currentTabResults.map((result) => result.id), [currentTabResults]); 1273 const visibleSelectedIds = import_react$2.useMemo(() => { 1274 if (selectedIds.length === 0) return []; 1275 const visibleIds = new Set(currentTabIds); 1276 return selectedIds.filter((id) => visibleIds.has(id)); 1277 }, [selectedIds, currentTabIds]); 1278 const allTabSelected = currentTabResults.length > 0 && visibleSelectedIds.length === currentTabResults.length; 1279 const someTabSelected = visibleSelectedIds.length > 0; 1280 const handleSelectAll = () => { 1281 setSelectedIds([]); 1282 setAllSelected(true); 1283 }; 1284 const handleDeselectAll = () => { 1285 setSelectedIds([]); 1286 setAllSelected(false); 1287 }; 1288 const handleSelectCount = (count) => { 1289 setSelectedIds(currentTabIds.slice(0, count)); 1290 setAllSelected(false); 1291 }; 1292 const handleRowSelect = import_react$2.useCallback((ids) => { 1293 if (!allSelected) { 1294 handleSelect(ids); 1295 return; 1296 } 1297 const removed = new Set(ids); 1298 setSelectedIds(currentTabIds.filter((id) => !removed.has(id))); 1299 setAllSelected(false); 1300 }, [ 1301 allSelected, 1302 currentTabIds, 1303 handleSelect, 1304 setSelectedIds 1305 ]); 1306 import_react$2.useEffect(() => { 1307 if (selectedIds.length > 0) setAllSelected(false); 1308 }, [selectedIds]); 1309 import_react$2.useEffect(() => { 1310 setAllSelected(false); 1311 }, [searchEpoch, tabSelected]); 1312 const { titles: clientJobTitles, counts: clientJobTitleCounts } = useJobTitleSuggestions(deferredResults, filters, locationsMappings, revealedIds, positionTaxonomy); 1313 const jobTitleCounts = (serverFiltered ? serverJobTitleCounts : null) ?? clientJobTitleCounts; 1314 const jobTitles = serverFiltered && serverJobTitleCounts !== null ? Object.keys(serverJobTitleCounts) : clientJobTitles; 1315 const deferredJobTitles = (0, import_react$2.useDeferredValue)(jobTitles); 1316 const deferredJobTitleCounts = (0, import_react$2.useDeferredValue)(jobTitleCounts); 1317 const allPeopleListVirtualizer = useVirtualizer({ 1318 count: allPeopleResults.length,
1319 getScrollElement: () => allPeopleListRef.current, 1320 estimateSize: import_react$2.useCallback(() => estimatedSize, []), 1321 enabled: tabSelected === "all_people" 1322 }); 1323 const decisionMakersListVirtualizer = useVirtualizer({ 1324 count: decisionMakersResults.length, 1325 getScrollElement: () => decisionMakersListRef.current, 1326 estimateSize: import_react$2.useCallback(() => estimatedSize, []), 1327 enabled: tabSelected === "decision_makers" 1328 }); 1329 const genericListVirtualizer = useVirtualizer({ 1330 count: genericResults.length, 1331 getScrollElement: () => genericListRef.current, 1332 estimateSize: import_react$2.useCallback(() => estimatedSize, []), 1333 enabled: tabSelected === "generic" 1334 }); 1335 const activeVirtualizer = tabSelected === "all_people" ? allPeopleListVirtualizer : tabSelected === "decision_makers" ? decisionMakersListVirtualizer : genericListVirtualizer; 1336 const loadMoreServerless = () => { 1337 if (hasLoadingError) return; 1338 if (!serverFilteredRef.current || loadingMoreRef.current || serverlessCursor == null) return; 1339 if (activeFetchesRef.current > 0) return; 1340 loadingMoreRef.current = true; 1341 fetchResults(void 0, serverlessCursor); 1342 }; 1343 const activeVirtualItems = activeVirtualizer.getVirtualItems(); 1344 import_react$2.useEffect(() => { 1345 const last = activeVirtualItems.at(-1); 1346 if (last && last.index >= currentTabFiltered - 8) loadMoreServerless(); 1347 }, [ 1348 activeVirtualItems, 1349 currentTabFiltered, 1350 serverlessCursor, 1351 hasLoadingError 1352 ]); 1353 import_react$2.useEffect(() => { 1354 if (!serverFiltered || !serverCounts || isSearchActive || hasLoadingError) return; 1355 const tabServerTotal = tabSelected === "all_people" ? serverCounts.all_people : tabSelected === "decision_makers" ? serverCounts.decision_makers : serverCounts.generic; 1356 if (currentTabFiltered === 0 && tabServerTotal > 0 && serverlessCursor != null) { 1357 if (emptyWindowLoadsRef.current.searchKey !== searchIdentityKey) emptyWindowLoadsRef.current = { 1358 searchKey: searchIdentityKey, 1359 counts: {} 1360 }; 1361 const used = emptyWindowLoadsRef.current.counts[tabSelected] ?? 0; 1362 if (used >= EMPTY_TAB_EXTRA_HYDRATIONS) return; 1363 emptyWindowLoadsRef.current.counts[tabSelected] = used + 1; 1364 loadMoreServerless(); 1365 } 1366 }, [ 1367 serverFiltered, 1368 serverCounts, 1369 tabSelected, 1370 currentTabFiltered, 1371 serverlessCursor, 1372 isSearchActive, 1373 searchIdentityKey, 1374 hasLoadingError 1375 ]); 1376 const serverTabRevealCount = serverFiltered && serverCounts ? tabSelected === "generic" ? serverCounts.generic_domains ?? serverCounts.generic : serverCounts[tabSelected] : null; 1377 const perCompanyExpressedByServer = !!serverCounts?.collapsed_per_company && serverCountsIdentity === searchIdentityKey && tabSelected !== "generic"; 1378 const hasActivePeoplePerCompanyFilter = !!filters.peoplePerCompany; 1379 const hasCappedPeopleCounts = cappedCounts !== null && tabSelected !== "generic"; 1380 const perCompanyNarrowsClientSide = hasActivePeoplePerCompanyFilter && !perCompanyExpressedByServer && !hasCappedPeopleCounts; 1381 const hasNarrowingClientFilters = !!filters.sourceType || perCompanyNarrowsClientSide; 1382 const perCompanyPricedExactly = perCompanyExpressedByServer && serverCounts?.collapsed_exact !== false; 1383 const revealHasNarrowingClientFilters = !!filters.sourceType || !!filters.peoplePerCompany && !perCompanyPricedExactly; 1384 const revealsWholeMatchSet = serverFiltered && visibleSelectedIds.length === 0 && !revealHasNarrowingClientFilters; 1385 const currentSearchVersion = searchVersionRef.current; 1386 const wholeMatchSetCurrentlyLoaded = serverFiltered && serverCount != null && serverlessCursor == null && !isSearchActive && !hydrationFailed && deferredResults === results && deferredResults.length >= serverCount; 1387 import_react$2.useEffect(() => { 1388 if (wholeMatchSetCurrentlyLoaded && currentSearchVersion === searchVersionRef.current) completedSearchVersionRef.current = currentSearchVersion; 1389 }, [wholeMatchSetCurrentlyLoaded, currentSearchVersion]); 1390 const wholeMatchSetLoaded = !hydrationFailed && (wholeMatchSetCurrentlyLoaded || completedSearchVersionRef.current === currentSearchVersion); 1391 const serverlessDisplayCounts = serverFiltered && !!serverCounts && wholeMatchSetLoaded && filters.hideAlreadySaved && !filters.sourceType && (!hasActivePeoplePerCompanyFilter || cappedCounts !== null) ? { 1392 all_people: allPeopleResults.length, 1393 decision_makers: decisionMakersResults.length, 1394 generic: hasActivePeoplePerCompanyFilter || serverCount === 0 && deferredResults.length === 0 ? serverCounts.generic : genericResults.length 1395 } : cappedCounts && serverCounts ? { 1396 ...serverCounts, 1397 ...cappedCounts.filtered 1398 } : serverCounts; 1399 const allPeopleCount = serverFiltered && serverlessDisplayCounts ? serverlessDisplayCounts.all_people : allPeopleResults.length; 1400 const decisionMakersCount = serverFiltered && serverlessDisplayCounts ? serverlessDisplayCounts.decision_makers : decisionMakersResults.length; 1401 const genericCount = serverFiltered && serverlessDisplayCounts ? serverlessDisplayCounts.generic : genericResults.length; 1402 import_react$2.useEffect(() => { 1403 if (tabSelected !== "all_people") return; 1404 if (serverFiltered) { 1405 if (!serverCounts) return; 1406 if (allPeopleCount === 0 && genericCount > 0) setTabSelected("generic"); 1407 return; 1408 } 1409 if (allPeopleResults.length === 0 && genericResults.length > 0) setTabSelected("generic"); 1410 }, [ 1411 tabSelected, 1412 serverFiltered, 1413 serverCounts, 1414 allPeopleCount, 1415 genericCount, 1416 allPeopleResults.length, 1417 genericResults.length 1418 ]); 1419 const tabDisplayTotal = tabSelected === "all_people" ? allPeopleCount : tabSelected === "decision_makers" ? decisionMakersCount : genericCount; 1420 const serverTabGrandTotal = hasCappedPeopleCounts ? cappedCounts.unfiltered?.[tabSelected] ?? null : serverGrandTotals ? serverGrandTotals[tabSelected] : null; 1421 const filteredCountNumerator = hasNarrowingClientFilters ? currentTabFiltered : tabDisplayTotal; 1422 const filteredCountBaseDenominator = hasNarrowingClientFilters ? tabDisplayTotal : serverTabGrandTotal; 1423 const cappedCountsIncludeSaved = hasCappedPeopleCounts && filters.hideAlreadySaved && !wholeMatchSetLoaded; 1424 const filteredCountPrefix = cappedCountsIncludeSaved ? "js.multi_domain_search.filters_applied_foun
1424d" : "js.multi_domain_search.filters_applied_showing"; 1425 const serverFilteredCountDenominator = filteredCountBaseDenominator === null ? null : Math.max(filteredCountNumerator, filteredCountBaseDenominator); 1426 const filteredCountTotal = currentTabTotal; 1427 const clientFiltersRemovedRows = currentTabTotal > currentTabFiltered; 1428 const showServerlessFilteredCountLine = serverFiltered && !!serverCounts && allowDiscoverPaging && (!hasNarrowingClientFilters || wholeMatchSetLoaded) && serverFilteredCountDenominator !== null && serverFilteredCountDenominator > 0 && !laterPageFailed && !hydrationFailed; 1429 const showSearchProgressLoader = isSearchActive && !showServerlessFilteredCountLine; 1430 const showLegacyFilteredCountLine = !serverFiltered && anyFiltersSet && filteredCountTotal > 0 && currentTabFiltered < filteredCountTotal && (currentTabFiltered > 0 || clientFiltersRemovedRows); 1431 const { revealAllOperationCost, missingCreditsVisual, numberOfEmailsToReveal, numberOfCreditsNeeded } = useRevealCost({ 1432 selectedIds: visibleSelectedIds, 1433 tabSelected, 1434 results, 1435 allPeopleResults, 1436 decisionMakersResults, 1437 genericResults, 1438 numberOfCreditsLeft, 1439 singleCreditsBucket: costEstimationContext.singleCreditsBucket, 1440 serverTabCount: revealsWholeMatchSet && !wholeMatchSetLoaded ? serverTabRevealCount : null 1441 }); 1442 const availableCreditsVisual = costEstimationContext.singleCreditsBucket ? numberOfCreditsLeft / CostCalculator.UNIFIED_CREDITS_BASE : numberOfCreditsLeft; 1443 const neededPlan = import_react$2.useMemo(() => { 1444 const plans = costEstimationContext.upgradePlans; 1445 return plans.find((p) => p.credits >= revealAllOperationCost)?.name || plans[plans.length - 1]?.name || ""; 1446 }, [revealAllOperationCost, costEstimationContext.upgradePlans]); 1447 const costEstimationData = import_react$2.useMemo(() => ({ 1448 singleCreditsBucket: costEstimationContext.singleCreditsBucket, 1449 creditsNeeded: revealAllOperationCost, 1450 availableCredits: availableCreditsVisual, 1451 creditsThatWillBeBought: 0, 1452 creditsThatWillBeBoughtPrice: 0, 1453 notCoveredCredits: missingCreditsVisual, 1454 searchesNeeded: revealAllOperationCost, 1455 availableSearchesWithOverage: availableCreditsVisual, 1456 outOfQuotaSearches: missingCreditsVisual, 1457 needVerifications: false, 1458 verificationsNeeded: 0, 1459 availableVerificationsWithOverage: 0, 1460 outOfQuotaVerifications: 0, 1461 showAlert: missingCreditsVisual > 0, 1462 currentPlan: costEstimationContext.currentPlan, 1463 neededPlan, 1464 hasAutoPurchase: costEstimationContext.hasAutoPurchase, 1465 numberOfEmailsToUncover: Math.max(0, numberOfEmailsToReveal - Math.floor(availableCreditsVisual)), 1466 neededAutoPurchaseLimit: costEstimationContext.currentAutoPurchaseMonthlyLimit + Math.ceil(missingCreditsVisual / 1e3) * costEstimationContext.creditPackPriceCents / 100, 1467 currency: costEstimationContext.currency 1468 }), [ 1469 revealAllOperationCost, 1470 missingCreditsVisual, 1471 numberOfEmailsToReveal, 1472 availableCreditsVisual, 1473 neededPlan, 1474 costEstimationContext 1475 ]); 1476 const [creditsModalOpen, setCreditsModalOpen] = import_react$2.useState(false); 1477 const pendingLeadsListId = import_react$2.useRef(); 1478 const revealByFiltersPendingRef = import_react$2.useRef(false); 1479 const getIdsForCurrentTab = import_react$2.useCallback(() => { 1480 return visibleSelectedIds.length > 0 ? visibleSelectedIds : currentTabIds; 1481 }, [visibleSelectedIds, currentTabIds]); 1482 const revealWithCreditCheck = (ids, options) => { 1483 const nonRevealedCount = ids.filter((id) => !revealedIdSet.has(id)).length; 1484 if (CostCalculator.calculate("search", nonRevealedCount, costEstimationContext.singleCreditsBucket) <= numberOfCreditsLeft) handleRevealMultiple(ids, { 1485 ...options, 1486 reveal_action: selectedIds.length > 0 ? "selected" : "all" 1487 }); 1488 else { 1489 revealByFiltersPendingRef.current = false; 1490 pendingLeadsListId.current = options?.leads_list_id; 1491 setCreditsModalOpen(true); 1492 } 1493 }; 1494 const revealAllByFilters = (options) => { 1495 const emailFilters = { 1496 ...buildEmailFilters(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled), 1497 hide_already_saved: filters.hideAlreadySaved, 1498 type: tabSelected === "generic" ? "generic" : "personal" 1499 }; 1500 if (tabSelected === "decision_makers") emailFilters.decision_maker = true; 1501 if (filters.sourceType) emailFilters.source_type = filters.sourceType; 1502 if (revealsWholeMatchSet && perCompanyLimit != null && perCompanyLimit >= 1) emailFilters.people_per_company = perCompanyLimit; 1503 const companyKey = type === "companies" ? "company_filters" : "domain_filters"; 1504 const revealingLoadedIds = getIdsForCurrentTab().filter((id) => !revealedIdSet.has(id)); 1505 return signalsAnalytics.reveal(handleRevealByFilters({ 1506 [companyKey]: filterParams, 1507 email_filters: emailFilters, 1508 credits_needed: options?.proceedWithAvailable ? numberOfCreditsLeft : numberOfCreditsNeeded, 1509 ...options?.leads_list_id != null ? { leads_list_id: options.leads_list_id } : {} 1510 }, revealingLoadedIds), revealingLoadedIds.length, "bulk", { 1511 leadsListId: options?.leads_list_id, 1512 savedCount: getIdsForCurrentTab().length 1513 }); 1514 }; 1515 const handleRevealAll = (options) => { 1516 if (!revealsWholeMatchSet) { 1517 revealWithCreditCheck(getIdsForCurrentTab(), options); 1518 return; 1519 } 1520 if (numberOfCreditsNeeded <= numberOfCreditsLeft) revealAllByFilters(options); 1521 else { 1522 revealByFiltersPendingRef.current = true; 1523 pendingLeadsListId.current = options?.leads_list_id; 1524 setCreditsModalOpen(true); 1525 } 1526 }; 1527 const handleProceedWithAvailable = () => { 1528 setCreditsModalOpen(false); 1529 if (revealByFiltersPendingRef.current) { 1530 revealByFiltersPendingRef.current = false;
1531 revealAllByFilters({ 1532 ...pendingLeadsListId.current != null && { leads_list_id: pendingLeadsListId.current }, 1533 proceedWithAvailable: true 1534 }); 1535 return; 1536 } 1537 const nonRevealed = getIdsForCurrentTab().filter((id) => !revealedIdSet.has(id)); 1538 const costPerSearch = CostCalculator.calculate("search", 1, costEstimationContext.singleCreditsBucket); 1539 const affordableCount = Math.floor(numberOfCreditsLeft / costPerSearch); 1540 const truncated = nonRevealed.slice(0, affordableCount); 1541 if (truncated.length > 0) { 1542 const opts = pendingLeadsListId.current ? { leads_list_id: pendingLeadsListId.current } : void 0; 1543 handleRevealMultiple(truncated, { 1544 ...opts, 1545 reveal_action: selectedIds.length > 0 ? "selected" : "all" 1546 }); 1547 } 1548 }; 1549 const handleGetMoreCredits = () => { 1550 setCreditsModalOpen(false); 1551 window.Turbo?.visit(edit_subscriptions_path()); 1552 }; 1553 const handleLeadManagerSave = async (name, listId) => { 1554 if (!leadsLists.find((list) => list.id === listId)) { 1555 setLeadsLists((prev) => [...prev, { 1556 id: listId, 1557 name 1558 }]); 1559 signalsAnalytics.markListCreated(listId); 1560 } 1561 handleRevealAll({ leads_list_id: listId }); 1562 }; 1563 const alreadyRevealedCount = import_react$2.useMemo(() => { 1564 if (visibleSelectedIds.length > 0) return visibleSelectedIds.filter((id) => revealedIdSet.has(id)).length; 1565 return currentTabResults.filter((result) => result.revealed).length; 1566 }, [ 1567 visibleSelectedIds, 1568 revealedIdSet, 1569 currentTabResults 1570 ]); 1571 const currentTabRows = tabSelected === "all_people" ? allPeopleTotal : tabSelected === "decision_makers" ? decisionMakersTotal : genericTotal; 1572 const alreadySavedCount = import_react$2.useMemo(() => currentTabRows.filter((result) => result.lead != null).length, [currentTabRows]); 1573 const saveHint = revealsWholeMatchSet && filters.hideAlreadySaved ? alreadySavedCount > 0 ? i18n.t("js.multi_domain_search.footer.saved_leads_excluded_hint") : void 0 : alreadyRevealedCount > 0 ? i18n.t("js.multi_domain_search.footer.existing_leads_hint", { 1574 count: alreadyRevealedCount, 1575 formatted_count: CurrencyHelper.numberWithDelimiter(alreadyRevealedCount) 1576 }) : void 0; 1577 const leadsListName = (id) => leadsLists.find((list) => list.id === id)?.name; 1578 const filterRevealOutcomeText = (outcome) => { 1579 const listName = leadsListName(outcome.leadsListId); 1580 const excluded = outcome.excludedAlreadySavedCount; 1581 const excludedArgs = { 1582 count: excluded, 1583 pretty_count: CurrencyHelper.numberWithDelimiter(excluded), 1584 listName 1585 }; 1586 const filterHint = i18n.t("js.multi_domain_search.footer.saved_leads_filter_hint"); 1587 const overlap = listName ? `${i18n.t("js.multi_domain_search.footer.already_saved_left_out_of_list", excludedArgs)} ${filterHint}` : i18n.t("js.multi_domain_search.footer.already_saved_left_out", excludedArgs); 1588 if (!outcome.revealedNothing) return overlap; 1589 const nothingReachedTheList = outcome.dispatchedCount === 0; 1590 const requestedArgs = { 1591 count: outcome.requestedCount, 1592 pretty_count: CurrencyHelper.numberWithDelimiter(outcome.requestedCount), 1593 listName 1594 }; 1595 if (listName && nothingReachedTheList && excluded > 0) return `${i18n.t("js.multi_domain_search.footer.no_new_contacts_in_list", requestedArgs)} ${filterHint}`; 1596 const nothingNew = i18n.t("js.multi_domain_search.footer.no_new_contacts", requestedArgs); 1597 return excluded > 0 && !nothingReachedTheList ? `${nothingNew} ${overlap}` : nothingNew; 1598 }; 1599 const outcomeLinkParams = (outcome) => ({ 1600 operation_id: outcome.operationId, 1601 ...outcome.leadsListId != null ? { leads_list_id: outcome.leadsListId } : {} 1602 }); 1603 const snackbarLinkParams = {}; 1604 if (operationId) snackbarLinkParams.operation_id = operationId; 1605 if (revealedLeadsListId) snackbarLinkParams.leads_list_id = revealedLeadsListId; 1606 if (lastRevealedIds.length === 1) { 1607 const matchedResult = results.find((emailResult) => lastRevealedIds.includes(emailResult.id)); 1608 if (matchedResult) snackbarLinkParams.email = matchedResult.email; 1609 } 1610 return /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(TooltipProvider, { children: [
1611 !hideHeader && /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("header", { 1612 className: "sidepanel__header mb-0", 1613 children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("h2", { 1614 className: "sidepanel__title", 1615 children: i18n.t("js.multi_domain_search.header") 1616 }) 1617 }), 1618 !allowDiscoverPaging && numberOfCompanies > 100 && /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SheetAlert, { children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(Alert, { 1619 variant: "info", 1620 size: "small", 1621 className: "py-6 px-8 gap-4 m-0 align-items-start", 1622 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(AlertIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("img", { 1623 src: upgrade_illustration_default, 1624 alt: "" 1625 }) }), /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(AlertContent, { children: [ 1626 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(AlertTitle, { 1627 className: "fw-medium", 1628 children: i18n.t("js.multi_domain_search.upgrade_from_free.title", { count: CurrencyHelper.numberWithDelimiter(effectiveTotal - results.length) }) 1629 }),
1630 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(AlertDescription, { children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("div", { dangerouslySetInnerHTML: { __html: i18n.t("js.multi_domain_search.upgrade_from_free.description_html", { count: CurrencyHelper.numberWithDelimiter(effectiveTotal - results.length) }) } }) }), 1631 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Button, { 1632 size: "sm", 1633 className: "mt-4", 1634 asChild: true, 1635 children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("a", { 1636 href: edit_subscriptions_path(), 1637 "data-turbo-frame": "_top", 1638 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { icon: "arrow-alt-circle-up" }), i18n.t("js.multi_domain_search.upgrade_from_free.cta")] 1639 }) 1640 }) 1641 ] })] 1642 }) }), 1643 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Filters, { 1644 nineLevelSeniorityEnabled, 1645 possibleFilters: [ 1646 "department", 1647 ...nineLevelSeniorityEnabled || seniorityFilterEnabled ? ["seniority"] : [], 1648 "job_title", 1649 "verification_status", 1650 "required_field", 1651 "location", 1652 "already_saved", 1653 "people_per_company", 1654 "source_type" 1655 ], 1656 filters, 1657 setFilters: (newFilters) => setFilters(newFilters), 1658 jobTitles: deferredJobTitles, 1659 jobTitleCounts: deferredJobTitleCounts, 1660 anyFiltersSet, 1661 clearFilters: handleClearFilters 1662 }), 1663 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SheetBody, { 1664 className: flexRegionClassName, 1665 children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabs, { 1666 value: tabSelected, 1667 ref: tabsRef, 1668 className: flexRegionClassName, 1669 children: [ 1670 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsList, { 1671 size: "md", 1672 fullWidth: true, 1673 children: [ 1674 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsTrigger, { 1675 value: "all_people", 1676 onClick: () => setTabSelected("all_people"), 1677 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(PillTabsTriggerName, { children: i18n.t("js.multi_domain_search.tabs.all_people") }
1677), /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsTriggerHint, { children: ["· ", CurrencyHelper.numberWithDelimiter(allPeopleCount)] })] 1678 }), 1679 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsTrigger, { 1680 value: "decision_makers", 1681 onClick: () => setTabSelected("decision_makers"), 1682 children: [ 1683 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { 1684 icon: "crown", 1685 size: "sm", 1686 className: "text-hint" 1687 }), 1688 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(PillTabsTriggerName, { children: i18n.t("js.multi_domain_search.tabs.decision_makers") }), 1689 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsTriggerHint, { children: ["· ", CurrencyHelper.numberWithDelimiter(decisionMakersCount)] }) 1690 ] 1691 }), 1692 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsTrigger, { 1693 value: "generic", 1694 onClick: () => setTabSelected("generic"), 1695 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(PillTabsTriggerName, { children: i18n.t("js.multi_domain_search.tabs.generic") }
1695), /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsTriggerHint, { children: ["· ", CurrencyHelper.numberWithDelimiter(genericCount)] })] 1696 }) 1697 ] 1698 }), 1699 hasLoadingError && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(Alert, { 1700 variant: "destructive", 1701 size: "small", 1702 className: "mt-4 mb-0 flex-shrink-0", 1703 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(AlertIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { icon: "exclamation-circle" }) }), /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(AlertContent, { children: [ 1704 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(AlertTitle, { children: i18n.t("js.multi_domain_search.loading_error.title") }), 1705 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(AlertDescription, { children: i18n.t(results.length > 0 ? "js.multi_domain_search.loading_error.partial_description" : "js.multi_domain_search.loading_error.description") }), 1706 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Button, { 1707 size: "sm", 1708 className: "mt-3", 1709 disabled: isSearchActive, 1710 onClick: retryFailedRequests, 1711 children: i18n.t("js.multi_domain_search.loading_error.retry") 1712 }) 1713 ] })] 1714 }), 1715 showSearchProgressLoader ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 1716 className: "d-flex align-items-center gap-2 text-xs text-hint mt-4", 1717 children: [ 1718 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { 1719 icon: "spinner-third", 1720 size: "sm", 1721 className: "fa-spin", 1722 "aria-hidden": true 1723 }), 1724 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("span", { children: [i18n.t("js.multi_domain_search.loading"), loadingPercentage !== null && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [ 1725 " · ", 1726 displayedLoadingPct, 1727 "%" 1728 ] })] }), 1729 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Separator, { 1730 orientation: "horizontal", 1731 variant: "light", 1732 decorative: true, 1733 className: "flex-grow-1" 1734 }) 1735 ] 1736 }) : serverFiltered ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 1737 className: "sidepanel__count-line d-flex align-items-center gap-2 text-xs text-hint mt-4", 1738 "data-testid": "filtered-count-line", 1739 "aria-live": "polite", 1740 "aria-atomic": "true", 1741 children: [ 1742 currentTabResults.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(MultiDomainSearchSelectAll, { 1743 checked: allSelected || allTabSelected, 1744 indeterminate: !allSelected && someTabSelected && !allTabSelected, 1745 selectAllCount: hasNarrowingClientFilters ? currentTabResults.length : tabDisplayTotal, 1746 onSelectAll: handleSelectAll, 1747 onSelectCount: handleSelectCount, 1748 onDeselectAll: handleDeselectAll 1749 }), 1750 (showServerlessFilteredCountLine || allSelected || visibleSelectedIds.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("span", { children: [visibleSelectedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("strong", { 1751 className: "text-xs-medium text-dark", 1752 children: i18n.t("js.multi_domain_search.bulk_actions.count_selected", { count: visibleSelectedIds.length }) 1753 }), showServerlessFilteredCountLine && " · "] }), showServerlessFilteredCountLine && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(import_jsx_runtime$2.Fragment, { children: [ 1754 visibleSelectedIds.length > 0 ? i18n.t(filteredCountPrefix, { filtered: CurrencyHelper.numberWithDelimiter(filteredCountNumerator) }) : /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("strong", { 1755 className: "text-xs-medium text-dark", 1756 children: i18n.t(filteredCountPrefix, { filtered: CurrencyHelper.numberWithDelimiter(filteredCountNumerator) }) 1757 }), 1758 " ", 1759 i18n.t(hasNarrowingClientFilters ? "js.multi_domain_search.filters_applied_suffix_matching" : cappedCountsIncludeSaved ? "js.multi_domain_search.filters_applied_suffix_including_saved" : "js.multi_domain_search.filters_applied_suffix", { total: CurrencyHelper.numberWithDelimiter(serverFilteredCountDenominator) }) 1760 ] })] }),
1761 isSearchActive && /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { 1762 icon: "spinner-third", 1763 size: "sm", 1764 className: "fa-spin", 1765 "aria-hidden": true 1766 }) 1767 ] 1768 }), /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Separator, { 1769 orientation: "horizontal", 1770 decorative: true, 1771 className: "multi-domain-search-count-divider" 1772 })] }) : showLegacyFilteredCountLine ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 1773 className: "d-flex align-items-center gap-2 text-xs text-hint mt-4", 1774 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("span", { children: [ 1775 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("strong", { 1776 className: "text-xs-medium text-dark", 1777 children: i18n.t("js.multi_domain_search.filters_applied_showing", { filtered: CurrencyHelper.numberWithDelimiter(currentTabFiltered) }) 1778 }), 1779 " ", 1780 i18n.t("js.multi_domain_search.filters_applied_suffix", { total: CurrencyHelper.numberWithDelimiter(filteredCountTotal) }) 1781 ] }
1781), /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Separator, { 1782 orientation: "horizontal", 1783 variant: "light", 1784 decorative: true, 1785 className: "flex-grow-1" 1786 })] 1787 }) : null, 1788 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(PillTabsContent, { 1789 value: "all_people", 1790 ref: allPeopleListRef, 1791 className: tabsContentClassName, 1792 style: tabsContentStyle, 1793 children: allPeopleResults.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(VirtualizedResults, { 1794 virtualItems: allPeopleListVirtualizer.getVirtualItems(), 1795 getTotalSize: () => allPeopleListVirtualizer.getTotalSize(), 1796 measureElement: allPeopleListVirtualizer.measureElement, 1797 results: allPeopleResults, 1798 domains, 1799 selectedIds, 1800 allSelected, 1801 revealingIds, 1802 leadsLists, 1803 folders, 1804 campaigns, 1805 onSelect: handleRowSelect, 1806 onReveal: handleReveal, 1807 onSaveEmail: handleLeadsListSave, 1808 onSaveCampaign: handleCampaignsSave, 1809 clickToCopy 1810 }) : isSearchActive || deferredResults !== results ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SkeletonRows, { count: 20 }) : hasLoadingError ? null : anyFiltersSet ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(EmptyState, { clearFilters: handleClearFilters }) : /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(EmptyState, {}) 1811 }), 1812 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(PillTabsContent, { 1813 value: "decision_makers", 1814 ref: decisionMakersListRef, 1815 className: tabsContentClassName, 1816 style: tabsContentStyle, 1817 children: decisionMakersResults.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(VirtualizedResults, { 1818 virtualItems: decisionMakersListVirtualizer.getVirtualItems(), 1819 getTotalSize: () => decisionMakersListVirtualizer.getTotalSize(), 1820 measureElement: decisionMakersListVirtualizer.measureElement, 1821 results: decisionMakersResults, 1822 domains, 1823 selectedIds, 1824 allSelected, 1825 revealingIds, 1826 leadsLists, 1827 folders, 1828 campaigns, 1829 onSelect: handleRowSelect, 1830 onReveal: handleReveal, 1831 onSaveEmail: handleLeadsListSave, 1832 onSaveCampaign: handleCampaignsSave, 1833 clickToCopy 1834 }) : isSearchActive || deferredResults !== results ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SkeletonRows, { count: 20 }) : hasLoadingError ? null : anyFiltersSet ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(EmptyState, { clearFilters: handleClearFilters }) : /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(EmptyState, {}) 1835 }), 1836 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(PillTabsContent, { 1837 value: "generic", 1838 ref: genericListRef, 1839 className: tabsContentClassName, 1840 style: tabsContentStyle, 1841 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(GenericEmailRevealAlert, { 1842 results: genericResults, 1843 numberOfCompanies, 1844 revealingIds, 1845 onReveal: handleReveal 1846 }), genericResults.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(VirtualizedResults, { 1847 virtualItems: genericListVirtualizer.getVirtualItems(), 1848 getTotalSize: () => genericListVirtualizer.getTotalSize(), 1849 measureElement: genericListVirtualizer.measureElement, 1850 results: genericResults, 1851 domains, 1852 selectedIds, 1853 allSelected, 1854 revealingIds, 1855 leadsLists, 1856 folders, 1857 campaigns, 1858 onSelect: handleRowSelect, 1859 onReveal: handleReveal, 1860 onSaveEmail: handleLeadsListSave, 1861 onSaveCampaign: handleCampaignsSave, 1862 clickToCopy 1863 }) : isSearchActive || deferredResults !== results ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SkeletonRows, { count: 20 }) : hasLoadingError ? null : anyFiltersSet ? /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(EmptyState, { clearFilters: handleClearFilters }) : /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(EmptyState, {})] 1864 }) 1865 ] 1866 }) 1867 }), 1868 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("footer", { 1869 ref: footerRef, 1870 className: "sidepanel__footer sidepanel__footer--sticky", 1871 children: [ 1872 lastRevealedIds.length >
1872 0 && !filterRevealOutcome && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(Snackbar, { children: [ 1873 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { 1874 icon: "check-circle", 1875 variant: "solid", 1876 size: "sm", 1877 className: "text-success" 1878 }), 1879 /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(SnackbarText, { children: [ 1880 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("strong", { 1881 className: "fw-medium", 1882 children: i18n.t("js.multi_domain_search.footer.leads", { 1883 count: lastRevealedIds.length, 1884 pretty_count: CurrencyHelper.numberWithDelimiter(lastRevealedIds.length) 1885 }) 1886 }), 1887 "\xA0", 1888 revealedLeadsListId ? i18n.t("js.multi_domain_search.footer.saved_in_list", { listName: leadsListName(revealedLeadsListId) }) : i18n.t("js.multi_domain_search.footer.saved") 1889 ] }), 1890 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SnackbarLink, { 1891 href: leads_path(snackbarLinkParams), 1892 turboFrame: "_top", 1893 children: i18n.t("js.multi_domain_search.footer.see_in_leads") 1894 }) 1895 ] }), 1896 filterRevealOutcome && /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)(Snackbar, { 1897 className: "snackbar--neutral", 1898 children: [ 1899 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { 1900 icon: "info-circle", 1901 size: "sm", 1902 "aria-hidden": "true" 1903 }), 1904 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SnackbarText, { children: filterRevealOutcomeText(filterRevealOutcome) }), 1905 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(SnackbarLink, { 1906 href: leads_path(outcomeLinkParams(filterRevealOutcome)), 1907 turboFrame: "_top", 1908 children: i18n.t("js.multi_domain_search.footer.see_in_leads") 1909 }) 1910 ] 1911 }),
1912 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(RevealSaveDropdown, { 1913 id: "multi-domain-search", 1914 label: revealingIds.length > 0 || revealAllInFlight ? i18n.t("js.multi_domain_search.footer.saving_leads") : i18n.t("js.multi_domain_search.footer.save_leads", { count: visibleSelectedIds.length }), 1915 cost: revealAllOperationCost, 1916 count: visibleSelectedIds.length, 1917 costIsEstimate: revealsWholeMatchSet, 1918 disabled: revealingIds.length > 0 || revealAllInFlight, 1919 onReveal: () => handleRevealAll(), 1920 onSave: handleLeadManagerSave, 1921 leadsLists, 1922 folders, 1923 hint: saveHint 1924 }), 1925 /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(CreditIndicator, { 1926 numberOfCreditsLeft: CostCalculator.calculateVisualRepresentation("search", numberOfCreditsLeft, costEstimationContext.singleCreditsBucket), 1927 missingCredits: missingCreditsVisual, 1928 costEstimationData, 1929 className: "ml-2" 1930 }) 1931 ] 1932 }) 1933 ] }), /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(CreditsOptionsModal, { 1934 open: creditsModalOpen, 1935 onClose: () => setCreditsModalOpen(false), 1936 onProceedWithAvailable: handleProceedWithAvailable, 1937 onGetMoreCredits: handleGetMoreCredits, 1938 costEstimationData, 1939 numberOfEmailsToReveal 1940 })] }); 1941} 1942function _temp(_, i) { 1943 return /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 1944 className: "d-flex flex-column", 1945 style: { 1946 gap: "var(--spacing-02)", 1947 padding: "var(--spacing-04) var(--spacing-02) var(--spacing-04) var(--spacing-06)", 1948 borderBottom: "1px solid var(--colors-neutral-300)" 1949 }, 1950 children: [/* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Skeleton, { 1951 size: "freeform", 1952 style: { 1953 height: "2.1rem", 1954 width: "70%" 1955 } 1956 }), /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Skeleton, { 1957 size: "freeform", 1958 style: { 1959 height: "2rem", 1960 width: "30%" 1961 } 1962 })] 1963 }, i); 1964} 1965var import_compiler_runtime$1, import_react$2, import_jsx_runtime$2, estimatedSize, MAX_SERVERLESS_RESULTS, EMPTY_TAB_EXTRA_HYDRATIONS, RETRYABLE_SEARCH_STATUSES, capServerlessPage, SERVER_HANDLED_FILTERS, peoplePerCompanyParam, buildSearchIdentityKey, buildEmailFilters, SkeletonRows, EmptyState; 1966function init_multi_domain_search() { 1967 return (init_multi_domain_search = __esmMin((() => { 1968 import_compiler_runtime$1 = require_compiler_runtime(); 1969 init_fetch_with_retry(); 1970 init_seniority(); 1971 import_react$2 = /* @__PURE__ */ __toESM(require_react()); 1972 init_upgrade_illustration(); 1973 init_currency_helper(); 1974 init_esm(); 1975 init_button(); 1976 init_snackbar(); 1977 init_icon(); 1978 init_credit_indicator(); 1979 init_costCalculator(); 1980 init_pill_tabs(); 1981 init_tooltip(); 1982 init_sheet(); 1983 init_filters(); 1984 init_reveal_save_dropdown(); 1985 init_generic_email_reveal_alert(); 1986 init_virtualized_results(); 1987 init_routes(); 1988 init_useAsyncQueuer(); 1989 init_useCampaignsSave(); 1990 init_useJobTitleSuggestions(); 1991 init_useCappedPeopleCounts(); 1992 init_useSplittedResults(); 1993 init_useLocationMappings(); 1994 init_skeleton(); 1995 init_separator(); 1996 init_useRevealCost(); 1997 init_useEmailSearch(); 1998 init_usePositionTaxonomy(); 1999 init_useLeadsListSave(); 2000 init_signals_analytics(); 2001 init_useMultiDomainSearchFiltersStorage(); 2002 init_alert(); 2003 init_credits_options_modal(); 2004 init_select_all_control(); 2005 init_useAnimatedNumber(); 2006 import_jsx_runtime$2 = require_jsx_runtime(); 2007 estimatedSize = 80; 2008 MAX_SERVERLESS_RESULTS = 25e4; 2009 EMPTY_TAB_EXTRA_HYDRATIONS = 1; 2010 RETRYABLE_SEARCH_STATUSES = [ 2011 408, 2012 502, 2013 503, 2014 504 2015 ]; 2016 capServerlessPage = (emailIds, loadedCount, nextCursor) => { 2017 const remaining = Math.max(0, MAX_SERVERLESS_RESULTS - loadedCount); 2018 const acceptedEmailIds = emailIds.slice(0, remaining); 2019 const nextLoadedCount = loadedCount + acceptedEmailIds.length; 2020 return { 2021 emailIds: acceptedEmailIds, 2022 loadedCount: nextLoadedCount, 2023 nextCursor: nextLoadedCount >= MAX_SERVERLESS_RESULTS ? null : nextCursor ?? null 2024 }; 2025 }; 2026 SERVER_HANDLED_FILTERS = [ 2027 "department", 2028 "seniority", 2029 "job_title", 2030 "verification_status", 2031 "required_field", 2032 "location" 2033 ]; 2034 peoplePerCompanyParam = (filters) => { 2035 const perCompany = filters.peoplePerCompany; 2036 if (perCompany?.type !== "specific_number") return null; 2037 const value = perCompany.specificNumber; 2038 return Number.isInteger(value) ? value : null; 2039 }; 2040 buildSearchIdentityKey = (filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled) => JSON.stringify([buildEmailFilters(filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled), peoplePerCompanyParam(filters)]); 2041 buildEmailFilters = (filters, seniorityFilterEnabled, positionTaxonomy, nineLevelSeniorityEnabled) => { 2042 const customQueries = new Set(filters.customJobTitleQueries ?? []); 2043 const selectedTitles = Array.from(new Set(filters.jobTitles ?? [])); 2044 return { 2045 departments: filters.departments, 2046 ...seniorityFilterEnabled && !nineLevelSeniorityEnabled && filters.levels?.length ? { levels: orderSeniorityLevels(filters.levels, positionTaxonomy) } : {}, 2047 ...nineLevelSeniorityEnabled && filters.seniorityLevels?.length ? { seniority_levels: orderClassifiedSeniorityLevels(filters.seniorityLevels) } : {}, 2048 exactJobTitles: selectedTitles.filter((title) => !customQueries.has(title)), 2049 jobTitles: selectedTitles.filter((title) => customQueries.has(title)), 2050 verificationStatus: filters.verificationStatus, 2051 requiredFields: filters.requiredFields, 2052 location: filters.location 2053 }; 2054 }; 2055 SkeletonRows = (t0) => { 2056 const $ = (0, import_compiler_runtime$1.c)(2); 2057 const { count } = t0; 2058 let t1; 2059 if ($[0] !== count) { 2060 t1 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(import_jsx_runtime$2.Fragment, { children: Array.from({ length: count }, _temp) }); 2061 $[0] = count; 2062 $[1] = t1; 2063 } else t1 = $[1]; 2064 return t1; 2065 }; 2066 EmptyState = (t0) => { 2067 const $ = (0, import_compiler_runtime$1.c)(6); 2068 const { clearFilters } = t0; 2069 let t1; 2070 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 2071 t1 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Icon, { 2072 icon: "info-circle", 2073 className: "text-hint" 2074 }); 2075 $[0] = t1; 2076 } else t1 = $[0]; 2077 let t2; 2078 if ($[1] === Symbol.for("react.memo_cache_sentinel")) { 2079 t2 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)("span", { 2080 className: "text-body-secondary", 2081 children: i18n.t("js.multi_domain_search.empty_state.text") 2082 }); 2083 $[1] = t2; 2084 } else t2 = $[1]; 2085 let t3; 2086 if ($[2] !== clearFilters) {
2087 t3 = clearFilters && /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(Button, { 2088 variant: "linkGrey", 2089 className: "fw-medium", 2090 onClick: clearFilters, 2091 children: i18n.t("js.multi_domain_search.empty_state.clear_filters") 2092 }); 2093 $[2] = clearFilters; 2094 $[3] = t3; 2095 } else t3 = $[3]; 2096 let t4; 2097 if ($[4] !== t3) { 2098 t4 = /* @__PURE__ */ (0, import_jsx_runtime$2.jsxs)("div", { 2099 className: "d-flex flex-column gap-2 justify-content-center align-items-center mt-16", 2100 children: [ 2101 t1, 2102 t2, 2103 t3 2104 ] 2105 }); 2106 $[4] = t3; 2107 $[5] = t4; 2108 } else t4 = $[5]; 2109 return t4; 2110 }; 2111 })))(); 2112} 2113//#endregion 2114//#region app/frontend/react/components/company-lists-context.tsx 2115var import_compiler_runtime, import_react$1, import_jsx_runtime$1, CompanyListsContext, CompanyListsProvider, useCompanyLists; 2116function init_company_lists_context() { 2117 return (init_company_lists_context = __esmMin((() => { 2118 import_compiler_runtime = require_compiler_runtime(); 2119 import_react$1 = require_react(); 2120 import_jsx_runtime$1 = require_jsx_runtime(); 2121 CompanyListsContext = (0, import_react$1.createContext)(void 0); 2122 CompanyListsProvider = (t0) => { 2123 const $ = (0, import_compiler_runtime.c)(5); 2124 const { initialCompanyLists, children } = t0; 2125 const [companyLists, setCompanyLists] = (0, import_react$1.useState)(initialCompanyLists); 2126 let t1; 2127 if ($[0] !== companyLists) { 2128 t1 = { 2129 companyLists, 2130 setCompanyLists 2131 }; 2132 $[0] = companyLists; 2133 $[1] = t1; 2134 } else t1 = $[1]; 2135 let t2; 2136 if ($[2] !== children || $[3] !== t1) { 2137 t2 = /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CompanyListsContext.Provider, { 2138 value: t1, 2139 children 2140 }); 2141 $[2] = children; 2142 $[3] = t1; 2143 $[4] = t2; 2144 } else t2 = $[4]; 2145 return t2; 2146 }; 2147 useCompanyLists = () => { 2148 const context = (0, import_react$1.useContext)(CompanyListsContext); 2149 if (context === void 0) throw new Error("useCompanyLists must be used within a CompanyListsProvider"); 2150 return context; 2151 }; 2152 })))(); 2153} 2154//#endregion 2155//#region app/frontend/lib/company-api.ts 2156async function createCompanyList(name) { 2157 const response = await fetch("/lead/company-list", { 2158 method: "POST", 2159 headers: { 2160 "Content-Type": "application/json", 2161 Accept: "application/json", 2162 "X-CSRF-Token": getCsrfToken() 2163 }, 2164 body: JSON.stringify({ lead_company_list: { name } }), 2165 redirect: "manual" 2166 }); 2167 if (!response.ok && response.status !== 302) return null; 2168 if (response.status === 302 || response.status === 301) { 2169 const match = response.headers.get("location")?.match(/company-lists\/(\d+)/); 2170 if (match) return { 2171 id: parseInt(match[1], 10), 2172 name 2173 }; 2174 } 2175 try { 2176 if (response.headers.get("content-type")?.includes("application/json")) { 2177 const json = await response.json(); 2178 return json.data || { 2179 id: json.id, 2180 name: json.name 2181 }; 2182 } 2183 } catch {} 2184 return null; 2185} 2186async function saveCompanies(domainIds, companyListId) { 2187 const response = await fetch("/search/companies/save", { 2188 method: "POST", 2189 headers: { 2190 "Content-Type": "application/json", 2191 Accept: "application/json", 2192 "X-CSRF-Token": getCsrfToken() 2193 }, 2194 body: JSON.stringify({ 2195 domain_ids: domainIds, 2196 ...companyListId && { company_list_id: companyListId } 2197 }) 2198 }); 2199 if (!response.ok) { 2200 const data = await response.json().catch(() => ({})); 2201 throw new Error(data.error || "Failed to save companies"); 2202 } 2203} 2204async function saveCompany(domainId, companyListId) { 2205 return saveCompanies([domainId], companyListId); 2206} 2207function init_company_api() { 2208 return (init_company_api = __esmMin((() => { 2209 init_utils(); 2210 })))(); 2211} 2212//#endregion 2213//#region app/frontend/react/components/discover/save-companies-dropdown.tsx 2214function SaveCompaniesDropdown({ numberOfDomains, domains, filters, selectAll = false, saveCompaniesLimit = null, onSaved, onOpen, size = "sm", variant, disabled = false }) { 2215 const { companyLists, setCompanyLists } = useCompanyLists(); 2216 const [isSaving, setIsSaving] = (0, import_react.useState)(false); 2217 const [searchQuery, setSearchQuery] = (0, import_react.useState)(""); 2218 const [isConfirmationOpen, setIsConfirmationOpen] = (0, import_react.useState)(false); 2219 const [isFreePlanCapOpen, setIsFreePlanCapOpen] = (0, import_react.useState)(false); 2220 const [pendingSaveAction, setPendingSaveAction] = (0, import_react.useState)(null); 2221 const searchInputRef = (0, import_react.useRef)(null); 2222 const dropdownRef = (0, import_react.useRef)(null); 2223 const [isOpen, setIsOpen] = (0, import_react.useState)(false); 2224 const domainIds = (0, import_react.useMemo)(() => domains.map((domain) =>
2224 domain.id), [domains]); 2225 const sortedLists = (0, import_react.useMemo)(() => [...companyLists].sort((a, b) => a.name.localeCompare(b.name)), [companyLists]); 2226 const filteredLists = (0, import_react.useMemo)(() => searchQuery ? sortedLists.filter((list) => list.name.toLowerCase().includes(searchQuery.toLowerCase())) : sortedLists, [sortedLists, searchQuery]); 2227 const hasExactMatch = (0, import_react.useMemo)(() => companyLists.some((list_0) => list_0.name.toLowerCase() === searchQuery.trim().toLowerCase()), [companyLists, searchQuery]); 2228 (0, import_react.useEffect)(() => { 2229 if (!isOpen) { 2230 setSearchQuery(""); 2231 return; 2232 } 2233 onOpen?.(); 2234 const timer = setTimeout(() => searchInputRef.current?.focus(), 100); 2235 return () => clearTimeout(timer); 2236 }, [isOpen, onOpen]); 2237 const isCappedByFreePlan = selectAll && saveCompaniesLimit != null && saveCompaniesLimit > 0 && numberOfDomains > saveCompaniesLimit; 2238 const upgradeHref = isCappedByFreePlan ? checkout_upgrade_plan_path({ 2239 needed_credits: 1, 2240 needed_level: 1, 2241 operation: "discover", 2242 direct_route: true, 2243 format: "turbo_stream" 2244 }) : void 0; 2245 const closeConfirmationDialog = (0, import_react.useCallback)(() => { 2246 setIsConfirmationOpen(false); 2247 setPendingSaveAction(null); 2248 }, []); 2249 const closeFreePlanCap = (0, import_react.useCallback)(() => { 2250 setIsFreePlanCapOpen(false); 2251 setPendingSaveAction(null); 2252 }, []); 2253 const { dialogRef, handleBackdropClick, stopPropagation } = useDialog({ 2254 isOpen: isConfirmationOpen, 2255 onClose: closeConfirmationDialog 2256 }); 2257 const { dialogRef: freePlanCapDialogRef, handleBackdropClick: handleFreePlanCapBackdropClick, stopPropagation: freePlanCapStopPropagation } = useDialog({ 2258 isOpen: isFreePlanCapOpen, 2259 onClose: closeFreePlanCap 2260 }); 2261 const queueSaveAction = (action) => { 2262 if (isCappedByFreePlan) { 2263 App.analytics.posthog_capture("discover-save-companies-free-cap-shown", { 2264 company_count: numberOfDomains, 2265 limit: saveCompaniesLimit 2266 }); 2267 setPendingSaveAction(() => action); 2268 setIsFreePlanCapOpen(true); 2269 return; 2270 } 2271 if (numberOfDomains <= 500) { 2272 action(); 2273 return; 2274 } 2275 setPendingSaveAction(() => action); 2276 setIsConfirmationOpen(true); 2277 }; 2278 const confirmSaveAction = async () => { 2279 const action_0 = pendingSaveAction; 2280 closeConfirmationDialog(); 2281 await action_0?.(); 2282 }; 2283 const confirmFreePlanCapSave = async () => { 2284 const action_1 = pendingSaveAction; 2285 closeFreePlanCap(); 2286 await action_1?.(); 2287 }; 2288 const saveCompanies = async (listId, listName, listCreated = false) => { 2289 setIsSaving(true); 2290 try { 2291 const body = selectAll ? { 2292 filter_params: filters, 2293 select_all: true, 2294 ...saveCompaniesLimit != null && { limit: saveCompaniesLimit }, 2295 ...listId && { company_list_id: listId } 2296 } : { 2297 domain_ids: domainIds, 2298 ...listId && { company_list_id: listId } 2299 }; 2300 const response = await fetch("/search/companies/save", { 2301 method: "POST", 2302 headers: { 2303 "Content-Type": "application/json", 2304 "X-CSRF-Token": getCsrfToken() 2305 }, 2306 body: JSON.stringify(body) 2307 }); 2308 if (!response.ok) { 2309 const error_0 = await response.json(); 2310 throw new Error(error_0.error || t("js.discover.save_companies.flash.error")); 2311 } 2312 const data = await response.json().catch(() => ({})); 2313 const reportedCount = isNumber(data.count) ? data.count : numberOfDomains; 2314 const savedCount = Math.min(reportedCount, numberOfDomains); 2315 onSaved?.(selectAll ? [] : domainIds, { 2316 listId, 2317 listName, 2318 listCreated 2319 }); 2320 if (numberOfDomains > 100) App.displayFlash("pending", t("js.discover.save_companies.flash.saving_in_progress", { count: savedCount })); 2321 else if (listId && listName) App.displayFlash("success", t("js.discover.save_companies.flash.success_html", { 2322 count: savedCount, 2323 name: listName, 2324 url: company_list_path({ id: listId }) 2325 })); 2326 else App.displayFlash("success", t("js.discover.save_companies.flash.success_no_list", { count: savedCount })); 2327 } catch (error) { 2328 App.displayFlash("error", error instanceof Error ? error.message : t("js.discover.save_companies.flash.error")); 2329 } finally { 2330 setIsSaving(false); 2331 } 2332 }; 2333 const handleCreateAndSave = async (name) => { 2334 if (!name) return; 2335 setIsSaving(true); 2336 try { 2337 const newList = await createCompanyList(name); 2338 if (!newList) {
2339 App.displayFlash("error", t("js.discover.company_save_manager.errors.failed_to_create_list")); 2340 setIsSaving(false); 2341 return; 2342 } 2343 setCompanyLists((prev) => [...prev, newList]); 2344 await saveCompanies(newList.id, newList.name, true); 2345 } catch { 2346 App.displayFlash("error", t("js.discover.save_companies.flash.error")); 2347 setIsSaving(false); 2348 } 2349 }; 2350 return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu, { 2351 ref: dropdownRef, 2352 open: isOpen, 2353 onOpenChange: setIsOpen, 2354 children: [ 2355 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuTrigger, { 2356 asChild: true, 2357 children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, { 2358 size, 2359 variant, 2360 disabled: isSaving || disabled, 2361 children: [ 2362 isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { 2363 loading: true, 2364 size: "sm" 2365 }), 2366 isSaving ? selectAll ? t("js.discover.index.saving_all_companies") : t("js.discover.index.saving_n_companies", { 2367 count: numberOfDomains, 2368 formatted_count: CurrencyHelper.numberWithDelimiter(numberOfDomains) 2369 }) : selectAll ? t("js.discover.index.save_all_companies") : t("js.discover.index.save_n_companies", { 2370 count: numberOfDomains, 2371 formatted_count: CurrencyHelper.numberWithDelimiter(numberOfDomains) 2372 }), 2373 !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { 2374 icon: "chevron-down", 2375 size: "xs", 2376 "aria-hidden": "true" 2377 }) 2378 ] 2379 }) 2380 }), 2381 /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuContent, { 2382 className: "save-companies-dropdown", 2383 children: [ 2384 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuItem, { 2385 className: "dropdown-search dropdown-search--alternative", 2386 children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownSearchInput, { 2387 ref: searchInputRef, 2388 className: "dropdown-search__input--alternative", 2389 placeholder: companyLists.length === 0 ? t("js.discover.save_companies.type_to_create") : t("js.discover.save_companies.search_or_create"), 2390 value: searchQuery, 2391 onChange: (e) => setSearchQuery(e.target.value) 2392 }) 2393 }), 2394 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuTitle, { 2395 small: true, 2396 children: t("js.discover.save_companies.add_to_a_list") 2397 }), 2398 /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { 2399 className: "save-companies-dropdown__list", 2400 children: [ 2401 filteredLists.length === 0 && !searchQuery.trim() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { 2402 className: "save-companies-dropdown__empty", 2403 children: t("js.discover.save_companies.empty") 2404 }), 2405 filteredLists.map((list_1) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, { 2406 onClick: () => queueSaveAction(() => saveCompanies(list_1.id, list_1.name)), 2407 closeOnClick: true, 2408 small: true, 2409 className: "save-companies-dropdown__list-item", 2410 children: [ 2411 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { 2412 icon: "diamond", 2413 size: "xs", 2414 "aria-hidden": "true" 2415 }), 2416 /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { 2417 className: "save-companies-dropdown__list-item-name", 2418 children: list_1.name 2419 }), 2420 list_1.companiesCount != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { 2421 className: "dropdown-item__count", 2422 children: CurrencyHelper.numberWithDelimiter(list_1.companiesCount) 2423 }) 2424 ] 2425 }, list_1.id)), 2426 searchQuery.trim() && !hasExactMatch && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, { 2427 onClick: () => { 2428 const name_0 = searchQuery.trim(); 2429 queueSaveAction(() => handleCreateAndSave(name_0)); 2430 }, 2431 closeOnClick: true, 2432 small: true, 2433 children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { 2434 icon: "plus", 2435 size: "xs", 2436 "aria-hidden": "true" 2437 }), t("js.discover.save_companies.create_list_and_save", { name: searchQuery.trim() })] 2438 }) 2439 ] 2440 }),
2441 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuDivider, {}), 2442 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuItem, { 2443 onClick: () => queueSaveAction(() => saveCompanies()), 2444 closeOnClick: true, 2445 small: true, 2446 children: t("js.discover.save_companies.save_without_list") 2447 }) 2448 ] 2449 }), 2450 isConfirmationOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog, { 2451 ref: dialogRef, 2452 onClick: handleBackdropClick, 2453 onPointerDown: stopPropagation, 2454 onMouseDown: stopPropagation, 2455 children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, { children: [ 2456 /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: t("js.discover.save_companies.confirmation.title", { 2457 count: numberOfDomains, 2458 formatted_count: CurrencyHelper.numberWithDelimiter(numberOfDomains) 2459 }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogCloseButton, { onClick: closeConfirmationDialog })] }), 2460 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogBody, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("js.discover.save_companies.confirmation.description", { 2461 count: numberOfDomains, 2462 formatted_count: CurrencyHelper.numberWithDelimiter(numberOfDomains) 2463 }) }) }), 2464 /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { 2465 variant: "ghost", 2466 onClick: closeConfirmationDialog, 2467 children: t("js.discover.save_companies.confirmation.cancel") 2468 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { 2469 variant: "primary", 2470 onClick: () => void confirmSaveAction(), 2471 children: t("js.discover.save_companies.confirmation.confirm") 2472 })] }) 2473 ] }) 2474 }), 2475 isFreePlanCapOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog, { 2476 ref: freePlanCapDialogRef, 2477 onClick: handleFreePlanCapBackdropClick, 2478 onPointerDown: freePlanCapStopPropagation, 2479 onMouseDown: freePlanCapStopPropagation, 2480 children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, { children: [ 2481 /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: t("js.discover.save_companies.free_plan_cap.title") }
2481), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogCloseButton, { onClick: closeFreePlanCap })] }), 2482 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogBody, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("js.discover.save_companies.free_plan_cap.description", { 2483 count: saveCompaniesLimit, 2484 total: CurrencyHelper.numberWithDelimiter(numberOfDomains) 2485 }) }) }), 2486 /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { 2487 variant: "default", 2488 onClick: () => void confirmFreePlanCapSave(), 2489 children: t("js.discover.save_companies.free_plan_cap.save_first", { count: saveCompaniesLimit }) 2490 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { 2491 asChild: true, 2492 variant: "primary", 2493 children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { 2494 href: upgradeHref, 2495 "data-turbo-frame": "checkout-modal", 2496 "data-turbo-prefetch": "false", 2497 onClick: () => { 2498 closeFreePlanCap(); 2499 App.analytics.posthog_capture("discover-save-companies-upgrade-clicked", { 2500 company_count: numberOfDomains, 2501 limit: saveCompaniesLimit 2502 }); 2503 }, 2504 children: t("js.discover.save_companies.free_plan_cap.upgrade") 2505 }) 2506 })] }) 2507 ] }) 2508 }) 2509 ] 2510 }); 2511} 2512var import_react, import_jsx_runtime, t; 2513function init_save_companies_dropdown() { 2514 return (init_save_companies_dropdown = __esmMin((() => { 2515 import_react = require_react(); 2516 init_useDialog(); 2517 init_routes(); 2518 init_currency_helper(); 2519 init_button(); 2520 init_icon(); 2521 init_dialog(); 2522 init_dropdown_menu(); 2523 init_company_lists_context(); 2524 init_company_api(); 2525 init_utils(); 2526 import_jsx_runtime = require_jsx_runtime(); 2527 t = (key, options) => window.i18n.t(key, options); 2528 })))(); 2529} 2530//#endregion 2531export { useRevealCost as _, saveCompany as a, useCompanyLists as c, SIGNAL_SOURCE as d, ageHours as f, init_useRevealCost as g, useSignalsSheetAnalytics as h, init_company_api as i, MultiDomainSearchLegacy as l, init_signals_analytics as m, init_save_companies_dropdown as n, CompanyListsProvider as o, captureSignalsEvent as p, createCompanyList as r, init_company_lists_context as s, SaveCompaniesDropdown as t, init_multi_domain_search as u, GenericEmailRevealAlert as v, init_generic_email_reveal_alert as y };
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.