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] = "155766dd-e8a1-41b7-81dc-82b8437ec47f", e._sentryDebugIdIdentifier = "sentry-dbid-155766dd-e8a1-41b7-81dc-82b8437ec47f"); 7 } catch (e) {} 8})(); 9import { a as __toESM, n as __esmMin } from "./rolldown-runtime-sLwMD6dS.js"; 10import { $t as CardTitle, As as Input, At as stopMenuTypeahead, Ba as new_leads_import_path, Bi as leads_bulk_path, Co as sequences_inbox_index_path, Dr as dashboard_grouped_signal_details_path, Eo as sequences_path, Er as dashboard_grouped_lead_signals_path, Et as DropdownMenuTrigger, Ft as TabsContent, Ii as lead_path, It as TabsList, Ka as onboarding_steps_path, Lt as init_tabs, M as init_currency_helper, Mi as init_routes, Mr as dashboard_seen_lead_signals_path, Nr as dashboard_seen_opportunities_path, Nt as Tab, Or as dashboard_lead_signal_preferences_path, Os as Checkbox, Pr as dashboard_signal_details_path, Pt as Tabs, Qt as CardContent, Ra as new_companies_import_path, So as sequences_email_accounts_center_path, Tr as dashboard_first_signal_path, Ui as leads_companies_people_path, Xt as init_label, Yr as discovers_path, Yt as Label, Zn as checkout_upgrade_plan_path, Zt as Card, _t as DropdownMenuHint, at as AlertDescription, bt as DropdownMenuItem, dn as formatCompanySizeRangeLabel, en as CardTitleActions, et as Skeleton, ft as DropdownMenu, gt as DropdownMenuHeader, h as init_costCalculator, ht as DropdownMenuDivider, it as AlertContent, j as CurrencyHelper, ja as multi_domain_search_emails_search_index_path, jr as dashboard_seen_grouped_lead_signals_path, js as init_input, kr as dashboard_lead_signals_path, ks as init_checkbox, kt as init_dropdown_menu, lt as init_alert, m as CostCalculator, ma as leads_path, mt as DropdownMenuContent, nn as init_card, nt as Alert, pn as init_text_helpers, st as AlertIcon, ti as edit_user_registration_path, tn as CardTitleSuffix, tt as init_skeleton, un as formatApproximateNumber, va as locations_path, vi as email_domains_health_reports_path, vt as DropdownMenuInlineItem, wr as dashboard_announcement_path, wt as DropdownMenuSubmit } from "./controllers-jdPQjaCB.js"; 11import { a as isNumber, o as isObject, s as isString, t as dictLookup } from "./type-guards-DVyG5ljW.js"; 12import { a as cn, i as require_compiler_runtime, l as getCsrfToken, n as cva, r as init_dist, 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, i as TooltipTrigger, n as TooltipContent, r as TooltipProvider, t as Tooltip } from "./tooltip-HHfiWInP.js"; 17import { i as init_tag, n as Tag, r as TagPrimitive } from "./tag-j7SWTISf.js"; 18import { c as SnackbarLink, l as SnackbarText, n as useAsyncQueuer, s as Snackbar, t as init_useAsyncQueuer, u as init_snackbar } from "./useAsyncQueuer-DxA58iaB.js"; 19import { $ as init_seniority, A as filterFunction, F as sortFunction, K as humanizeDepartment, O as Filters, Q as CLASSIFIED_SENIORITY_LEVELS, X as init_esm, Y as usePositionTaxonomy, Z as useVirtualizer, a as Result, i as useLocationMappings, j as init_filters, k as clearFilters$1, n as useEmailSearch, o as init_result, q as init_usePositionTaxonomy, r as init_useLocationMappings, t as init_useEmailSearch } from "./useEmailSearch-D71GAJgY.js"; 20import { n as init_sentry_error_boundary, r as withSentryErrorBoundary } from "./sentry-error-boundary-riAWv_XB.js"; 21import { $n as CompanyItemLinks, Dt as GlobalHealthWidget, Jn as CompanyItemCheckbox, Ot as init_global_health_widget, Qn as CompanyItemInfo, Xn as CompanyItemEmailCount, Yn as CompanyItemContent, Zn as CompanyItemHeader, ar as CompanyItemSignals, er as CompanyItemLogo, hn as init_page_header, ir as CompanyItemRoot, mn as PageHeaderTitle, nr as CompanyItemMetaList, or as formatCompanySize, rr as CompanyItemName, sn as PageHeader, sr as init_company_item, tr as CompanyItemMeta } from "./turbo-mount-DM0tFk1M.js"; 22import { n as init_reveal_save_dropdown, t as RevealSaveDropdown }
22 from "./reveal-save-dropdown-abkRRssE.js"; 23import { a as init_useJobTitleSuggestions, c as useCampaignsSave, i as useSplittedResults, n as useLeadsListSave, o as useJobTitleSuggestions, r as init_useSplittedResults, s as init_useCampaignsSave, t as init_useLeadsListSave } from "./useLeadsListSave-sUF_wqjF.js"; 24import { a as PillTabsTriggerHint, i as PillTabsTrigger, o as PillTabsTriggerName, r as PillTabsList, s as init_pill_tabs, t as PillTabs } from "./pill-tabs-BfmSgv8F.js"; 25import { a as SheetContent, c as SheetHeader, d as SheetTrigger, f as init_sheet, i as SheetBody, r as SheetBack, s as SheetFooter, t as Sheet, u as SheetTitle } from "./sheet-2lP39BDr.js"; 26import { n as Select, r as init_select } from "./select-CGg0QGYX.js"; 27import { a as FilterValue, o as init_FilterValue } from "./inferred-email-tag-C2HWuGMo.js"; 28import { n as init_cost_indicator, t as CostIndicator } from "./cost-indicator-DKlBRQzL.js"; 29import { a as LeadSaveManager, l as init_lead_save_manager, u as isLeadSavePickerLayer } from "./lead-campaigns-manager-Chy_HL1G.js"; 30import { _ as useRevealCost, d as SIGNAL_SOURCE, f as ageHours, g as init_useRevealCost, h as useSignalsSheetAnalytics, l as MultiDomainSearchLegacy, m as init_signals_analytics, n as init_save_companies_dropdown, o as CompanyListsProvider, p as captureSignalsEvent, s as init_company_lists_context, t as SaveCompaniesDropdown, u as init_multi_domain_search, v as GenericEmailRevealAlert, y as init_generic_email_reveal_alert } from "./save-companies-dropdown-C5M3L4ZX.js"; 31import { a as formatTimeAgo, i as LeadSignalItem, n as departmentOrderByCount, o as init_lead_signal_item, r as init_emails_list, t as EmailsList } from "./emails-list-Dj82n7oT.js"; 32import { r as init_separator, t as Separator } from "./separator-D5aQTBY-.js"; 33import { n as buildCompanyLogoUrl, r as init_company_logo } from "./company-logo-iCJP3mFk.js"; 34import { 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"; 35import { a as SuggestionItem, o as init_suggestion_item } from "./select-count-menu-CyKZwGL-.js"; 36import { a as EmptyStateDescription, d as init_empty_state, l as EmptyStateTitle, n as EmptyStateActions, r as EmptyStateBackground, t as EmptyState } from "./empty-state-Crv2GUlN.js"; 37import { n as init_loading_spinner, t as LoadingSpinner } from "./loading-spinner-Ati6dit1.js"; 38import { n as useCompanySearch, t as init_useCompanySearch } from "./useCompanySearch-DV9eCnXs.js"; 39import { n as useDialog, t as init_useDialog } from "./useDialog-CwjC07YA.js"; 40import { a as signalContentRowsFor, l as teamActivityKey, n as hiringRoleGroups, o as signalSentence, r as init_signal_content, u as teamActivityReference } from "./signal-content-nWbAr07A.js"; 41import { n as useIsInView, t as init_use_is_in_view } from "./use-is-in-view-ClVAfloW.js"; 42import { n as init_confetti, t as Confetti } from "./confetti-D26MlcV3.js"; 43import { n as init_toast, r as toast } from "./toast-DdFPY0Ky.js"; 44import { n as init_upgrade_block, t as UpgradeBlock } from "./upgrade-block-EjZJv2Ky.js"; 45import { i as init_metrics, n as MetricsItem, r as MetricsSeparator, t as Metrics } from "./metrics-D-jJFy-A.js"; 46import { a as init_dist$1, s as usePage, t as Head_default } from "./dist-ZlUHm0R7.js"; 47import { r as init_AppLayout, t as AppLayout } from "./AppLayout-KSwds5qH.js"; 48import { n as useFlashMessages, t as init_useFlashMessages } from "./useFlashMessages-CaMVzoCv.js"; 49import { n as init_CompanySidePanel, t as CompanySidePanel } from "./CompanySidePanel-DRX-ZQzq.js"; 50import { i as init_useful_resources, n as init_greeting_helper, r as DashboardUsefulResources, t as GreetingHelper } from "./greeting_helper-BxaWdRiR.js"; 51import { a as init_campaigns_context, i as CampaignsProvider, n as init_leads_lists_context, o as useCampaigns, r as useLeadsLists, t as LeadsListsProvider } from "./leads-lists-context-DhM63X1i.js"; 52import { n as DropdownSearchNoResults, r as init_dropdown_search, t as DropdownSearch } from "./dropdown-search-B4PrbtsV.js"; 53import { n as init_LeadSidePanel, t as LeadSidePanel } from "./LeadSidePanel-CR2rlYVP.js"; 54//#region app/frontend/pages/Dashboard/parse-team-activities-section.ts 55function parseTeamActivitiesSection(raw) { 56 if (raw === void 0) return void 0; 57 try { 58 const section = JSON.parse(raw); 59 return { 60 data: Array.isArray(section.data) ? { 61 team_activities: section.data, 62 next_cursor: section.next_cursor ?? null 63 } : section.data,
64 degraded: section.degraded 65 }; 66 } catch { 67 return { 68 data: { 69 team_activities: [], 70 next_cursor: null 71 }, 72 degraded: true 73 }; 74 } 75} 76//#endregion 77//#region app/frontend/react/components/dashboard-metrics.tsx 78function _temp$4(value) { 79 const clamped = Math.min(100, Math.max(0, value)); 80 return clamped === 100 ? "100.00" : Math.min(99.99, Math.round(clamped * 100) / 100).toFixed(2); 81} 82var import_compiler_runtime$12, import_react$17, import_jsx_runtime$17, DashboardMetrics; 83function init_dashboard_metrics() { 84 return (init_dashboard_metrics = __esmMin((() => { 85 import_compiler_runtime$12 = require_compiler_runtime(); 86 import_react$17 = /* @__PURE__ */ __toESM(require_react()); 87 init_button(); 88 init_currency_helper(); 89 init_routes(); 90 init_metrics(); 91 import_jsx_runtime$17 = require_jsx_runtime(); 92 DashboardMetrics = import_react$17.forwardRef((t0, ref) => { 93 const $ = (0, import_compiler_runtime$12.c)(49); 94 let className; 95 let leads_count; 96 let leads_without_email; 97 let props; 98 let unread_inbox_messages; 99 let verified_leads; 100 if ($[0] !== t0) { 101 ({className, leads_count, leads_without_email, verified_leads, unread_inbox_messages, ...props} = t0); 102 $[0] = t0; 103 $[1] = className; 104 $[2] = leads_count; 105 $[3] = leads_without_email; 106 $[4] = props; 107 $[5] = unread_inbox_messages; 108 $[6] = verified_leads; 109 } else { 110 className = $[1]; 111 leads_count = $[2]; 112 leads_without_email = $[3]; 113 props = $[4]; 114 unread_inbox_messages = $[5]; 115 verified_leads = $[6]; 116 } 117 const totalLeads = leads_count ?? 0; 118 const withoutEmail = leads_without_email ?? 0; 119 const verified = verified_leads ?? 0; 120 const percentageForDisplay = _temp$4; 121 const t1 = totalLeads > 0 ? (totalLeads - withoutEmail) / totalLeads * 100 : 0; 122 let t2; 123 if ($[7] !== t1) { 124 t2 = percentageForDisplay(t1); 125 $[7] = t1; 126 $[8] = t2; 127 } else t2 = $[8]; 128 const percentageOfLeadsWithEmail = t2; 129 const t3 = totalLeads > 0 ? verified / totalLeads * 100 : 0; 130 let t4; 131 if ($[9] !== t3) { 132 t4 = percentageForDisplay(t3); 133 $[9] = t3; 134 $[10] = t4; 135 } else t4 = $[10]; 136 const percentageOfVerifiedLeads = t4; 137 let t5; 138 if ($[11] === Symbol.for("react.memo_cache_sentinel")) { 139 t5 = i18n.t("js.dashboard.headline_metrics.total_leads"); 140 $[11] = t5; 141 } else t5 = $[11]; 142 let t6; 143 if ($[12] !== totalLeads) { 144 t6 = CurrencyHelper.numberWithDelimiter(totalLeads); 145 $[12] = totalLeads; 146 $[13] = t6; 147 } else t6 = $[13]; 148 let t7; 149 if ($[14] !== t6) { 150 t7 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsItem, { 151 icon: "users", 152 iconColor: "var(--colors-secondary-600)", 153 label: t5, 154 value: t6 155 }); 156 $[14] = t6; 157 $[15] = t7; 158 } else t7 = $[15]; 159 let t8; 160 if ($[16] === Symbol.for("react.memo_cache_sentinel")) { 161 t8 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsSeparator, {}); 162 $[16] = t8; 163 } else t8 = $[16]; 164 let t9; 165 if ($[17] === Symbol.for("react.memo_cache_sentinel")) { 166 t9 = i18n.t("js.dashboard.headline_metrics.with_email_address"); 167 $[17] = t9; 168 } else t9 = $[17]; 169 const t10 = `${percentageOfLeadsWithEmail}%`; 170 let t11; 171 if ($[18] !== percentageOfLeadsWithEmail || $[19] !== totalLeads) { 172 t11 = totalLeads >
172 0 && Number(percentageOfLeadsWithEmail) < 100 && /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(Button, { 173 size: "xs", 174 variant: "default", 175 asChild: true, 176 children: /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)("a", { 177 href: leads_path({ email: "~" }), 178 children: i18n.t("js.dashboard.headline_metrics.show_missing") 179 }) 180 }); 181 $[18] = percentageOfLeadsWithEmail; 182 $[19] = totalLeads; 183 $[20] = t11; 184 } else t11 = $[20]; 185 let t12; 186 if ($[21] !== t10 || $[22] !== t11) { 187 t12 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsItem, { 188 icon: "at", 189 iconColor: "var(--colors-success-600)", 190 label: t9, 191 value: t10, 192 wide: true, 193 children: t11 194 }); 195 $[21] = t10; 196 $[22] = t11; 197 $[23] = t12; 198 } else t12 = $[23]; 199 let t13; 200 if ($[24] === Symbol.for("react.memo_cache_sentinel")) { 201 t13 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsSeparator, {}); 202 $[24] = t13; 203 } else t13 = $[24]; 204 let t14; 205 if ($[25] === Symbol.for("react.memo_cache_sentinel")) { 206 t14 = i18n.t("js.dashboard.headline_metrics.verified"); 207 $[25] = t14; 208 } else t14 = $[25]; 209 const t15 = `${percentageOfVerifiedLeads}%`; 210 let t16; 211 if ($[26] !== percentageOfVerifiedLeads || $[27] !== totalLeads) { 212 t16 = totalLeads >
212 0 && Number(percentageOfVerifiedLeads) < 100 && /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(Button, { 213 size: "xs", 214 variant: "default", 215 asChild: true, 216 children: /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)("a", { 217 href: leads_path({ verification_status: [ 218 "error", 219 "unknown", 220 "~" 221 ] }), 222 children: i18n.t("js.dashboard.headline_metrics.show_unverified") 223 }) 224 }); 225 $[26] = percentageOfVerifiedLeads; 226 $[27] = totalLeads; 227 $[28] = t16; 228 } else t16 = $[28]; 229 let t17; 230 if ($[29] !== t15 || $[30] !== t16) { 231 t17 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsItem, { 232 icon: "check-circle", 233 iconColor: "var(--colors-success-600)", 234 label: t14, 235 value: t15, 236 wide: true, 237 children: t16 238 }); 239 $[29] = t15; 240 $[30] = t16; 241 $[31] = t17; 242 } else t17 = $[31]; 243 let t18; 244 if ($[32] === Symbol.for("react.memo_cache_sentinel")) { 245 t18 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsSeparator, {}); 246 $[32] = t18; 247 } else t18 = $[32]; 248 let t19; 249 if ($[33] === Symbol.for("react.memo_cache_sentinel")) { 250 t19 = i18n.t("js.dashboard.headline_metrics.new_replies"); 251 $[33] = t19; 252 } else t19 = $[33]; 253 let t20; 254 if ($[34] !== unread_inbox_messages) { 255 t20 = CurrencyHelper.numberWithDelimiter(unread_inbox_messages ?? 0); 256 $[34] = unread_inbox_messages; 257 $[35] = t20; 258 } else t20 = $[35]; 259 let t21; 260 if ($[36] !== unread_inbox_messages) { 261 t21 = (unread_inbox_messages ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(Button, { 262 size: "xs", 263 variant: "default", 264 asChild: true, 265 children: /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)("a", { 266 href: sequences_inbox_index_path(), 267 children: i18n.t("js.dashboard.headline_metrics.go_to_inbox") 268 }) 269 }); 270 $[36] = unread_inbox_messages; 271 $[37] = t21; 272 } else t21 = $[37]; 273 let t22; 274 if ($[38] !== t20 || $[39] !== t21) { 275 t22 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsx)(MetricsItem, { 276 icon: "inbox", 277 iconColor: "var(--colors-cyan-500)", 278 label: t19, 279 value: t20, 280 children: t21 281 }); 282 $[38] = t20; 283 $[39] = t21; 284 $[40] = t22; 285 } else t22 = $[40]; 286 let t23; 287 if ($[41] !== className || $[42] !== props || $[43] !== ref || $[44] !== t12 || $[45] !== t17 || $[46] !== t22 || $[47] !== t7) { 288 t23 = /* @__PURE__ */ (0, import_jsx_runtime$17.jsxs)(Metrics, { 289 ref, 290 className, 291 ...props, 292 children: [ 293 t7, 294 t8, 295 t12, 296 t13, 297 t17, 298 t18, 299 t22 300 ] 301 }); 302 $[41] = className; 303 $[42] = props; 304 $[43] = ref; 305 $[44] = t12; 306 $[45] = t17; 307 $[46] = t22; 308 $[47] = t7; 309 $[48] = t23; 310 } else t23 = $[48]; 311 return t23; 312 }); 313 DashboardMetrics.displayName = "DashboardMetrics"; 314 })))(); 315} 316//#endregion 317//#region app/frontend/react/hooks/useEmailsLoading.ts 318var import_react$16, useEmailsLoading; 319function init_useEmailsLoading() { 320 return (init_useEmailsLoading = __esmMin((() => { 321 init_useAsyncQueuer(); 322 import_react$16 = require_react(); 323 init_routes(); 324 useEmailsLoading = (initialEmailIds, setResults) => { 325 const [isLoading, setIsLoading] = (0, import_react$16.useState)(false); 326 const [domains, setDomains] = (0, import_react$16.useState)([]); 327 const fetchEmailsFn = async (emailIds) => { 328 const data = await (await fetch(multi_domain_search_emails_search_index_path(), { 329 method: "POST", 330 headers: { 331 "Content-Type": "application/json", 332 "X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content 333 }, 334 body: JSON.stringify({ 335 email_ids: emailIds, 336 include_domains: true 337 }) 338 })).json(); 339 setResults((prev) => { 340 const existingIds = new Set(prev.map((result) => result.id)); 341 const newResults = data.emails.filter((email) => !existingIds.has(email.id)); 342 return [...prev, ...newResults]; 343 }); 344 setDomains((prev_0) => { 345 const knownValues = new Set(prev_0.map((domain) => domain.value)); 346 const added = (data.domains ?? []).filter((domain_0) => !knownValues.has(domain_0.value)); 347 return added.length > 0 ? [...prev_0, ...added] : prev_0; 348 }); 349 if (queue.peekPendingItems().length === 0) setIsLoading(false); 350 }; 351 const queue = useAsyncQueuer(fetchEmailsFn, { 352 concurrency: 10, 353 wait: 10 354 }); 355 (0, import_react$16.useEffect)(() => { 356 queue.start(); 357 }, [queue]); 358 (0, import_react$16.useEffect)(() => { 359 if (initialEmailIds.length > 0) { 360 setIsLoading(true); 361 const batchSize = 5e3; 362 for (let i = 0; i < initialEmailIds.length; i += batchSize) queue.addItem(initialEmailIds.slice(i, i + batchSize)); 363 } 364 }, [initialEmailIds]); 365 return { 366 queue, 367 isLoading, 368 domains 369 }; 370 }; 371 })))(); 372} 373//#endregion 374//#region app/frontend/react/components/search/company-filters.tsx 375var import_compiler_runtime$11, import_react$15, import_jsx_runtime$16, t$1, COMPANY_SIZE_OPTIONS, COMPANY_TYPE_OPTIONS, computeTypeOrder, MILLION, amountToMillions, parseMillions, FUNDING_AMOUNT_SHORTCUTS, FUNDING_SERIES_MAPPING, fundingDateRange, FUNDING_SERIES_OPTIONS, LOCATION_TYPE_ORDER, LOCATION_TYPE_LABELS, DEFAULT_LOCATION_TYPES, INLINE_MENU_STYLES, TruncatingLabel, FilterSummaryRow, CompanyFilters, filterCompanies, normalizeCompanyType, matchesLocation; 376function init_company_filters() { 377 return (init_company_filters = __esmMin((() => { 378 import_compiler_runtime$11 = require_compiler_runtime(); 379 import_react$15 = /* @__PURE__ */ __toESM(require_react()); 380 init_dropdown_menu(); 381 init_dropdown_search(); 382 init_suggestion_item(); 383 init_tooltip(); 384 init_button(); 385 init_icon(); 386 init_checkbox(); 387 init_tag(); 388 init_select(); 389 init_input(); 390 init_label(); 391 init_tabs(); 392 init_routes(); 393 init_text_helpers(); 394 init_FilterValue(); 395 import_jsx_runtime$16 = require_jsx_runtime(); 396 t$1 = (key, options) => window.i18n.t(key, options); 397 COMPANY_SIZE_OPTIONS = [ 398 "1-10", 399 "11-50", 400 "51-200", 401 "201-500", 402 "501-1000", 403 "1001-5000", 404 "5001-10000", 405 "10001+" 406 ]; 407 COMPANY_TYPE_OPTIONS = [ 408 "educational", 409 "educational institution", 410 "government agency", 411 "non profit", 412 "partnership", 413 "privately held", 414 "public company", 415 "self employed", 416 "self owned", 417 "sole proprietorship" 418 ];
419 computeTypeOrder = (selectedTypes) => { 420 const selectedValues = new Set(selectedTypes.map((type) => type.value)); 421 return { 422 pinned: COMPANY_TYPE_OPTIONS.filter((value) => selectedValues.has(value)), 423 rest: COMPANY_TYPE_OPTIONS.filter((value) => !selectedValues.has(value)) 424 }; 425 }; 426 MILLION = 1e6; 427 amountToMillions = (amount) => amount != null ? String(amount / MILLION) : ""; 428 parseMillions = (value) => { 429 const cleaned = value.replace(/,/g, "").trim(); 430 if (cleaned === "") return void 0; 431 const parsed = parseFloat(cleaned); 432 return isNaN(parsed) ? void 0 : Math.round(parsed * MILLION); 433 }; 434 FUNDING_AMOUNT_SHORTCUTS = [ 435 { 436 key: "under", 437 label: "Up to $1M", 438 from: void 0, 439 to: MILLION 440 }, 441 { 442 key: "range", 443 label: "$1Mâ$10M", 444 from: MILLION, 445 to: 10 * MILLION 446 }, 447 { 448 key: "over", 449 label: "$10M+", 450 from: 10 * MILLION, 451 to: void 0 452 } 453 ]; 454 FUNDING_SERIES_MAPPING = { 455 pre_seed: ["Pre-Seed"], 456 seed: ["Seed"], 457 pre_series_a: ["Pre-Series A"], 458 series_a: ["Series A"],
459 pre_series_b: ["Pre-Series B"], 460 series_b: ["Series B"], 461 pre_series_c: ["Pre-Series C"], 462 "series_c+": [ 463 "Series C", 464 "Pre-Series D", 465 "Series D", 466 "Pre-Series E", 467 "Series E", 468 "Pre-Series F", 469 "Series F", 470 "Pre-Series G", 471 "Series G", 472 "Pre-Series H", 473 "Series H", 474 "Pre-Series I", 475 "Series I" 476 ], 477 other: ["Other"] 478 }; 479 fundingDateRange = (date, dateFrom, dateTo) => { 480 const daysAgo = (days) => Date.now() - days * 24 * 60 * 60 * 1e3; 481 if (date === "custom") { 482 if (dateFrom == null && dateTo == null) return null; 483 return { 484 from: dateFrom == null ? -Infinity : daysAgo(dateFrom), 485 to: dateTo == null ? Infinity : daysAgo(dateTo) 486 }; 487 } 488 const days = date ? { 489 "7d": 7, 490 "1m": 30, 491 "3m": 90, 492 "6m": 180, 493 "1y": 365 494 }[date] : void 0; 495 return days ? { 496 from: daysAgo(days), 497 to: Infinity 498 } : null; 499 }; 500 FUNDING_SERIES_OPTIONS = [ 501 { 502 value: "pre_seed", 503 label: "Pre-Seed" 504 }, 505 { 506 value: "seed", 507 label: "Seed" 508 }, 509 { 510 value: "pre_series_a", 511 label: "Pre-Series A" 512 }, 513 { 514 value: "series_a", 515 label: "Series A" 516 }, 517 { 518 value: "pre_series_b", 519 label: "Pre-Series B" 520 }, 521 { 522 value: "series_b", 523 label: "Series B" 524 }, 525 { 526 value: "pre_series_c", 527 label: "Pre-Series C" 528 }, 529 { 530 value: "series_c+", 531 label: "Series C+" 532 }, 533 { 534 value: "other", 535 label: "Other" 536 } 537 ]; 538 LOCATION_TYPE_ORDER = [ 539 "country", 540 "state", 541 "city" 542 ]; 543 LOCATION_TYPE_LABELS = { 544 country: "Country", 545 state: "State", 546 city: "City" 547 }; 548 DEFAULT_LOCATION_TYPES = ["country"]; 549 INLINE_MENU_STYLES = { menu: (base) => ({ 550 ...base, 551 position: "static", 552 margin: "var(--spacing-01) 0 0", 553 border: "1px solid var(--colors-neutral-300)",
554 boxShadow: "none" 555 }) }; 556 TruncatingLabel = (t0) => { 557 const $ = (0, import_compiler_runtime$11.c)(14); 558 const { htmlFor, text } = t0; 559 const ref = (0, import_react$15.useRef)(null); 560 const [truncated, setTruncated] = (0, import_react$15.useState)(false); 561 let t1; 562 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 563 t1 = () => { 564 const el = ref.current; 565 if (el) setTruncated(el.scrollWidth > el.clientWidth); 566 }; 567 $[0] = t1; 568 } else t1 = $[0]; 569 const measure = t1; 570 let t2; 571 if ($[1] !== text) { 572 t2 = [text]; 573 $[1] = text; 574 $[2] = t2; 575 } else t2 = $[2]; 576 (0, import_react$15.useEffect)(measure, t2); 577 let t3; 578 if ($[3] === Symbol.for("react.memo_cache_sentinel")) { 579 t3 = { 580 minWidth: 0, 581 overflow: "hidden", 582 textOverflow: "ellipsis", 583 whiteSpace: "nowrap" 584 }; 585 $[3] = t3; 586 } else t3 = $[3]; 587 let t4; 588 if ($[4] !== htmlFor || $[5] !== text) { 589 t4 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("label", { 590 ref, 591 htmlFor, 592 style: t3, 593 onMouseEnter: measure, 594 children: text 595 }); 596 $[4] = htmlFor; 597 $[5] = text; 598 $[6] = t4; 599 } else t4 = $[6]; 600 const label = t4; 601 if (!truncated) return label; 602 let t5; 603 if ($[7] !== label) { 604 t5 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(TooltipTrigger, { 605 asChild: true, 606 children: label 607 }); 608 $[7] = label; 609 $[8] = t5; 610 } else t5 = $[8]; 611 let t6; 612 if ($[9] !== text) { 613 t6 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(TooltipContent, { children: text }); 614 $[9] = text; 615 $[10] = t6; 616 } else t6 = $[10]; 617 let t7; 618 if ($[11] !== t5 || $[12] !== t6) { 619 t7 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Tooltip, { children: [t5, t6] }) }); 620 $[11] = t5; 621 $[12] = t6; 622 $[13] = t7; 623 } else t7 = $[13]; 624 return t7; 625 }; 626 FilterSummaryRow = (t0) => { 627 const $ = (0, import_compiler_runtime$11.c)(11); 628 const { label, summary, onClick } = t0; 629 let t1; 630 if ($[0] !== label) { 631 t1 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("span", { children: label }); 632 $[0] = label; 633 $[1] = t1; 634 } else t1 = $[1]; 635 let t2; 636 if ($[2] === Symbol.for("react.memo_cache_sentinel")) { 637 t2 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { 638 icon: "angle-right", 639 size: "sm" 640 }); 641 $[2] = t2; 642 } else t2 = $[2]; 643 let t3; 644 if ($[3] !== summary) { 645 t3 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("span", { 646 className: "d-flex align-items-center gap-1 text-muted", 647 children: [summary, t2] 648 }); 649 $[3] = summary; 650 $[4] = t3; 651 } else t3 = $[4]; 652 let t4; 653 if ($[5] !== t1 || $[6] !== t3) { 654 t4 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("button", { 655 type: "button", 656 className: "d-flex align-items-center justify-content-between w-100", 657 children: [t1, t3] 658 }); 659 $[5] = t1; 660 $[6] = t3; 661 $[7] = t4; 662 } else t4 = $[7]; 663 let t5; 664 if ($[8] !== onClick || $[9] !== t4) { 665 t5 = /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuItem, { 666 asChild: true, 667 onClick, 668 children: t4 669 }); 670 $[8] = onClick; 671 $[9] = t4; 672 $[10] = t5; 673 } else t5 = $[10]; 674 return t5; 675 }; 676 CompanyFilters = ({ filters, setFilters, clearFilters, anyFiltersSet, industries = [], technologies = [], visibleFilters = [ 677 "location", 678 "industry", 679 "size", 680 "type", 681 "year", 682 "funding", 683 "technologies" 684 ], fundingTypes, optionValues, additionalFilters }) => { 685 const locationTriggerRef = (0, import_react$15.useRef)(null); 686 const industryTriggerRef = (0, import_react$15.useRef)(null); 687 const sizeTriggerRef = (0, import_react$15.useRef)(null); 688 const typeTriggerRef = (0, import_react$15.useRef)(null); 689 const yearTriggerRef = (0, import_react$15.useRef)(null); 690 const fundingTriggerRef = (0, import_react$15.useRef)(null); 691 const technologiesTriggerRef = (0, import_react$15.useRef)(null); 692 const [selectedLocation, setSelectedLocation] = (0, import_react$15.use
692State)(filters.location || []); 693 const [selectedIndustries, setSelectedIndustries] = (0, import_react$15.useState)(filters.industries || []); 694 const [selectedSizes, setSelectedSizes] = (0, import_react$15.useState)(filters.companySizes || []); 695 const [selectedTypes, setSelectedTypes] = (0, import_react$15.useState)(filters.companyTypes || []); 696 const [selectedTechnologies, setSelectedTechnologies] = (0, import_react$15.useState)(filters.technologies || []); 697 const [selectedYearFounded, setSelectedYearFounded] = (0, import_react$15.useState)(filters.yearFounded); 698 const [selectedFunding, setSelectedFunding] = (0, import_react$15.useState)(filters.funding); 699 const [typeOrder, setTypeOrder] = (0, import_react$15.useState)(() => computeTypeOrder(filters.companyTypes || [])); 700 const [yearFoundedMode, setYearFoundedMode] = (0, import_react$15.useState)(selectedYearFounded?.mode || "interval"); 701 const [yearFrom, setYearFrom] = (0, import_react$15.useState)(selectedYearFounded?.from?.toString() || ""); 702 const [yearTo, setYearTo] = (0, import_react$15.useState)(selectedYearFounded?.to?.toString() || ""); 703 const [specificYears, setSpecificYears] = (0, import_react$15.useState)(selectedYearFounded?.years || []); 704 const [yearInput, setYearInput] = (0, import_react$15.useState)(""); 705 const [fundingSeries, setFundingSeries] = (0, import_react$15.useState)(selectedFunding?.series || []); 706 const [fundingAmountFrom, setFundingAmountFrom] = (0, import_react$15.useState)(() => amountToMillions(selectedFunding?.amountFrom)); 707 const [fundingAmountTo, setFundingAmountTo] = (0, import_react$15.useState)(() => amountToMillions(selectedFunding?.amountTo)); 708 const [fundingDate, setFundingDate] = (0, import_react$15.useState)(selectedFunding?.date || ""); 709 const [fundingDateFrom, setFundingDateFrom] = (0, import_react$15.useState)(selectedFunding?.dateFrom?.toString() || ""); 710 const [fundingDateTo, setFundingDateTo] = (0, import_react$15.useState)(selectedFunding?.dateTo?.toString() || ""); 711 (0, import_react$15.useEffect)(() => { 712 setSelectedLocation(filters.location || []); 713 setSelectedIndustries(filters.industries || []); 714 setSelectedSizes(filters.companySizes || []); 715 setSelectedTypes(filters.companyTypes || []); 716 setSelectedTechnologies(filters.technologies || []); 717 setSelectedYearFounded(filters.yearFounded); 718 setSelectedFunding(filters.funding); 719 setYearFoundedMode(filters.yearFounded?.mode || "interval"); 720 setYearFrom(filters.yearFounded?.from?.toString() || ""); 721 setYearTo(filters.yearFounded?.to?.toString() || ""); 722 setSpecificYears(filters.yearFounded?.years || []); 723 setYearInput(""); 724 setFundingSeries(filters.funding?.series || []); 725 setFundingAmountFrom(amountToMillions(filters.funding?.amountFrom)); 726 setFundingAmountTo(amountToMillions(filters.funding?.amountTo)); 727 setFundingDate(filters.funding?.date || ""); 728 setFundingDateFrom(filters.funding?.dateFrom?.toString() || ""); 729 setFundingDateTo(filters.funding?.dateTo?.toString() || ""); 730 }, [filters]); 731 const [openFilter, setOpenFilter] = (0, import_react$15.useState)(null); 732 const showFilter = (filter) => { 733 if (filter !== "location") setLocationQuery(""); 734 setOpenFilter(filter); 735 }; 736 const applyFilters = (overrides = {}) => { 737 setFilters({ 738 location: selectedLocation, 739 industries: selectedIndustries, 740 companySizes: selectedSizes, 741 companyTypes: selectedTypes, 742 technologies: selectedTechnologies, 743 yearFounded: selectedYearFounded, 744 funding: selectedFunding, 745 ...overrides 746 }); 747 showFilter(null); 748 }; 749 const locationKey = (loc) => `${loc.type}-${loc.value}`; 750 const [locationQuery, setLocationQuery] = (0, import_react$15.useState)(""); 751 const [locationResults, setLocationResults] = (0, import_react$15.useState)([]); 752 const [locationLoading, setLocationLoading] = (0, import_react$15.useState)(false); 753 const [pinnedLocations, setPinnedLocations] = (0, import_react$15.useState)(() => filters.location || []); 754 const pinnedLocationKeys = new Set(pinnedLocations.map(locationKey)); 755 (0, import_react$15.useEffect)(() => { 756 if (openFilter !== "location" || optionValues?.locations) return; 757 const query = locationQuery.trim(); 758 setLocationLoading(true); 759 setLocationResults([]); 760 const controller = new AbortController(); 761 const timer = setTimeout(() => { 762 fetch(locations_path({ query }), { signal: controller.signal }).then((response) => { 763 if (!response.ok) throw new Error(`Location search failed: ${response.status}`); 764 return response.json(); 765 }).then((data) => { 766 const unique = data.filter((item, index, all) => all.findIndex((other) => other.value === item.value && other.type === item.type) === index); 767 setLocationResults(unique.map((item_0) => ({ 768 ...item_0, 769 included: true 770 }))); 771 }).catch(() => {}).finally(() => { 772 if (!controller.signal.aborted) setLocationLoading(false); 773 }); 774 }, query ? 250 : 0); 775 return () => { 776 clearTimeout(timer); 777 controller.abort(); 778 }; 779 }, [ 780 locationQuery, 781 openFilter, 782 optionValues?.locations 783 ]); 784 const sameLocation = (a, b) => a.value === b.value && a.type === b.type; 785 const toggleLocation = (location) => { 786 setSelectedLocation((prev) => prev.some((loc_1) => sameLocation(loc_1, location)) ? prev.filter((loc_0) => !sameLocation(loc_0, location)) : [...prev, { 787 value: location.value, 788 type: location.type, 789 label: location.label, 790 included: true 791 }]); 792 }; 793 const changeLocationInclusion = (location_0, included) => { 794 setSelectedLocation((prev_0) => prev_0.map((loc_2) => sameLocation(loc_2, location_0) ? { 795 ...loc_2, 796 included 797 } : loc_2)); 798 }; 799 const renderLocationRow = (location_1) => { 800 const selected = selectedLocation.find((loc_3) => sameLocation(loc_3, location_1)); 801 const included_0 = selected?.included ?? true; 802 const excluded = !!selected && !included_0; 803 const id = `filter-location-${location_1.type}-${location_1.value}`; 804 return /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("li", { 805 className: "dropdown-item", 806 children: [
807 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuInlineItem, { 808 preserveRole: true, 809 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Checkbox, { 810 checked: !!selected, 811 id, 812 onChange: () => toggleLocation(location_1) 813 }) 814 }), 815 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(TruncatingLabel, { 816 htmlFor: id, 817 text: location_1.label 818 }), 819 selected && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 820 className: "tags-list__actions", 821 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuInlineItem, { children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("button", { 822 type: "button", 823 className: `tags-list__action ${included_0 ? "active" : ""}`, 824 style: { paddingBlock: 0 }, 825 onClick: () => changeLocationInclusion(selected, true), 826 children: "Include" 827 }) }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuInlineItem, { children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("button", { 828 type: "button", 829 className: `tags-list__action tags-list__action--exclude ${excluded ? "active" : ""}`, 830 style: { paddingBlock: 0 }, 831 onClick: () => changeLocationInclusion(selected, false), 832 children: "Exclude" 833 }) })] 834 }) 835 ] 836 }, `${location_1.type}-${location_1.value}`); 837 }; 838 const matchingLocations = optionValues?.locations ? optionValues.locations.filter((location_2) => location_2.label.toLowerCase().includes(locationQuery.trim().toLowerCase())) : locationResults; 839 const isLocationLoading = !optionValues?.locations && locationLoading; 840 const unselectedLocationResults = matchingLocations.filter((result) => !pinnedLocationKeys.has(locationKey(result))); 841 const isDefaultLocationView = locationQuery.trim() === ""; 842 const groupedLocationResults = LOCATION_TYPE_ORDER.filter((type) => !isDefaultLocationView || DEFAULT_LOCATION_TYPES.includes(type)).map((type_0) => ({ 843 type: type_0, 844 items: unselectedLocationResults.filter((result_0) => result_0.type === type_0) 845 })).filter((group) => group.items.length > 0); 846 const pinnedMatchesQuery = matchingLocations.some((result_1) => pinnedLocationKeys.has(locationKey(result_1))); 847 const noLocationResults = !isLocationLoading && locationQuery.trim() !== "" && groupedLocationResults.length === 0 && !pinnedMatchesQuery; 848 const locationFilterBody = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [
849 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownSearch, { 850 as: "li", 851 variant: "alternative", 852 autoFocus: true, 853 ariaLabel: "Location", 854 placeholder: "Search for locations...", 855 value: locationQuery, 856 onValueChange: setLocationQuery 857 }), 858 /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 859 className: "location-filter-dropdown__results", 860 children: [ 861 pinnedLocations.map(renderLocationRow), 862 isLocationLoading && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownSearchNoResults, { 863 className: "d-flex align-items-center gap-2 text-start", 864 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { 865 icon: "spinner-third", 866 className: "fa-spin", 867 size: "sm", 868 "aria-hidden": "true" 869 }), "Searching locationsâ¦"] 870 }), 871 noLocationResults && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownSearchNoResults, { 872 className: "text-start", 873 children: "No locations found" 874 }), 875 groupedLocationResults.map((group_0) => /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_react$15.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("li", { 876 className: "dropdown-menu__group-header", 877 children: LOCATION_TYPE_LABELS[group_0.type] 878 }), group_0.items.map(renderLocationRow)] }, group_0.type)) 879 ] 880 }),
881 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 882 disabled: selectedLocation.length === 0 && !filters.location?.length, 883 onClick: () => applyFilters(), 884 children: "Apply filters" 885 }) 886 ] }); 887 const industryOptions = industries.map((ind) => ({ 888 value: ind.id, 889 label: ind.name 890 })); 891 const industryFilterBody = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Select, { 892 options: industryOptions, 893 placeholder: "Select industries...", 894 isMulti: true, 895 value: selectedIndustries.map((id_0) => industryOptions.find((opt) => opt.value === id_0)), 896 onChange: (selected_0) => { 897 setSelectedIndustries((selected_0 || []).map((opt_0) => opt_0.value)); 898 }, 899 styles: INLINE_MENU_STYLES 900 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 901 disabled: selectedIndustries.length === 0 && !filters.industries?.length, 902 onClick: () => applyFilters(), 903 children: "Apply filters" 904 })] }); 905 const availableSizes = optionValues ? new Set(optionValues.sizes) : null; 906 const sizeFilterBody = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [COMPANY_SIZE_OPTIONS.filter((size) => !availableSizes || availableSizes.has(size)).map((size_0) => /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenuItem, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Checkbox, { 907 checked: selectedSizes.includes(size_0), 908 id: `filter-size-${size_0}`, 909 onChange: () => setSelectedSizes((prev_1) => prev_1.includes(size_0) ? prev_1.filter((s) => s !== size_0) : [...prev_1, size_0]) 910 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("label", { 911 htmlFor: `filter-size-${size_0}`, 912 children: formatCompanySizeRangeLabel(size_0) 913 })] }, size_0)), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 914 disabled: selectedSizes.length === 0 && !filters.companySizes?.length, 915 onClick: () => applyFilters(), 916 children: "Apply filters" 917 })] }); 918 const typeOptions = COMPANY_TYPE_OPTIONS.map((type_1) => ({ 919 value: type_1, 920 label: type_1.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) 921 })); 922 const typeLabels = Object.fromEntries(typeOptions.map((option) => [option.value, option.label])); 923 const toggleCompanyType = (value) => { 924 setSelectedTypes((prev_2) => prev_2.some((type_3) => type_3.value === value) ? prev_2.filter((type_2) => type_2.value !== value) : [...prev_2, { 925 value, 926 label: typeLabels[value], 927 included: true 928 }]); 929 }; 930 const changeCompanyTypeInclusion = (companyType, included_1) => { 931 const newTypes = selectedTypes.map((type_4) => { 932 if (type_4.value === companyType.value) return { 933 ...type_4, 934 included: included_1 935 }; 936 return type_4; 937 }); 938 setSelectedTypes(newTypes); 939 }; 940 const renderTypeRow = (value_0) => { 941 const selected_1 = selectedTypes.find((type_5) => type_5.value === value_0); 942 const included_2 = selected_1?.included ?? true; 943 const excluded_0 = !!selected_1 && !included_2; 944 return /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("li", { 945 className: "dropdown-item", 946 children: [ 947 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuInlineItem, { 948 preserveRole: true, 949 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Checkbox, { 950 checked: !!selected_1, 951 id: `filter-type-${value_0}`, 952 onChange: () => toggleCompanyType(value_0) 953 }) 954 }), 955 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("label", { 956 htmlFor: `filter-type-${value_0}`, 957 children: typeLabels[value_0] 958 }), 959 /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 960 className: "tags-list__actions", 961 style: { visibility: selected_1 ? "visible" : "hidden" }, 962 "aria-hidden": !selected_1, 963 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuInlineItem, { 964 disabled: !selected_1, 965 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("button", { 966 type: "button", 967 className: `tags-list__action ${selected_1 && included_2 ? "active" : ""}`, 968 style: { paddingBlock: 0 }, 969 disabled: !selected_1, 970 onClick: () => selected_1 && changeCompanyTypeInclusion(selected_1, true), 971 children: "Include" 972 }) 973 }
973), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuInlineItem, { 974 disabled: !selected_1, 975 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("button", { 976 type: "button", 977 className: `tags-list__action tags-list__action--exclude ${excluded_0 ? "active" : ""}`, 978 style: { paddingBlock: 0 }, 979 disabled: !selected_1, 980 onClick: () => selected_1 && changeCompanyTypeInclusion(selected_1, false), 981 children: "Exclude" 982 }) 983 })] 984 }) 985 ] 986 }, value_0); 987 }; 988 const visibleType = (value_1) => !optionValues || optionValues.types.some((type_6) => normalizeCompanyType(type_6) === normalizeCompanyType(value_1)); 989 const typeFilterBody = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [ 990 typeOrder.pinned.filter(visibleType).map(renderTypeRow), 991 typeOrder.rest.filter(visibleType).map(renderTypeRow), 992 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 993 disabled: selectedTypes.length === 0 && !filters.companyTypes?.length, 994 onClick: () => applyFilters(), 995 children: "Apply filters" 996 }) 997 ] }); 998 const addSpecificYear = () => { 999 const year = parseInt(yearInput, 10); 1000 if (!isNaN(year) && year >= 1e3 && year <= (/* @__PURE__ */ new Date()).getFullYear()) { 1001 if (!specificYears.includes(year)) setSpecificYears([...specificYears, year].sort()); 1002 setYearInput(""); 1003 } 1004 }; 1005 const yearFoundedApplyDisabled = (yearFoundedMode === "interval" ? yearFrom.trim() === "" && yearTo.trim() === "" : specificYears.length === 0) && !filters.yearFounded; 1006 const yearFoundedFilterBody = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Tabs, { 1007 className: "year-founded-tabs", 1008 value: yearFoundedMode, 1009 onValueChange: (v) => setYearFoundedMode(v), 1010 children: [ 1011 /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(TabsList, { 1012 style: { 1013 marginInline: "calc(-1 * var(--spacing-01))", 1014 paddingRight: "var(--spacing-03)", 1015 paddingLeft: "var(--spacing-03)" 1016 }, 1017 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Tab, { 1018 value: "interval", 1019 label: "Interval" 1020 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Tab, { 1021 value: "specific", 1022 label: "Specific" 1023 })] 1024 }), 1025 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(TabsContent, { 1026 value: "interval", 1027 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1028 className: "d-flex gap-2", 1029 onKeyDown: stopMenuTypeahead, 1030 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1031 className: "form-group flex-fill", 1032 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Label, { 1033 htmlFor: "year-from", 1034 children: "From" 1035 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1036 id: "year-from", 1037 size: "sm", 1038 fullWidth: true, 1039 type: "number", 1040 min: "1000", 1041 max: (/* @__PURE__ */ new Date()).getFullYear(), 1042 value: yearFrom, 1043 onChange: (e) => setYearFrom(e.target.value), 1044 placeholder: "Year" 1045 })] 1046 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1047 className: "form-group flex-fill", 1048 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Label, { 1049 htmlFor: "year-to", 1050 children: "To" 1051 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1052 id: "year-to", 1053 size: "sm", 1054 fullWidth: true, 1055 type: "number", 1056 min: "1000", 1057 max: (/* @__PURE__ */ new Date()).getFullYear(), 1058 value: yearTo, 1059 onChange: (e_0) => setYearTo(e_0.target.value), 1060 placeholder: "Year" 1061 })] 1062 })] 1063 }) 1064 }), 1065 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(TabsContent, { 1066 value: "specific", 1067 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1068 onKeyDown: stopMenuTypeahead, 1069 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1070 fullWidth: true, 1071 type: "number", 1072 min: "1000", 1073 max: (/* @__PURE__ */ new Date()).getFullYear(), 1074 value: yearInput, 1075 onChange: (e_1) => setYearInput(e_1.target.value), 1076 onKeyDown: (e_2) => { 1077 if (e_2.key === "Enter") { 1078 e_2.preventDefault(); 1079 addSpecificYear(); 1080 } 1081 }, 1082 onBlur: addSpecificYear, 1083 placeholder: "Add year" 1084 }), specificYears.length >
1084 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("div", { 1085 className: "tags-list flex-wrap mt-2", 1086 children: specificYears.map((year_0) => /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Tag, { 1087 label: year_0, 1088 dismissable: true, 1089 dismissAriaLabel: i18n.t("js.filters.remove"), 1090 onDismiss: () => setSpecificYears(specificYears.filter((y) => y !== year_0)) 1091 }, year_0)) 1092 })] 1093 }) 1094 }) 1095 ] 1096 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 1097 disabled: yearFoundedApplyDisabled, 1098 onClick: () => { 1099 const yearFounded = yearFoundedMode === "interval" ? { 1100 mode: "interval", 1101 from: yearFrom ? parseInt(yearFrom, 10) : void 0, 1102 to: yearTo ? parseInt(yearTo, 10) : void 0 1103 } : { 1104 mode: "specific", 1105 years: specificYears 1106 }; 1107 const applied = yearFounded.from || yearFounded.to || yearFounded.years?.length ? yearFounded : void 0; 1108 setSelectedYearFounded(applied); 1109 applyFilters({ yearFounded: applied }); 1110 }, 1111 children: "Apply filters" 1112 })] }); 1113 const FUNDING_DATE_OPTIONS = [ 1114 { 1115 value: "", 1116 label: "" 1117 }, 1118 { 1119 value: "7d", 1120 label: "Past 7 days" 1121 }, 1122 { 1123 value: "1m", 1124 label: "Past month" 1125 }, 1126 { 1127 value: "3m", 1128 label: "Past 3 months" 1129 }, 1130 { 1131 value: "6m", 1132 label: "Past 6 months" 1133 }, 1134 { 1135 value: "1y", 1136 label: "Past year" 1137 }, 1138 { 1139 value: "custom", 1140 label: "Custom..." 1141 } 1142 ]; 1143 const [fundingView, setFundingView] = (0, import_react$15.useState)("main"); 1144 const fundingContentRef = (0, import_react$15.useRef)(null); 1145 (0, import_react$15.useEffect)(() => { 1146 const content = fundingContentRef.current; 1147 if (!content) return; 1148 (fundingView === "main" ? content.querySelector("[role=\"menuitem\"]") : content.querySelector("input, select"))?.focus(); 1149 }, [fundingView]); 1150 const activeFundingShortcut = FUNDING_AMOUNT_SHORTCUTS.find((shortcut) => parseMillions(fundingAmountFrom) === shortcut.from && parseMillions(fundingAmountTo) === shortcut.to); 1151 const fundingAmountSummary = activeFundingShortcut ? activeFundingShortcut.label : fundingAmountFrom && fundingAmountTo ? `$${fundingAmountFrom}Mâ$${fundingAmountTo}M` : fundingAmountFrom ? `$${fundingAmountFrom}M+` : fundingAmountTo ? `Up to $${fundingAmountTo}M` : "Any"; 1152 const fundingDateSummary = fundingDate ? FUNDING_DATE_OPTIONS.find((opt_1) => opt_1.value === fundingDate)?.label || "Any time" : "Any time"; 1153 const fundingSubHeader = (title) => /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenuHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Button, { 1154 type: "button", 1155 variant: "ghost", 1156 iconOnly: true, 1157 size: "xs", 1158 onClick: () => setFundingView("main"), 1159 "aria-label": "Back", 1160 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "arrow-left" }) 1161 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("strong", { 1162 className: "dropdown-menu__title", 1163 children: title 1164 })] }); 1165 const fundingAmountSection = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { children: [
1166 /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1167 className: "d-flex align-items-baseline justify-content-between", 1168 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("span", { 1169 className: "h-legend h-legend--sm", 1170 children: "Series amount" 1171 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("span", { 1172 className: "text-hint text-2xs", 1173 children: "in $ millions" 1174 })] 1175 }), 1176 /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1177 className: "d-flex align-items-center gap-2 mt-1", 1178 children: [ 1179 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1180 id: "funding-amount-from", 1181 size: "sm", 1182 fullWidth: true, 1183 type: "text", 1184 inputMode: "decimal", 1185 value: fundingAmountFrom, 1186 onChange: (e_3) => setFundingAmountFrom(e_3.target.value), 1187 placeholder: "From", 1188 "aria-label": "From" 1189 }), 1190 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("span", { 1191 "aria-hidden": "true", 1192 children: "â" 1193 }), 1194 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1195 id: "funding-amount-to", 1196 size: "sm", 1197 fullWidth: true, 1198 type: "text", 1199 inputMode: "decimal", 1200 value: fundingAmountTo, 1201 onChange: (e_4) => setFundingAmountTo(e_4.target.value), 1202 placeholder: "To", 1203 "aria-label": "To" 1204 }) 1205 ] 1206 }),
1207 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("div", { 1208 className: "d-flex flex-wrap gap-1 mt-2", 1209 children: FUNDING_AMOUNT_SHORTCUTS.map((shortcut_0) => { 1210 const active = parseMillions(fundingAmountFrom) === shortcut_0.from && parseMillions(fundingAmountTo) === shortcut_0.to; 1211 return /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(SuggestionItem, { 1212 selected: active, 1213 onSelectedChange: () => { 1214 setFundingAmountFrom(active ? "" : amountToMillions(shortcut_0.from)); 1215 setFundingAmountTo(active ? "" : amountToMillions(shortcut_0.to)); 1216 }, 1217 children: shortcut_0.label 1218 }, shortcut_0.key); 1219 }) 1220 }) 1221 ] }); 1222 const fundingDateSection = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Label, { 1223 htmlFor: "funding-date", 1224 children: "Date" 1225 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("select", { 1226 id: "funding-date", 1227 className: "h-select h-select--block h-select--sm", 1228 value: fundingDate, 1229 onChange: (e_5) => setFundingDate(e_5.target.value), 1230 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("option", { 1231 value: "", 1232 children: "Any time" 1233 }), FUNDING_DATE_OPTIONS.filter((opt_2) => opt_2.value).map((opt_3) => /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("option", { 1234 value: opt_3.value, 1235 children: opt_3.label 1236 }, opt_3.value))] 1237 })] }), fundingDate === "custom" && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1238 className: "signal-custom-dates mt-2", 1239 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1240 className: "form-group", 1241 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Label, { 1242 className: "h-label--regular", 1243 htmlFor: "funding-date-from", 1244 children: "From" 1245 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1246 className: "h-input-group", 1247 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1248 type: "number", 1249 id: "funding-date-from", 1250 min: 0, 1251 max: 365, 1252 value: fundingDateFrom, 1253 onChange: (e_6) => setFundingDateFrom(e_6.target.value) 1254 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("span", { 1255 className: "h-input-group__text", 1256 children: "days ago" 1257 })] 1258 })] 1259 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1260 className: "form-group", 1261 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Label, { 1262 className: "h-label--regular", 1263 htmlFor: "funding-date-to", 1264 children: "To" 1265 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1266 className: "h-input-group", 1267 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Input, { 1268 type: "number", 1269 id: "funding-date-to", 1270 min: 0, 1271 max: 365, 1272 value: fundingDateTo, 1273 onChange: (e_7) => setFundingDateTo(e_7.target.value) 1274 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("span", { 1275 className: "h-input-group__text", 1276 children: "days ago" 1277 })] 1278 })] 1279 })] 1280 })] }); 1281 const applyFundingFilters = () => { 1282 const amountFrom = parseMillions(fundingAmountFrom); 1283 const amountTo = parseMillions(fundingAmountTo); 1284 const hasDateCriterion = fundingDate === "custom" ? Boolean(fundingDateFrom || fundingDateTo) : Boolean(fundingDate); 1285 const funding = fundingSeries.length > 0 || amountFrom != null || amountTo != null || hasDateCriterion ? { 1286 series: fundingSeries, 1287 amountFrom, 1288 amountTo, 1289 date: fundingDate || void 0, 1290 dateFrom: fundingDate === "custom" && fundingDateFrom ? parseInt(fundingDateFrom, 10) : void 0, 1291 dateTo: fundingDate === "custom" && fundingDateTo ? parseInt(fundingDateTo, 10) : void 0 1292 } : void 0; 1293 setSelectedFunding(funding); 1294 applyFilters({ funding }); 1295 }; 1296 const fundingApplyDisabled = fundingSeries.length === 0 && parseMillions(fundingAmountFrom) == null && parseMillions(fundingAmountTo) == null && (fundingDate === "custom" ? !fundingDateFrom && !fundingDateTo : !fundingDate) && !filters.funding; 1297 const availableFundingTypes = fundingTypes ? new Set(fundingTypes) : null;
1298 const fundingFilterBody = fundingView === "amount" ? /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [ 1299 fundingSubHeader("Series amount"), 1300 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("div", { 1301 className: "funding-filter-dropdown__sections", 1302 onKeyDown: stopMenuTypeahead, 1303 children: fundingAmountSection 1304 }), 1305 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 1306 onClick: () => setFundingView("main"), 1307 children: "Done" 1308 }) 1309 ] }) : fundingView === "date" ? /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [ 1310 fundingSubHeader("Date"), 1311 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("div", { 1312 className: "funding-filter-dropdown__sections", 1313 onKeyDown: stopMenuTypeahead, 1314 children: fundingDateSection 1315 }), 1316 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 1317 onClick: () => setFundingView("main"), 1318 children: "Done" 1319 }) 1320 ] }) : /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [
1321 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("div", { 1322 className: "funding-filter-dropdown__series", 1323 children: FUNDING_SERIES_OPTIONS.filter((series) => !availableFundingTypes || FUNDING_SERIES_MAPPING[series.value]?.some((type_7) => availableFundingTypes.has(type_7))).map((series_0) => /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenuItem, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Checkbox, { 1324 id: `filter-funding-${series_0.value}`, 1325 checked: fundingSeries.includes(series_0.value), 1326 onChange: () => setFundingSeries((prev_3) => prev_3.includes(series_0.value) ? prev_3.filter((s_0) => s_0 !== series_0.value) : [...prev_3, series_0.value]) 1327 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("label", { 1328 htmlFor: `filter-funding-${series_0.value}`, 1329 children: series_0.label 1330 })] }, series_0.value)) 1331 }), 1332 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("hr", { className: "dropdown-divider" }), 1333 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterSummaryRow, { 1334 label: "Series amount", 1335 summary: fundingAmountSummary, 1336 onClick: () => setFundingView("amount") 1337 }),
1338 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterSummaryRow, { 1339 label: "Date", 1340 summary: fundingDateSummary, 1341 onClick: () => setFundingView("date") 1342 }), 1343 /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 1344 disabled: fundingApplyDisabled, 1345 onClick: applyFundingFilters, 1346 children: "Apply filters" 1347 }) 1348 ] }); 1349 const technologiesByCategory = technologies.reduce((acc, tech) => { 1350 if (!acc[tech.category]) acc[tech.category] = []; 1351 acc[tech.category].push({ 1352 value: tech.slug, 1353 label: tech.name, 1354 category: tech.category 1355 }); 1356 return acc; 1357 }, {}); 1358 const technologyOptions = Object.entries(technologiesByCategory).flatMap(([category, techs]) => techs.map((tech_0) => ({ 1359 ...tech_0, 1360 group: category 1361 }))); 1362 const technologiesFilterBody = /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(import_jsx_runtime$16.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Select, { 1363 options: technologyOptions, 1364 placeholder: "Select technologies...", 1365 isMulti: true, 1366 value: selectedTechnologies.map((slug) => technologyOptions.find((opt_4) => opt_4.value === slug)), 1367 onChange: (selected_2) => { 1368 setSelectedTechnologies((selected_2 || []).map((opt_5) => opt_5.value)); 1369 }, 1370 styles: INLINE_MENU_STYLES 1371 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuSubmit, { 1372 disabled: selectedTechnologies.length === 0 && !filters.technologies?.length, 1373 onClick: () => applyFilters(), 1374 children: "Apply filters" 1375 })] }); 1376 const technologiesFilter = visibleFilters.includes("technologies") && technologies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1377 className: "h-filter", 1378 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1379 open: openFilter === "technologies", 1380 onOpenChange: (open) => showFilter(open ? "technologies" : null), 1381 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1382 asChild: true, 1383 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1384 className: "h-filter__button", 1385 size: "sm", 1386 ref: technologiesTriggerRef, 1387 children: ["Technologies", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1388 }) 1389 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1390 className: "overflow-visible", 1391 tabNavigable: true, 1392 children: technologiesFilterBody 1393 })] 1394 }), filters.technologies?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1395 labels: filters.technologies.map((slug_0) => technologies.find((tech_1) => tech_1.slug === slug_0)?.name ?? slug_0), 1396 removeAriaLabel: t$1("js.filters.remove"), 1397 onRemove: () => { 1398 setSelectedTechnologies([]); 1399 setFilters({ 1400 ...filters, 1401 technologies: [] 1402 }); 1403 } 1404 })] 1405 }); 1406 return /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)("div", { 1407 className: "ds-filters flex-nowrap", 1408 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1409 className: "d-flex flex-wrap gap-2", 1410 children: [ 1411 visibleFilters[0] === "technologies" && technologiesFilter,
1412 visibleFilters.includes("location") && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1413 className: "h-filter", 1414 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1415 open: openFilter === "location", 1416 onOpenChange: (open_0) => { 1417 if (open_0) setPinnedLocations(selectedLocation); 1418 showFilter(open_0 ? "location" : null); 1419 }, 1420 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1421 asChild: true, 1422 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1423 className: "h-filter__button", 1424 size: "sm", 1425 ref: locationTriggerRef, 1426 children: ["Location", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1427 }) 1428 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1429 className: "location-filter-dropdown", 1430 headerNavigable: true, 1431 tabNavigable: true, 1432 children: locationFilterBody 1433 })] 1434 }), filters.location?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1435 labels: filters.location.map((loc_4) => loc_4.included ? loc_4.label : `${t$1("js.filters.location.exclude")}: ${loc_4.label}`), 1436 removeAriaLabel: t$1("js.filters.remove"), 1437 onRemove: () => { 1438 setSelectedLocation([]); 1439 setFilters({ 1440 ...filters, 1441 location: [] 1442 }); 1443 } 1444 })] 1445 }), 1446 visibleFilters.includes("industry") && industries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1447 className: "h-filter", 1448 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1449 open: openFilter === "industry", 1450 onOpenChange: (open_1) => showFilter(open_1 ? "industry" : null), 1451 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1452 asChild: true, 1453 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1454 className: "h-filter__button", 1455 size: "sm", 1456 ref: industryTriggerRef, 1457 children: ["Industry", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1458 }) 1459 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1460 tabNavigable: true, 1461 children: industryFilterBody 1462 })] 1463 }), filters.industries?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1464 labels: filters.industries.map((id_1) => industries.find((ind_0) => ind_0.id === id_1)?.name ?? String(id_1)), 1465 removeAriaLabel: t$1("js.filters.remove"), 1466 onRemove: () => { 1467 setSelectedIndustries([]); 1468 setFilters({ 1469 ...filters, 1470 industries: [] 1471 }); 1472 } 1473 })] 1474 }),
1475 visibleFilters.includes("size") && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1476 className: "h-filter", 1477 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1478 open: openFilter === "size", 1479 onOpenChange: (open_2) => showFilter(open_2 ? "size" : null), 1480 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1481 asChild: true, 1482 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1483 className: "h-filter__button", 1484 size: "sm", 1485 ref: sizeTriggerRef, 1486 children: ["Size", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1487 }) 1488 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1489 tabNavigable: true, 1490 children: sizeFilterBody 1491 })] 1492 }), filters.companySizes?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1493 labels: filters.companySizes.map((size_1) => formatCompanySizeRangeLabel(size_1)), 1494 removeAriaLabel: t$1("js.filters.remove"), 1495 onRemove: () => { 1496 setSelectedSizes([]); 1497 setFilters({ 1498 ...filters, 1499 companySizes: [] 1500 }); 1501 } 1502 })] 1503 }),
1504 visibleFilters.includes("type") && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1505 className: "h-filter", 1506 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1507 open: openFilter === "type", 1508 onOpenChange: (open_3) => { 1509 if (open_3) setTypeOrder(computeTypeOrder(selectedTypes)); 1510 showFilter(open_3 ? "type" : null); 1511 }, 1512 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1513 asChild: true, 1514 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1515 className: "h-filter__button", 1516 size: "sm", 1517 ref: typeTriggerRef, 1518 children: ["Type", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1519 }) 1520 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1521 tabNavigable: true, 1522 children: typeFilterBody 1523 })] 1524 }), filters.companyTypes?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1525 labels: filters.companyTypes.map((type_8) => type_8.included ? type_8.label : `${t$1("js.filters.location.exclude")}: ${type_8.label}`), 1526 removeAriaLabel: t$1("js.filters.remove"), 1527 onRemove: () => { 1528 setSelectedTypes([]); 1529 setFilters({ 1530 ...filters, 1531 companyTypes: [] 1532 }); 1533 } 1534 })] 1535 }),
1536 visibleFilters.includes("year") && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1537 className: "h-filter", 1538 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1539 open: openFilter === "year", 1540 onOpenChange: (open_4) => showFilter(open_4 ? "year" : null), 1541 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1542 asChild: true, 1543 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1544 className: "h-filter__button", 1545 size: "sm", 1546 ref: yearTriggerRef, 1547 children: ["Year founded", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1548 }) 1549 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1550 className: "overflow-visible", 1551 tabNavigable: true, 1552 children: yearFoundedFilterBody 1553 })] 1554 }), filters.yearFounded && (() => { 1555 const isInterval = filters.yearFounded.mode === "interval"; 1556 return /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1557 summary: isInterval ? `${filters.yearFounded.from || "?"} - ${filters.yearFounded.to || "?"}` : void 0, 1558 labels: isInterval ? [] : (filters.yearFounded.years || []).map(String), 1559 removeAriaLabel: t$1("js.filters.remove"), 1560 onRemove: () => { 1561 setSelectedYearFounded(void 0); 1562 setFilters({ 1563 ...filters, 1564 yearFounded: void 0 1565 }); 1566 } 1567 }); 1568 })()] 1569 }),
1570 visibleFilters.includes("funding") && /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)("div", { 1571 className: "h-filter", 1572 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(DropdownMenu, { 1573 open: openFilter === "funding", 1574 onOpenChange: (open_5) => { 1575 if (open_5) setFundingView("main"); 1576 showFilter(open_5 ? "funding" : null); 1577 }, 1578 children: [/* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuTrigger, { 1579 asChild: true, 1580 children: /* @__PURE__ */ (0, import_jsx_runtime$16.jsxs)(Button, { 1581 className: "h-filter__button", 1582 size: "sm", 1583 ref: fundingTriggerRef, 1584 children: ["Funding", /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Icon, { icon: "angle-down" })] 1585 }) 1586 }), /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(DropdownMenuContent, { 1587 ref: fundingContentRef, 1588 className: "funding-filter-dropdown", 1589 tabNavigable: true, 1590 children: fundingFilterBody 1591 })] 1592 }), filters.funding && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(FilterValue, { 1593 labels: ["Funding"], 1594 removeAriaLabel: t$1("js.filters.remove"), 1595 onRemove: () => { 1596 setSelectedFunding(void 0); 1597 setFilters({ 1598 ...filters, 1599 funding: void 0 1600 }); 1601 } 1602 })] 1603 }), 1604 visibleFilters[0] !== "technologies" && technologiesFilter, 1605 additionalFilters, 1606 anyFiltersSet && /* @__PURE__ */ (0, import_jsx_runtime$16.jsx)(Button, { 1607 variant: "ghost", 1608 size: "sm", 1609 onClick: clearFilters, 1610 children: "Clear filters" 1611 }) 1612 ] 1613 }) 1614 }); 1615 }; 1616 filterCompanies = (companies, filters) => { 1617 let filtered = companies; 1618 if (filters.location.length > 0) { 1619 const includeLocations = filters.location.filter((loc) => loc.included); 1620 const excludeLocations = filters.location.filter((loc) => !loc.included); 1621 filtered = filtered.filter((company) => { 1622 if (excludeLocations.some((loc) => matchesLocation(company, loc))) return false; 1623 if (includeLocations.length > 0) return includeLocations.some((loc) => matchesLocation(company, loc)); 1624 return true; 1625 }); 1626 } 1627 if (filters.industries.length > 0) {} 1628 if (filters.companySizes.length > 0) filtered = filtered.filter((company) => filters.companySizes.includes(company.size || "")); 1629 if (filters.companyTypes.length > 0) { 1630 const includeTypes = filters.companyTypes.filter((type) => type.included); 1631 const excludeTypes = filters.companyTypes.filter((type) => !type.included); 1632 filtered = filtered.filter((company) => { 1633 const companyType = normalizeCompanyType(company.company_type); 1634 if (excludeTypes.some((type) => companyType === normalizeCompanyType(type.value))) return false; 1635 if (includeTypes.length > 0) return includeTypes.some((type) => companyType === normalizeCompanyType(type.value)); 1636 return true; 1637 }); 1638 } 1639 if (filters.technologies.length > 0) filtered = filtered.filter((company) => filters.technologies.some((tech) => company.technologies_slugs.includes(tech))); 1640 if (filters.yearFounded) { 1641 if (filters.yearFounded.mode === "interval") { 1642 if (filters.yearFounded.from) filtered = filtered.filter((company) => company.founded_year && company.founded_year >= filters.yearFounded.from); 1643 if (filters.yearFounded.to) filtered = filtered.filter((company) => company.founded_year && company.founded_year <= filters.yearFounded.to); 1644 } else if (filters.yearFounded.years) filtered = filtered.filter((company) => filters.yearFounded.years.includes(company.founded_year || 0)); 1645 } 1646 if (filters.funding) { 1647 const { series, amountFrom, amountTo, date, dateFrom, dateTo } = filters.funding; 1648 const seriesTypes = new Set(series.flatMap((value) => FUNDING_SERIES_MAPPING[value] ?? [])); 1649 const dateRange = fundingDateRange(date, dateFrom, dateTo); 1650 if (seriesTypes.size > 0 || amountFrom != null || amountTo != null || dateRange) filtered = filtered.filter((company) => (company.fundings ?? []).some((round) => { 1651 if (seriesTypes.size > 0 && !(round.type && seriesTypes.has(round.type))) return false;
1652 if (amountFrom != null || amountTo != null) { 1653 if (round.value_usd == null) return false; 1654 if (amountFrom != null && round.value_usd < amountFrom) return false; 1655 if (amountTo != null && round.value_usd > amountTo) return false; 1656 } 1657 if (dateRange) { 1658 if (!round.date) return false; 1659 const roundDate = new Date(round.date).getTime(); 1660 if (roundDate < dateRange.from || roundDate > dateRange.to) return false; 1661 } 1662 return true; 1663 })); 1664 } 1665 return filtered; 1666 }; 1667 normalizeCompanyType = (value) => value?.toLowerCase().replace(/[\s_-]+/g, " ").trim(); 1668 matchesLocation = (company, location) => { 1669 if (location.type === "country") return company.country === location.value; 1670 if (location.type === "state") return company.state === location.value; 1671 if (location.type === "city") { 1672 const [city, state, country] = location.value.split(","); 1673 return company.city === city && (state === "" || company.state === state) && (company.country ?? "") === country; 1674 } 1675 return false; 1676 }; 1677 })))(); 1678} 1679//#endregion 1680//#region app/frontend/react/components/search/companies-list.tsx 1681var import_react$14, import_jsx_runtime$15, ESTIMATED_COMPANY_SIZE, distinctNewEmailCount, CompaniesList; 1682function init_companies_list() { 1683 return (init_companies_list = __esmMin((() => { 1684 import_react$14 = /* @__PURE__ */ __toESM(require_react()); 1685 init_esm(); 1686 init_company_item(); 1687 init_loading_spinner(); 1688 init_button(); 1689 init_utils(); 1690 init_separator(); 1691 import_jsx_runtime$15 = require_jsx_runtime(); 1692 ESTIMATED_COMPANY_SIZE = 120; 1693 distinctNewEmailCount = (activities) => { 1694 const emailIds = /* @__PURE__ */ new Set(); 1695 for (const activity of activities ?? []) { 1696 if (activity.activity.event_type !== "email_added") continue; 1697 const data = activity.activity.associated_record_data; 1698 if (isNumber(data?.email_id)) emailIds.add(data.email_id); 1699 } 1700 return emailIds.size; 1701 }; 1702 CompaniesList = ({ className, companies, selectedIds, onSelect, isLoading, emptyMessage, onClearFilters, signalsByCompanyId, onShowNewPeople }) => { 1703 const parentRef = (0, import_react$14.useRef)(null); 1704 const [signalExpansionOverrides, setSignalExpansionOverrides] = (0, import_react$14.useState)(/* @__PURE__ */ new Map()); 1705 const soleCompanyId = companies.length === 1 && signalsByCompanyId ? companies[0].id : null; 1706 const signalIsExpanded = (0, import_react$14.useCallback)((id) => signalExpansionOverrides.get(id) ?? id === soleCompanyId, [signalExpansionOverrides, soleCompanyId]); 1707 const toggleSignals = (0, import_react$14.useCallback)((id_0) => { 1708 setSignalExpansionOverrides((previous) => { 1709 const next = new Map(previous); 1710 next.set(id_0, !(previous.get(id_0) ?? id_0 === soleCompanyId)); 1711 return next; 1712 }); 1713 }, [soleCompanyId]); 1714 const virtualizer = useVirtualizer({ 1715 count: companies.length, 1716 getScrollElement: () => parentRef.current, 1717 estimateSize: (0, import_react$14.useCallback)(() => ESTIMATED_COMPANY_SIZE, []), 1718 overscan: 5 1719 }); 1720 const handleSelect = (0, import_react$14.useCallback)((id_1) => { 1721 onSelect([id_1]); 1722 }, [onSelect]); 1723 if (isLoading && companies.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)("div", { 1724 className: "companies-list companies-list--loading", 1725 children: /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(LoadingSpinner, {}) 1726 }); 1727 if (companies.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)("div", { 1728 className: "companies-list companies-list--empty", 1729 children: [/* @__PURE__ */ (0, import_jsx_runtime$15.jsx)("p", { children: emptyMessage || "No companies found" }), onClearFilters && /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(Button, { 1730 variant: "default", 1731 size: "sm", 1732 onClick: onClearFilters, 1733 children: "Clear filters" 1734 })] 1735 }); 1736 return /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)("div", { 1737 className: cn("companies-list", className), 1738 ref: parentRef, 1739 style: { 1740 height: "100%", 1741 overflow: "auto" 1742 }, 1743 children: /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)("div", { 1744 style: { 1745 height: `${virtualizer.getTotalSize()}px`, 1746 width: "100%", 1747 position: "relative" 1748 }, 1749 children: [virtualizer.getVirtualItems().map((virtualItem) => { 1750 const company = companies[virtualItem.index]; 1751 if (!company) return null; 1752 const selected = selectedIds.includes(company.id); 1753 return /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)("div", { 1754 "data-index": virtualItem.index, 1755 ref: virtualizer.measureElement, 1756 style: { 1757 position: "absolute", 1758 top: 0, 1759 left: 0, 1760 width: "100%", 1761 transform: `translateY(${virtualItem.start}px)` 1762 }, 1763 children: /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)(CompanyItemRoot, { 1764 company, 1765 selected, 1766 onSelect: handleSelect, 1767 className: virtualItem.index === companies.length - 1 ? "company-item--last" : void 0, 1768 children: [/* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemCheckbox, { onSelect: handleSelect }
1768), /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)(CompanyItemContent, { children: [ 1769 /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)(CompanyItemHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemLogo, {}), /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)(CompanyItemInfo, { children: [ 1770 /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemName, {}), 1771 /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(Separator, { 1772 orientation: "vertical", 1773 full: true 1774 }), 1775 /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemLinks, {}), 1776 /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(Separator, { 1777 orientation: "vertical", 1778 full: true 1779 }), 1780 /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemEmailCount, { newCount: distinctNewEmailCount(signalsByCompanyId?.[company.id]) }) 1781 ] })] }), 1782 (company.size || company.category || company.address) && /* @__PURE__ */ (0, import_jsx_runtime$15.jsxs)(CompanyItemMetaList, { children: [ 1783 company.size && /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemMeta, { 1784 icon: "users", 1785 children: formatCompanySize(company.size) 1786 }), 1787 company.category && /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemMeta, { 1788 icon: "buildings", 1789 children: company.category 1790 }),
1791 company.address && /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemMeta, { 1792 icon: "map-location-dot", 1793 children: company.address 1794 }) 1795 ] }), 1796 signalsByCompanyId?.[company.id]?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(CompanyItemSignals, { 1797 signals: signalsByCompanyId[company.id], 1798 expanded: signalIsExpanded(company.id), 1799 onToggle: () => toggleSignals(company.id), 1800 onShowNewPeople: onShowNewPeople ? () => onShowNewPeople(company.id) : void 0 1801 }) 1802 ] })] 1803 }) 1804 }, virtualItem.key); 1805 }), isLoading && /* @__PURE__ */ (0, import_jsx_runtime$15.jsx)(LoadingSpinner, {})] 1806 }) 1807 }); 1808 }; 1809 })))(); 1810} 1811//#endregion 1812//#region app/frontend/react/components/opportunity-companies-sidepanel.tsx 1813function OpportunityCompaniesSidepanel({ id, domainIds, description, isOpen, onOpenChange, triggerVariant = "default" }) { 1814 const { companies, isLoading, selectedIds, handleSelect } = useCompanySearch({ 1815 domainIds, 1816 enabled: isOpen 1817 }); 1818 const [filters, setFilters] = (0, import_react$13.useState)({ 1819 location: [], 1820 industries: [], 1821 companySizes: [], 1822 companyTypes: [], 1823 technologies: [] 1824 }); 1825 const [view, setView] = (0, import_react$13.useState)("companies"); 1826 const [peopleProps, setPeopleProps] = (0, import_react$13.useState)(null); 1827 const [peopleLoading, setPeopleLoading] = (0, import_react$13.useState)(false); 1828 const [peopleError, setPeopleError] = (0, import_react$13.useState)(null); 1829 const rankedCompanies = (0, import_react$13.useMemo)(() => [...companies].sort((a, b) => (b.number_of_emails || 0) - (a.number_of_emails || 0)), [companies]); 1830 const filteredCompanies = (0, import_react$13.useMemo)(() => { 1831 return filterCompanies(rankedCompanies, filters); 1832 }, [rankedCompanies, filters]); 1833 const anyFiltersSet = filters.location.length > 0 || filters.industries.length > 0 || filters.companySizes.length > 0 || filters.companyTypes.length > 0 || filters.technologies.length > 0 || filters.yearFounded !== void 0 || filters.funding !== void 0; 1834 const selectedVisibleCompanies = (0, import_react$13.useMemo)(() => filteredCompanies.filter((company) => selectedIds.includes(company.id)), [filteredCompanies, selectedIds]); 1835 const hasVisibleSelection = selectedVisibleCompanies.length > 0; 1836 const targetedCompanies = hasVisibleSelection ? selectedVisibleCompanies : filteredCompanies; 1837 const peopleCount = (0, import_react$13.useMemo)(() => targetedCompanies.reduce((sum, company_0) => sum + (company_0.number_of_emails || 0), 0), [targetedCompanies]); 1838 const findPeopleText = hasVisibleSelection ? i18n.t("js.discover.index.find_people", { 1839 count: peopleCount, 1840 results: CurrencyHelper.numberWithDelimiter(peopleCount) 1841 }) : i18n.t("js.discover.index.find_all_people", { 1842 count: peopleCount, 1843 results: CurrencyHelper.numberWithDelimiter(peopleCount) 1844 }); 1845 const domainsParam = targetedCompanies.map((company_1) => company_1.value).join("|"); 1846 const peopleAbortRef = (0, import_react$13.useRef)(null); 1847 const fetchPeopleProps = (0, import_react$13.useCallback)(async () => { 1848 peopleAbortRef.current?.abort(); 1849 const controller = new AbortController(); 1850 peopleAbortRef.current = controller; 1851 setPeopleLoading(true); 1852 setPeopleError(null); 1853 try { 1854 const response = await fetch(`${leads_companies_people_path()}.json?from=companies&metadata_only=true`, { 1855 headers: { 1856 Accept: "application/json", 1857 "X-CSRF-Token": getCsrfToken() 1858 }, 1859 signal: controller.signal 1860 }); 1861 if (!response.ok) throw new Error(`${response.status}`); 1862 const data = await response.json(); 1863 if (!controller.signal.aborted) setPeopleProps(data); 1864 } catch (error) { 1865 if (controller.signal.aborted || error?.name === "AbortError") return; 1866 setPeopleError("Failed to load. Please try again."); 1867 } finally { 1868 if (!controller.signal.aborted) setPeopleLoading(false); 1869 } 1870 }, []); 1871 (0, import_react$13.useEffect)(() => { 1872 if (view !== "people" || peopleProps) return; 1873 fetchPeopleProps(); 1874 }, [ 1875 view, 1876 peopleProps, 1877 fetchPeopleProps 1878 ]); 1879 const leavePeopleLayer = (0, import_react$13.useCallback)(() => { 1880 peopleAbortRef.current?.abort(); 1881 setView("companies"); 1882 setPeopleProps(null); 1883 setPeopleError(null); 1884 }, []); 1885 const handleOpenChange = (open) => { 1886 if (!open) leavePeopleLayer(); 1887 onOpenChange(open); 1888 }; 1889 return /* @__PURE__ */ (0, import_jsx_runtime$14.jsxs)(Sheet, { 1890 open: isOpen, 1891 onOpenChange: handleOpenChange, 1892 children: [/* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SheetTrigger, { 1893 asChild: true, 1894 children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(Button, { 1895 variant: triggerVariant, 1896 size: "xs", 1897 children: "Show companies" 1898 }) 1899 }
1899), /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SheetContent, { 1900 size: "lg", 1901 className: "p-0 opportunity-sidepanel", 1902 children: view === "people" ? /* @__PURE__ */ (0, import_jsx_runtime$14.jsxs)(import_jsx_runtime$14.Fragment, { children: [ 1903 /* @__PURE__ */ (0, import_jsx_runtime$14.jsxs)(SheetHeader, { 1904 className: "sidepanel__header--with-back", 1905 children: [/* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SheetBack, { 1906 "aria-label": "Back to companies", 1907 onClick: leavePeopleLayer 1908 }), /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SheetTitle, { children: i18n.t("js.multi_domain_search.header") })] 1909 }), 1910 peopleLoading && /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)("div", { 1911 className: "d-flex flex-grow-1 align-items-center justify-content-center grey", 1912 children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(Icon, { 1913 loading: true, 1914 "aria-label": "Loading" 1915 }) 1916 }), 1917 peopleError && /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)("div", { 1918 className: "m-8 text-center text-danger", 1919 children: peopleError 1920 }),
1921 peopleProps && /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(MultiDomainSearchLegacy, { 1922 ...peopleProps, 1923 type: "domains", 1924 source: "companies", 1925 userLookalikeId: Number(id), 1926 filterParams: { domains: domainsParam }, 1927 numberOfCompanies: targetedCompanies.length, 1928 numberOfResults: peopleCount, 1929 genericEmailsCount: 0, 1930 personalEmailsCount: 0, 1931 hideHeader: true 1932 }) 1933 ] }) : /* @__PURE__ */ (0, import_jsx_runtime$14.jsxs)(import_jsx_runtime$14.Fragment, { children: [ 1934 /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SheetHeader, { 1935 className: "sidepanel__header--bordered", 1936 children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SheetTitle, { children: "Discover new companies" }) 1937 }), 1938 description && /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)("div", { 1939 className: "opportunity-sidepanel__overview", 1940 children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsxs)(Alert, { 1941 className: "sidepanel__alert h-alert--ai ai-audience-description", 1942 children: [/* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(AlertIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(Icon, { 1943 icon: "sparkle", 1944 size: "sm", 1945 className: "ai-gradient-icon-light" 1946 }) }
1946), /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(AlertContent, { children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(AlertDescription, { children: /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)("p", { 1947 className: "ai-audience-description__sentence mb-0", 1948 children: description 1949 }) }) })] 1950 }) 1951 }), 1952 /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(CompanyFilters, { 1953 filters, 1954 setFilters, 1955 clearFilters: () => clearFilters(setFilters), 1956 anyFiltersSet 1957 }), 1958 /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(CompaniesList, { 1959 className: "px-4", 1960 companies: filteredCompanies, 1961 selectedIds, 1962 onSelect: handleSelect, 1963 isLoading, 1964 emptyMessage: anyFiltersSet ? "No companies match your filters" : "No companies found", 1965 onClearFilters: anyFiltersSet ? () => clearFilters(setFilters) : void 0 1966 }), 1967 /* @__PURE__ */ (0, import_jsx_runtime$14.jsxs)(SheetFooter, { 1968 sticky: true, 1969 className: "opportunity-sidepanel__footer opportunity-companies-sidepanel__footer", 1970 children: [/* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(Button, { 1971 variant: "primary", 1972 disabled: isLoading || peopleCount === 0, 1973 onClick: () => setView("people"), 1974 children: findPeopleText 1975 }), /* @__PURE__ */ (0, import_jsx_runtime$14.jsx)(SaveCompaniesDropdown, { 1976 numberOfDomains: targetedCompanies.length, 1977 domains: targetedCompanies, 1978 filters: {}, 1979 size: null, 1980 disabled: isLoading || targetedCompanies.length === 0, 1981 onSaved: (domainIds_0, { listCreated }) => captureSignalsEvent("signals-saved", { 1982 signal_source: SIGNAL_SOURCE.hunterSuggestion, 1983 signal_id: Number(id), 1984 entity: "company", 1985 target: "company_list", 1986 count: domainIds_0.length || targetedCompanies.length, 1987 list_created: listCreated 1988 }) 1989 })] 1990 }) 1991 ] }) 1992 })] 1993 }); 1994} 1995var import_react$13, import_jsx_runtime$14, clearFilters; 1996function init_opportunity_companies_sidepanel() { 1997 return (init_opportunity_companies_sidepanel = __esmMin((() => { 1998 import_react$13 = /* @__PURE__ */ __toESM(require_react()); 1999 init_sheet(); 2000 init_alert(); 2001 init_button(); 2002 init_icon(); 2003 init_currency_helper(); 2004 init_routes(); 2005 init_utils(); 2006 init_useCompanySearch(); 2007 init_company_filters(); 2008 init_companies_list(); 2009 init_save_companies_dropdown(); 2010 init_multi_domain_search(); 2011 init_signals_analytics(); 2012 import_jsx_runtime$14 = require_jsx_runtime(); 2013 clearFilters = (setFilters) => { 2014 setFilters({ 2015 location: [], 2016 industries: [], 2017 companySizes: [], 2018 companyTypes: [], 2019 technologies: [] 2020 }); 2021 }; 2022 })))(); 2023} 2024//#endregion 2025//#region app/frontend/react/components/opportunity-signal-item.tsx 2026function OpportunityPeopleSidepanel({ id, emailIds, campaigns, setCampaigns, leadsLists, setLeadsLists, folders = [], numberOfCreditsLeft, description, isOpen, onOpenChange, triggerVariant = "default" }) { 2027 const [emailsToLoad, setEmailsToLoad] = (0, import_react$12.useState)([]); 2028 const [filters, setFilters] = (0, import_react$12.useState)({ 2029 requiredFields: [], 2030 departments: [], 2031 jobTitles: [], 2032 customJobTitleQueries: [], 2033 verificationStatus: [], 2034 hideAlreadySaved: false, 2035 location: [], 2036 peoplePerCompany: void 0 2037 }); 2038 const locationsMappings = useLocationMappings(); 2039 const userLookalikeId = Number(id); 2040 const { results, setResults, revealingIds, revealedIds, selectedIds, handleReveal: baseHandleReveal, handleRevealMultiple: baseHandleRevealMultiple, handleSelect } = useEmailSearch({ 2041 initialEmails: [], 2042 context: `dashboard-${id}`, 2043 source: "dashboard", 2044 userLookalikeId 2045 }); 2046 const signalsAnalytics = useSignalsSheetAnalytics(userLookalikeId); 2047 const handleReveal = (emailId) => signalsAnalytics.reveal(baseHandleReveal(emailId), 1, "single"); 2048 const handleRevealMultiple = (ids, options) => signalsAnalytics.reveal(baseHandleRevealMultiple(ids, options), ids.filter((emailId_0) => !results.find((r) => r.id === emailId_0)?.revealed).length, "bulk", { 2049 leadsListId: options?.leads_list_id, 2050 savedCount: ids.length 2051 }); 2052 const handleCampaignsSave = signalsAnalytics.wrapCampaignsSave(useCampaignsSave(results, campaigns, setCampaigns, setResults)); 2053 const handleLeadsListSave = signalsAnalytics.wrapLeadsListSave(useLeadsListSave(results, setLeadsLists, setResults)); 2054 const [tabSelected, setTabSelected] = (0, import_react$12.useState)("all_people"); 2055 const { isLoading, domains } = useEmailsLoading(emailsToLoad, setResults); 2056 const revealAllButtonRef = import_react$12.useRef(null); 2057 (0, import_react$12.useEffect)(() => { 2058 if (isOpen && emailsToLoad.length === 0) setEmailsToLoad(emailIds); 2059 }, [ 2060 isOpen, 2061 emailIds,
2062 emailsToLoad.length 2063 ]); 2064 const deferredResults = (0, import_react$12.useDeferredValue)(results); 2065 const { taxonomy: positionTaxonomy } = usePositionTaxonomy(); 2066 const { titles: jobTitles, counts: jobTitleCounts } = useJobTitleSuggestions(deferredResults, filters, locationsMappings, revealedIds, positionTaxonomy); 2067 const deferredJobTitles = (0, import_react$12.useDeferredValue)(jobTitles); 2068 const deferredJobTitleCounts = (0, import_react$12.useDeferredValue)(jobTitleCounts); 2069 const filteredResults = (0, import_react$12.useMemo)(() => { 2070 return filterFunction(deferredResults, filters, locationsMappings, revealedIds, [], positionTaxonomy).sort(sortFunction); 2071 }, [ 2072 deferredResults, 2073 filters, 2074 locationsMappings, 2075 positionTaxonomy, 2076 revealedIds 2077 ]); 2078 const departmentOrder = (0, import_react$12.useMemo)(() => departmentOrderByCount(results, positionTaxonomy), [positionTaxonomy, results]); 2079 const { allPeopleResults, decisionMakersResults, genericResults } = useSplittedResults(filteredResults); 2080 const { revealAllOperationCost } = useRevealCost({ 2081 selectedIds, 2082 tabSelected, 2083 results, 2084 allPeopleResults, 2085 decisionMakersResults, 2086 genericResults, 2087 numberOfCreditsLeft 2088 }); 2089 const [saveMenuOpen, setSaveMenuOpen] = (0, import_react$12.useState)(false); 2090 const revealCurrentTab = (leadsListId) => { 2091 const idsToReveal = tabSelected === "all_people" ? allPeopleResults.map((result) => result.id) : tabSelected === "decision_makers" ? decisionMakersResults.map((result_0) => result_0.id) : tabSelected === "generic" ? genericResults.map((result_1) => result_1.id) : []; 2092 handleRevealMultiple(idsToReveal, leadsListId ? { leads_list_id: leadsListId } : void 0); 2093 }; 2094 const handleLeadManagerSave = async (name, listId) => { 2095 if (!leadsLists.find((list) => list.id === listId)) { 2096 setLeadsLists((prev) => [...prev, { 2097 id: listId, 2098 name 2099 }]); 2100 signalsAnalytics.markListCreated(listId); 2101 } 2102 if (selectedIds.length > 0) handleRevealMultiple(selectedIds, { leads_list_id: listId }); 2103 else revealCurrentTab(listId); 2104 setSaveMenuOpen(false); 2105 }; 2106 const anyFiltersSet = filters.departments.length > 0 || filters.jobTitles.length > 0 || filters.requiredFields.length > 0 || filters.hideAlreadySaved || filters.verificationStatus.length > 0 || filters.location.length > 0 || !!filters.peoplePerCompany || !!filters.sourceType; 2107 return /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(Sheet, { 2108 open: isOpen, 2109 onOpenChange, 2110 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(SheetTrigger, { 2111 asChild: true, 2112 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Button, { 2113 variant: triggerVariant, 2114 size: "xs", 2115 children: "Show people" 2116 }) 2117 }), /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(SheetContent, { 2118 size: "lg", 2119 className: "p-0 opportunity-sidepanel", 2120 children: [
2121 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(SheetHeader, { 2122 className: "sidepanel__header--bordered", 2123 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(SheetTitle, { children: "Discover new leads" }) 2124 }), 2125 description && /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("div", { 2126 className: "opportunity-sidepanel__overview", 2127 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(Alert, { 2128 className: "sidepanel__alert h-alert--ai ai-audience-description", 2129 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(AlertIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Icon, { 2130 icon: "sparkle", 2131 size: "sm", 2132 className: "ai-gradient-icon-light" 2133 }) }), /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(AlertContent, { children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(AlertDescription, { children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("p", { 2134 className: "ai-audience-description__sentence mb-0", 2135 children: description 2136 }) }) })] 2137 }) 2138 }),
2139 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Filters, { 2140 possibleFilters: [ 2141 "department", 2142 "job_title", 2143 "already_saved", 2144 "verification_status", 2145 "required_field", 2146 "location", 2147 "people_per_company", 2148 "source_type" 2149 ], 2150 jobTitles: deferredJobTitles, 2151 jobTitleCounts: deferredJobTitleCounts, 2152 anyFiltersSet, 2153 clearFilters: () => clearFilters$1(setFilters), 2154 setFilters, 2155 filters 2156 }), 2157 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(EmailsList, { 2158 results: filteredResults, 2159 departmentOrder, 2160 hideCompanyDetails: false, 2161 domains, 2162 anyFiltersSet, 2163 selectedIds, 2164 campaigns, 2165 leadsLists, 2166 folders, 2167 handleReveal, 2168 handleLeadsListSave, 2169 handleCampaignsSave, 2170 handleSelect, 2171 showGenericAlert: false, 2172 isLoading, 2173 onTabSelected: setTabSelected, 2174 clearFilters: () => clearFilters$1(setFilters) 2175 }), 2176 /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(SheetFooter, { 2177 sticky: true, 2178 className: "opportunity-sidepanel__footer", 2179 children: [revealedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(Snackbar, { children: [ 2180 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Icon, { 2181 icon: "check-circle", 2182 variant: "solid", 2183 size: "sm", 2184 className: "text-success" 2185 }), 2186 /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(SnackbarText, { children: [ 2187 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("strong", { 2188 className: "fw-medium", 2189 children: i18n.t("js.multi_domain_search.footer.leads", { 2190 count: revealedIds.length, 2191 pretty_count: CurrencyHelper.numberWithDelimiter(revealedIds.length) 2192 }) 2193 }), 2194 "\xA0", 2195 i18n.t("js.multi_domain_search.footer.saved") 2196 ] }), 2197 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(SnackbarLink, { 2198 href: leads_path({ updated_at: "last1h" }), 2199 turboFrame: "_top", 2200 children: i18n.t("js.multi_domain_search.footer.see_in_leads") 2201 }) 2202 ] }
2202), /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(DropdownMenu, { 2203 open: saveMenuOpen, 2204 onOpenChange: setSaveMenuOpen, 2205 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(DropdownMenuTrigger, { 2206 asChild: true, 2207 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(Button, { 2208 variant: "primary", 2209 children: [revealingIds.length > 0 ? i18n.t("js.multi_domain_search.footer.saving_leads") : i18n.t("js.multi_domain_search.footer.save_leads", { count: selectedIds.length }), /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(ButtonSuffix, { children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(CostIndicator, { 2210 variant: "bare", 2211 cost: revealAllOperationCost 2212 }) })] 2213 }) 2214 }), /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(DropdownMenuContent, { 2215 onPointerDownOutside: (event) => { 2216 if (isLeadSavePickerLayer(event)) event.preventDefault(); 2217 }, 2218 onFocusOutside: (event_0) => { 2219 if (isLeadSavePickerLayer(event_0)) event_0.preventDefault(); 2220 }, 2221 children: [ 2222 /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(DropdownMenuHint, { children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Icon, { 2223 icon: "coin", 2224 size: "xs", 2225 className: "cost-indicator__icon" 2226 }), i18n.t("js.multi_domain_search.footer.cost_tooltip", { 2227 count: selectedIds.length, 2228 cost: CurrencyHelper.numberWithDelimiter(revealAllOperationCost) 2229 })] }), 2230 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(DropdownMenuDivider, {}), 2231 /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(LeadSaveManager, { 2232 nested: true, 2233 className: "w-100", 2234 id: "multi-domain-search", 2235 leadsLists, 2236 folders, 2237 onSave: handleLeadManagerSave, 2238 disableButtons: revealingIds.length > 0, 2239 cost: revealAllOperationCost, 2240 hint: i18n.t("js.multi_domain_search.footer.existing_leads_hint", { 2241 count: 3, 2242 formatted_count: CurrencyHelper.numberWithDelimiter(3) 2243 }), 2244 children: [i18n.t("js.multi_domain_search.footer.save_in_a_specific_list"), /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Icon, { 2245 icon: "angle-right", 2246 size: "sm", 2247 "aria-hidden": "true" 2248 })] 2249 }), 2250 /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(DropdownMenuItem, { 2251 asChild: true, 2252 closeOnClick: true, 2253 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("button", { 2254 ref: revealAllButtonRef, 2255 disabled: revealingIds.length > 0, 2256 onClick: () => { 2257 if (selectedIds.length > 0) handleRevealMultiple(selectedIds); 2258 else revealCurrentTab(); 2259 }, 2260 children: i18n.t("js.multi_domain_search.footer.save_in_leads") 2261 }) 2262 }) 2263 ] 2264 })] 2265 })] 2266 }) 2267 ] 2268 })] 2269 }); 2270} 2271var import_react$12, import_jsx_runtime$13, OPPORTUNITY_SIGNAL_VARIANT, renderVariantTag, renderEntityTag, OpportunitySignalItem; 2272function init_opportunity_signal_item() { 2273 return (init_opportunity_signal_item = __esmMin((() => { 2274 import_react$12 = /* @__PURE__ */ __toESM(require_react()); 2275 init_utils(); 2276 init_tag(); 2277 init_icon(); 2278 init_button(); 2279 init_cost_indicator(); 2280 init_sheet(); 2281 init_alert(); 2282 init_filters(); 2283 init_emails_list(); 2284 init_routes(); 2285 init_snackbar(); 2286 init_currency_helper(); 2287 init_useLocationMappings(); 2288 init_usePositionTaxonomy(); 2289 init_useEmailSearch(); 2290 init_useEmailsLoading(); 2291 init_useCampaignsSave(); 2292 init_useLeadsListSave(); 2293 init_useJobTitleSuggestions(); 2294 init_useSplittedResults(); 2295 init_useRevealCost(); 2296 init_lead_save_manager(); 2297 init_campaigns_context(); 2298 init_leads_lists_context(); 2299 init_opportunity_companies_sidepanel(); 2300 init_signals_analytics(); 2301 init_text_helpers(); 2302 init_dropdown_menu(); 2303 import_jsx_runtime$13 = require_jsx_runtime(); 2304 OPPORTUNITY_SIGNAL_VARIANT = { 2305 proactiveSuggestion: "proactive-suggestion", 2306 savedSearchUpdate: "saved-search-update" 2307 }; 2308 renderVariantTag = (variant) => { 2309 switch (variant) {
2310 case OPPORTUNITY_SIGNAL_VARIANT.proactiveSuggestion: return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Tag, { 2311 size: "md", 2312 variant: "ai", 2313 icon: "sparkle", 2314 label: "Hunter suggestion" 2315 }); 2316 case OPPORTUNITY_SIGNAL_VARIANT.savedSearchUpdate: return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Tag, { 2317 size: "md", 2318 variant: "info", 2319 icon: "bookmark", 2320 label: "New results" 2321 }); 2322 } 2323 }; 2324 renderEntityTag = (entityType, entityCount, approximate) => { 2325 const formattedCount = formatApproximateNumber(entityCount, approximate); 2326 switch (entityType) { 2327 case "lead": return /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(TagPrimitive.TagRoot, { 2328 size: "md", 2329 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(TagPrimitive.TagIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Icon, { 2330 icon: "users", 2331 size: "sm", 2332 opticalCorrection: true 2333 }) }), /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)(TagPrimitive.TagLabel, { children: [formattedCount, " people"] })] 2334 }); 2335 case "company": return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Tag, { 2336 size: "md", 2337 icon: "buildings", 2338 label: `${formattedCount} companies` 2339 }); 2340 } 2341 }; 2342 OpportunitySignalItem = import_react$12.forwardRef((props, ref) => { 2343 const { className, title, description, visited, entityType, entityCount, entityCountApproximate = false, onClick, variant, emphasizeUnread = false, ...variantProps } = props; 2344 const ctaVariant = emphasizeUnread && !visited ? "primary" : "default"; 2345 const proactiveSuggestionProps = variant === OPPORTUNITY_SIGNAL_VARIANT.proactiveSuggestion ? variantProps : {}; 2346 const savedSearchUpdateProps = variant === OPPORTUNITY_SIGNAL_VARIANT.savedSearchUpdate ? variantProps : {}; 2347 const { id, numberOfCreditsLeft, lookalikeIds, isOpen, onOpenChange, ...proactiveSuggestionRest } = proactiveSuggestionProps; 2348 const { href, actionLabel, ...savedSearchUpdateRest } = savedSearchUpdateProps; 2349 const rest = variant === OPPORTUNITY_SIGNAL_VARIANT.savedSearchUpdate ? savedSearchUpdateRest : proactiveSuggestionRest; 2350 const { campaigns, setCampaigns } = useCampaigns(); 2351 const { leadsLists, setLeadsLists, folders } = useLeadsLists(); 2352 const [internalSidepanelOpen, setInternalSidepanelOpen] = (0, import_react$12.useState)(false); 2353 const isControlled = isOpen !== void 0 && onOpenChange !== void 0; 2354 const isSidepanelOpen = isControlled ? isOpen : internalSidepanelOpen; 2355 const setIsSidepanelOpen = isControlled ? onOpenChange : setInternalSidepanelOpen; 2356 const renderLink = () => { 2357 if (variant === OPPORTUNITY_SIGNAL_VARIANT.savedSearchUpdate) return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("a", { 2358 className: "opportunity-signal-item__link", 2359 href, 2360 onClick: () => onClick?.(), 2361 "aria-label": `View opportunity: ${title}`, 2362 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("span", { 2363 className: "sr-only", 2364 children: `View opportunity: ${title}` 2365 }) 2366 }); 2367 return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("button", { 2368 className: "opportunity-signal-item__link", 2369 onClick: () => { 2370 setIsSidepanelOpen(true); 2371 onClick?.(); 2372 }, 2373 "aria-label": `View opportunity: ${title}`, 2374 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("span", { 2375 className: "sr-only", 2376 children: `View opportunity: ${title}` 2377 }) 2378 }); 2379 }; 2380 const renderAction = () => { 2381 if (variant === OPPORTUNITY_SIGNAL_VARIANT.savedSearchUpdate) return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Button, { 2382 variant: ctaVariant, 2383 size: "xs", 2384 asChild: true, 2385 children: /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("a", { 2386 href, 2387 onClick: () => onClick?.(), 2388 children: actionLabel ?? "Open in Discover" 2389 }) 2390 }); 2391 if (entityType === "lead") return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(OpportunityPeopleSidepanel, { 2392 id: id.toString(), 2393 emailIds: lookalikeIds, 2394 numberOfCreditsLeft, 2395 campaigns, 2396 leadsLists, 2397 folders, 2398 setCampaigns,
2399 setLeadsLists, 2400 isOpen: isSidepanelOpen, 2401 onOpenChange: setIsSidepanelOpen, 2402 description, 2403 triggerVariant: ctaVariant 2404 }); 2405 return /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(OpportunityCompaniesSidepanel, { 2406 id: id.toString(), 2407 isOpen: isSidepanelOpen, 2408 onOpenChange: setIsSidepanelOpen, 2409 domainIds: lookalikeIds, 2410 description, 2411 triggerVariant: ctaVariant 2412 }); 2413 }; 2414 return /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)("article", { 2415 ref, 2416 className: cn("opportunity-signal-item", className), 2417 ...rest, 2418 children: [ 2419 renderLink(), 2420 /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)("div", { 2421 className: "opportunity-signal-item__content", 2422 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)("div", { 2423 className: "opportunity-signal-item__header", 2424 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("span", { 2425 className: "opportunity-signal-item__title", 2426 children: title 2427 }), !visited && /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)(Tag, { 2428 size: "sm", 2429 variant: "info", 2430 label: "New" 2431 })] 2432 }), /* @__PURE__ */ (0, import_jsx_runtime$13.jsx)("span", { 2433 className: "opportunity-signal-item__description", 2434 children: description 2435 })] 2436 }), 2437 /* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)("div", { 2438 className: "opportunity-signal-item__footer", 2439 children: [/* @__PURE__ */ (0, import_jsx_runtime$13.jsxs)("div", { 2440 className: "opportunity-signal-item__badges", 2441 children: [ 2442 renderVariantTag(variant), 2443 " ", 2444 renderEntityTag(entityType, entityCount, entityCountApproximate) 2445 ] 2446 }), renderAction()] 2447 }) 2448 ] 2449 }); 2450 }); 2451 OpportunitySignalItem.displayName = "OpportunitySignalItem"; 2452 })))(); 2453} 2454//#endregion 2455//#region app/frontend/react/components/dashboard_shortcuts.tsx 2456var import_compiler_runtime$10, import_react$11, import_jsx_runtime$12, DashboardShortcut, DashboardShortcuts; 2457function init_dashboard_shortcuts() { 2458 return (init_dashboard_shortcuts = __esmMin((() => { 2459 import_compiler_runtime$10 = require_compiler_runtime(); 2460 import_react$11 = /* @__PURE__ */ __toESM(require_react()); 2461 init_utils(); 2462 init_card(); 2463 init_icon(); 2464 init_button(); 2465 init_confetti(); 2466 init_dropdown_menu(); 2467 init_routes(); 2468 import_jsx_runtime$12 = require_jsx_runtime(); 2469 DashboardShortcut = import_react$11.forwardRef((t0, ref) => { 2470 const $ = (0, import_compiler_runtime$10.c)(27); 2471 let children; 2472 let className; 2473 let description; 2474 let icon; 2475 let iconVariant; 2476 let props; 2477 let title; 2478 if ($[0] !== t0) { 2479 ({className, icon, iconVariant, title, description, children, ...props} = t0); 2480 $[0] = t0; 2481 $[1] = children; 2482 $[2] = className; 2483 $[3] = description; 2484 $[4] = icon; 2485 $[5] = iconVariant; 2486 $[6] = props; 2487 $[7] = title; 2488 } else { 2489 children = $[1]; 2490 className = $[2]; 2491 description = $[3]; 2492 icon = $[4]; 2493 iconVariant = $[5]; 2494 props = $[6]; 2495 title = $[7]; 2496 } 2497 let t1; 2498 if ($[8] !== className) { 2499 t1 = cn("dashboard-shortcut", className); 2500 $[8] = className; 2501 $[9] = t1; 2502 } else t1 = $[9]; 2503 let t2; 2504 if ($[10] !== icon || $[11] !== iconVariant) {
2505 t2 = /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("div", { 2506 className: "dashboard-shortcut__icon", 2507 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Icon, { 2508 icon, 2509 variant: iconVariant, 2510 size: "md", 2511 "aria-hidden": "true" 2512 }) 2513 }); 2514 $[10] = icon; 2515 $[11] = iconVariant; 2516 $[12] = t2; 2517 } else t2 = $[12]; 2518 let t3; 2519 if ($[13] !== title) { 2520 t3 = /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("h3", { 2521 className: "dashboard-shortcut__title", 2522 children: title 2523 }); 2524 $[13] = title; 2525 $[14] = t3; 2526 } else t3 = $[14]; 2527 let t4; 2528 if ($[15] !== description) { 2529 t4 = /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("p", { 2530 className: "dashboard-shortcut__description", 2531 children: description 2532 }); 2533 $[15] = description; 2534 $[16] = t4; 2535 } else t4 = $[16]; 2536 let t5; 2537 if ($[17] !== children || $[18] !== t2 || $[19] !== t3 || $[20] !== t4) { 2538 t5 = /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)(CardContent, { children: [ 2539 t2, 2540 t3, 2541 t4, 2542 children 2543 ] }); 2544 $[17] = children; 2545 $[18] = t2; 2546 $[19] = t3; 2547 $[20] = t4; 2548 $[21] = t5; 2549 } else t5 = $[21]; 2550 let t6; 2551 if ($[22] !== props || $[23] !== ref || $[24] !== t1 || $[25] !== t5) {
2552 t6 = /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Card, { 2553 ref, 2554 className: t1, 2555 ...props, 2556 children: t5 2557 }); 2558 $[22] = props; 2559 $[23] = ref; 2560 $[24] = t1; 2561 $[25] = t5; 2562 $[26] = t6; 2563 } else t6 = $[26]; 2564 return t6; 2565 }); 2566 DashboardShortcut.displayName = "DashboardShortcut"; 2567 DashboardShortcuts = import_react$11.forwardRef(({ className, title, subtitle, variant = "default", onClose, ...props }, ref) => { 2568 if (variant === "success") return /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)("div", { 2569 ref, 2570 className: cn("dashboard-shortcuts", "dashboard-shortcuts--success", className), 2571 ...props, 2572 children: [ 2573 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Confetti, { timeout: 5 }), 2574 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("div", { className: "dashboard-shortcuts__gradient" }), 2575 onClose && /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Button, { 2576 variant: "ghost", 2577 iconOnly: true, 2578 className: "dashboard-shortcuts__close", 2579 onClick: onClose, 2580 "aria-label": "Close",
2581 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Icon, { 2582 icon: "times", 2583 "aria-hidden": "true" 2584 }) 2585 }), 2586 /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)("div", { 2587 className: "dashboard-shortcuts__success-content", 2588 children: [ 2589 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("div", { 2590 className: "dashboard-shortcuts__success-icon", 2591 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Icon, { 2592 icon: "circle-check", 2593 "aria-hidden": "true", 2594 variant: "light", 2595 color: "success" 2596 }) 2597 }), 2598 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("h2", { 2599 className: "dashboard-shortcuts__success-title", 2600 children: i18n.t("js.dashboard.index.shortcuts.success.title") 2601 }), 2602 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("span", { 2603 className: "dashboard-shortcuts__success-description", 2604 children: i18n.t("js.dashboard.index.shortcuts.success.description") 2605 }) 2606 ] 2607 }) 2608 ] 2609 });
2610 return /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)("div", { 2611 ref, 2612 className: cn("dashboard-shortcuts", className), 2613 ...props, 2614 children: [ 2615 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("div", { 2616 className: "dashboard-shortcuts__logo", 2617 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("img", { 2618 src: "/images/hunter-logo-outline.svg", 2619 alt: "", 2620 "aria-hidden": "true", 2621 className: "dashboard-shortcuts__logo-img" 2622 }) 2623 }), 2624 /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)("div", { 2625 className: "dashboard-shortcuts__header", 2626 children: [/* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("h2", { 2627 className: "dashboard-shortcuts__title", 2628 children: title 2629 }), subtitle && /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("p", { 2630 className: "dashboard-shortcuts__subtitle", 2631 children: subtitle 2632 })] 2633 }), 2634 /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)("div", { 2635 className: "dashboard-shortcuts__cards", 2636 children: [
2637 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(DashboardShortcut, { 2638 icon: "h-discover", 2639 iconVariant: "custom", 2640 title: i18n.t("js.dashboard.index.discover_card.title"), 2641 description: i18n.t("js.dashboard.index.discover_card.description"), 2642 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Button, { 2643 variant: "primary", 2644 size: "sm", 2645 asChild: true, 2646 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("a", { 2647 href: discovers_path(), 2648 children: i18n.t("js.dashboard.index.discover_card.cta") 2649 }) 2650 }) 2651 }), 2652 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(DashboardShortcut, { 2653 icon: "h-leads", 2654 iconVariant: "custom", 2655 title: i18n.t("js.dashboard.index.import_card.title"), 2656 description: i18n.t("js.dashboard.index.import_card.description"), 2657 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, import_jsx_runtime$12.
2657jsx)(DropdownMenuTrigger, { 2658 asChild: true, 2659 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)(Button, { 2660 variant: "primary", 2661 size: "sm", 2662 children: [i18n.t("js.dashboard.index.import_card.cta"), /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Icon, { 2663 icon: "chevron-down", 2664 size: "xs", 2665 "aria-hidden": "true" 2666 })] 2667 }) 2668 }), /* @__PURE__ */ (0, import_jsx_runtime$12.jsxs)(DropdownMenuContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(DropdownMenuItem, { 2669 asChild: true, 2670 closeOnClick: true, 2671 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("a", { 2672 href: new_companies_import_path(), 2673 children: i18n.t("js.dashboard.index.import_card.import_companies") 2674 }) 2675 }), /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(DropdownMenuItem, { 2676 asChild: true, 2677 closeOnClick: true, 2678 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("a", { 2679 href: new_leads_import_path(), 2680 children: i18n.t("js.dashboard.index.import_card.import_people") 2681 }) 2682 })] })] }) 2683 }),
2684 /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(DashboardShortcut, { 2685 icon: "h-sequences", 2686 iconVariant: "custom", 2687 title: i18n.t("js.dashboard.index.campaigns_card.title"), 2688 description: i18n.t("js.dashboard.index.campaigns_card.description"), 2689 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)(Button, { 2690 variant: "primary", 2691 size: "sm", 2692 asChild: true, 2693 children: /* @__PURE__ */ (0, import_jsx_runtime$12.jsx)("a", { 2694 href: sequences_path(), 2695 children: i18n.t("js.dashboard.index.campaigns_card.cta") 2696 }) 2697 }) 2698 }) 2699 ] 2700 }) 2701 ] 2702 }); 2703 }); 2704 DashboardShortcuts.displayName = "DashboardShortcuts"; 2705 })))(); 2706} 2707//#endregion 2708//#region app/frontend/react/components/lead-signals-load-more.tsx 2709function LeadSignalsLoadMore(t0) { 2710 const $ = (0, import_compiler_runtime$9.c)(19); 2711 const { boundaryRef, paginationState, onLoadMore } = t0; 2712 const isLoading = paginationState === "loading"; 2713 let t1; 2714 if ($[0] !== isLoading || $[1] !== paginationState) { 2715 t1 = isLoading ? i18n.t("js.dashboard.lead_signals.loading_more") : paginationState === "error" ? i18n.t("js.dashboard.lead_signals.retry_loading") : i18n.t("js.dashboard.lead_signals.load_more"); 2716 $[0] = isLoading; 2717 $[1] = paginationState; 2718 $[2] = t1; 2719 } else t1 = $[2]; 2720 const label = t1; 2721 let t2; 2722 if ($[3] !== isLoading) { 2723 t2 = cn({ "is-loading": isLoading }); 2724 $[3] = isLoading; 2725 $[4] = t2; 2726 } else t2 = $[4]; 2727 let t3; 2728 if ($[5] !== isLoading) { 2729 t3 = isLoading && /* @__PURE__ */ (0, import_jsx_runtime$11.jsx)(Icon, { 2730 loading: true, 2731 size: "sm" 2732 }); 2733 $[5] = isLoading; 2734 $[6] = t3; 2735 } else t3 = $[6]; 2736 let t4; 2737 if ($[7] !== isLoading || $[8] !== label || $[9] !== onLoadMore || $[10] !== t2 || $[11] !== t3) { 2738 t4 = /* @__PURE__ */ (0, import_jsx_runtime$11.jsxs)(Button, { 2739 variant: "linkGrey", 2740 size: "xs", 2741 disabled: isLoading, 2742 onClick: onLoadMore, 2743 className: t2, 2744 children: [t3, label] 2745 }); 2746 $[7] = isLoading; 2747 $[8] = label; 2748 $[9] = onLoadMore; 2749 $[10] = t2; 2750 $[11] = t3; 2751 $[12] = t4; 2752 } else t4 = $[12]; 2753 const t5 = isLoading || paginationState === "error" ? label : ""; 2754 let t6; 2755 if ($[13] !== t5) { 2756 t6 = /* @__PURE__ */ (0, import_jsx_runtime$11.jsx)("span", { 2757 className: "sr-only", 2758 role: "status", 2759 "aria-live": "polite", 2760 "aria-atomic": "true", 2761 children: t5 2762 }); 2763 $[13] = t5; 2764 $[14] = t6; 2765 } else t6 = $[14]; 2766 let t7; 2767 if ($[15] !== boundaryRef || $[16] !== t4 || $[17] !== t6) { 2768 t7 = /* @__PURE__ */ (0, import_jsx_runtime$11.jsxs)("div", { 2769 ref: boundaryRef, 2770 className: "dashboard-v2__lead-signals-load-more", 2771 children: [t4, t6] 2772 }); 2773 $[15] = boundaryRef; 2774 $[16] = t4; 2775 $[17] = t6; 2776 $[18] = t7; 2777 } else t7 = $[18]; 2778 return t7; 2779} 2780var import_compiler_runtime$9, import_jsx_runtime$11; 2781function init_lead_signals_load_more() { 2782 return (init_lead_signals_load_more = __esmMin((() => { 2783 import_compiler_runtime$9 = require_compiler_runtime(); 2784 require_react(); 2785 init_utils(); 2786 init_button(); 2787 init_icon(); 2788 import_jsx_runtime$11 = require_jsx_runtime(); 2789 })))(); 2790} 2791//#endregion 2792//#region app/frontend/react/helpers/lead-signals-aggregator.ts 2793function groupedLeadSignalToSignal(group) { 2794 const { summary, representative, newest } = group; 2795 const company = companyName(representative); 2796 let message; 2797 if (group.entity_count > 1) message = aggregatedMessage(group.event_type, group.entity_count, [], summary); 2798 else if (group.event_type === "email_added") { 2799 const count = summary.email_count; 2800 const makers = summary.decision_maker_count; 2801 message = `${company} has ${count} new email address${count === 1 ? "" : "es"}`; 2802 if (makers > 0) message += `, including ${makers} decision maker${makers === 1 ? "" : "s"}`; 2803 } else if (group.event_type === "position_changed" || group.event_type === "location_changed") message = singleActivityMessage(group.event_type, representative); 2804 else if (summary.labels_complete && summary.label_count > 0 && [ 2805 "technology_added", 2806 "technology_deleted", 2807 "executive_added" 2808 ].includes(group.event_type)) { 2809 const labels = summary.labels; 2810 const names = summary.label_count > 3 ? `${labels[0]}, ${labels[1]} and ${summary.label_count - 2} more` : nameList(labels, ""); 2811 message = `${company} ${group.event_type === "executive_added" ? "hired a new" : group.event_type === "technology_added" ? "started using" : "stopped using"} ${names}`; 2812 } else message = signalSentence(company, [newest]); 2813 const entity = group.entity_count === 1 ? singleEntity(representative, group.entity_type) : void 0; 2814 const entityNames = [...summary.names]; 2815 if (entity?.kind === "lead" && entity.name) entityNames.push(entity.name); 2816 return { 2817 id: group.id, 2818 groupToken: group.token, 2819 membershipVersion: group.membership_version, 2820 timestamp: new Date(group.newest_at), 2821 signal: message ?? "", 2822 eventType: group.event_type, 2823 entityType: group.entity_type, 2824 entityCount: group.entity_count, 2825 activityCount: group.activity_count, 2826 visited: group.visited, 2827 activities: [representative], 2828 entity, 2829 entityNames, 2830 leadingCompanies: group.entity_count > 1 ? summary.companies : void 0, 2831 leadingCompanyCount: group.entity_count > 1 ? summary.company_count : void 0 2832 }; 2833} 2834var seriesMapping, getSmallestFundingRound, localDayKey, distinctEntityCount, newestTimestamp, hasUsableMeta, representativeActivity, companyName, singleEntity, distinctCompanies, bucketVisited, newEmailCompanyKey, sortNewEmailA
2834ctivitiesByVolume, teamActivitiesToLeadSignals, distinctEntityNames, nameList, signalEntityNames, aggregatedSubject, aggregatedMessage, newEmailsMessage, singleActivityMessage, SINGLE_ACTIVITY_CATEGORY_TITLES, MULTI_ACTIVITY_CATEGORY_TITLES, TECHNOLOGY_CATEGORY_TITLE_KEYS, categoryTitleFor, sidepanelTitleFor; 2835function init_lead_signals_aggregator() { 2836 return (init_lead_signals_aggregator = __esmMin((() => { 2837 init_signal_content(); 2838 seriesMapping = { 2839 "Pre-Seed": 0, 2840 Seed: 1, 2841 "Pre-Series A": 2, 2842 "Series A": 3, 2843 "Pre-Series B": 4, 2844 "Series B": 5, 2845 "Pre-Series C": 6, 2846 "Series C": 7, 2847 "Pre-Series D": 8, 2848 "Series D": 9, 2849 "Pre-Series E": 10, 2850 "Series E": 11, 2851 "Pre-Series F": 12, 2852 "Series F": 13, 2853 "Pre-Series G": 14, 2854 "Series G": 15, 2855 "Pre-Series H": 16, 2856 "Series H": 17, 2857 "Pre-Series I": 18, 2858 "Series I": 19 2859 }; 2860 getSmallestFundingRound = (activities) => { 2861 const smallestFundingRoundIndex = activities.reduce((minIndex, activity) => { 2862 const meta = activity.activity.associated_record_data; 2863 const fundingRoundIndex = meta?.type ? dictLookup(seriesMapping, meta.type) ?? 999 : 999; 2864 return fundingRoundIndex < minIndex ? fundingRoundIndex : minIndex; 2865 }, 999); 2866 if (smallestFundingRoundIndex === 999) return null; 2867 return Object.keys(seriesMapping).find((key) => dictLookup(seriesMapping, key) === smallestFundingRoundIndex) ?? null; 2868 }; 2869 localDayKey = (createdAt) => { 2870 const date = new Date(createdAt); 2871 return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; 2872 }; 2873 distinctEntityCount = (activities) => new Set(activities.map((activity) => `${activity.activity.activity_type}|${activity.activity.activity_id}`)).size; 2874 newestTimestamp = (activities) => new Date(Math.max(...activities.map((activity) => new Date(activity.created_at).getTime()))); 2875 hasUsableMeta = (activity) => { 2876 const meta = activity.activity.associated_record_data; 2877 return Boolean(meta?.company_name || meta?.value || meta?.full_email); 2878 }; 2879 representativeActivity = (activities) => { 2880 const newestFirst = [...activities].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); 2881 return newestFirst.find(hasUsableMeta) ?? newestFirst[0]; 2882 }; 2883 companyName = (activity) => { 2884 const meta = activity.activity.associated_record_data; 2885 return meta?.company_name || meta?.value || "This company"; 2886 }; 2887 singleEntity = (activity, entityType) => { 2888 const meta = activity.activity.associated_record_data; 2889 if (entityType === "email") return { 2890 kind: "lead", 2891 name: meta?.full_email ?? null, 2892 avatarUrl: meta?.avatar_url ?? null, 2893 leadId: meta?.lead_id ?? null, 2894 companyDomain: meta?.value ?? meta?.domain ?? null 2895 }; 2896 return { 2897 kind: "company", 2898 domain: meta?.value ?? null, 2899 name: meta?.company_name ?? null 2900 }; 2901 }; 2902 distinctCompanies = (activities) => { 2903 const seen = /* @__PURE__ */ new Set(); 2904 const companies = []; 2905 for (const activity of activities) { 2906 const meta = activity.activity.associated_record_data; 2907 const name = meta?.company_name || meta?.value; 2908 if (!name) continue; 2909 const domain = meta?.value ?? meta?.domain ?? null; 2910 const identity = domain ?? name; 2911 if (seen.has(identity)) continue; 2912 seen.add(identity); 2913 companies.push({ 2914 domain, 2915 name 2916 }); 2917 } 2918 return companies; 2919 }; 2920 bucketVisited = (activities) => activities.every((activity) => activity.seen_at != null); 2921 newEmailCompanyKey = ({ activity }) => { 2922 const data = activity.associated_record_data; 2923 if (isObject(data)) { 2924 if (isString(data.value)) return data.value; 2925 if (isString(data.domain)) return data.domain; 2926 if (isString(data.company_name)) return data.company_name; 2927 } 2928 return `${activity.activity_type}:${activity.activity_id}`; 2929 };
2930 sortNewEmailActivitiesByVolume = (activities) => { 2931 const emailsByCompany = /* @__PURE__ */ new Map(); 2932 for (const teamActivity of activities) { 2933 const { activity } = teamActivity; 2934 if (activity.event_type !== "email_added") continue; 2935 const company = newEmailCompanyKey(teamActivity); 2936 const data = activity.associated_record_data; 2937 const email = isObject(data) && isNumber(data.id) ? `email-${data.id}` : `activity-${teamActivityKey(teamActivity)}`; 2938 const emails = emailsByCompany.get(company) ?? /* @__PURE__ */ new Set(); 2939 emails.add(email); 2940 emailsByCompany.set(company, emails); 2941 } 2942 return [...activities].sort((a, b) => (emailsByCompany.get(newEmailCompanyKey(b))?.size ?? 0) - (emailsByCompany.get(newEmailCompanyKey(a))?.size ?? 0)); 2943 }; 2944 teamActivitiesToLeadSignals = (teamActivities) => { 2945 if (!teamActivities || teamActivities.length === 0) return []; 2946 const groupedTeamActivities = teamActivities.reduce((acc, activity) => { 2947 const eventType = activity.activity.event_type; 2948 const groupKey = `${localDayKey(activity.created_at)}|${eventType}`; 2949 if (!acc[groupKey]) acc[groupKey] = []; 2950 acc[groupKey].push(activity); 2951 return acc; 2952 }, {}); 2953 return Object.keys(groupedTeamActivities).map((key) => { 2954 const [, eventType] = key.split("|"); 2955 const activities = eventType === "email_added" ? sortNewEmailA
2955ctivitiesByVolume(groupedTeamActivities[key]) : groupedTeamActivities[key]; 2956 const entityType = activities[0].activity.activity_type; 2957 const entityCount = distinctEntityCount(activities); 2958 const timestamp = newestTimestamp(activities); 2959 if (entityCount > 1) { 2960 const signal = aggregatedMessage(eventType, entityCount, activities); 2961 if (signal === null) return null; 2962 return { 2963 id: key, 2964 timestamp, 2965 signal, 2966 eventType, 2967 entityCount, 2968 entityType, 2969 visited: bucketVisited(activities), 2970 activities, 2971 leadingCompanies: distinctCompanies(activities) 2972 }; 2973 } 2974 const representative = representativeActivity(activities); 2975 const signal = eventType === "email_added" ? newEmailsMessage(activities) : eventType === "position_changed" || eventType === "location_changed" ? singleActivityMessage(eventType, representative) : signalSentence(companyName(representative), activities); 2976 if (signal === null) return null; 2977 return { 2978 id: key, 2979 timestamp, 2980 signal, 2981 eventType, 2982 entityCount, 2983 entityType, 2984 visited: bucketVisited(activities), 2985 activities, 2986 entity: singleEntity(representative, entityType) 2987 }; 2988 }).filter((signal) => signal !== null).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); 2989 }; 2990 distinctEntityNames = (activities) => { 2991 const seen = /* @__PURE__ */ new Set(); 2992 const names = []; 2993 for (const activity of activities) { 2994 const meta = activity.activity.associated_record_data; 2995 const name = meta?.company_name || meta?.value || meta?.full_email; 2996 if (name && !seen.has(name)) { 2997 seen.add(name); 2998 names.push(name); 2999 } 3000 } 3001 return names; 3002 }; 3003 nameList = (names, remainderNoun, count = names.length) => { 3004 if (count <= 3) { 3005 if (names.length === 3) return `${names[0]}, ${names[1]} and ${names[2]}`; 3006 if (names.length === 2) return `${names[0]} and ${names[1]}`; 3007 return names[0]; 3008 } 3009 const remainder = count - 3; 3010 const noun = remainder === 1 ? remainderNoun.replace(/ies$/, "y").replace(/s$/, "") : remainderNoun; 3011 return `${names[0]}, ${names[1]}, ${names[2]} and ${remainder} more ${noun}`; 3012 }; 3013 signalEntityNames = (signal) => { 3014 if (signal.entityNames) return signal.entityNames; 3015 const names = distinctEntityNames(signal.activities); 3016 for (const activity of signal.activities) { 3017 if (activity.activity.activity_type !== "email") continue; 3018 const meta = activity.activity.associated_record_data; 3019 if (meta?.full_email) names.push(meta.full_email); 3020 } 3021 return names; 3022 }; 3023 aggregatedSubject = (activities, entityCount, kind, summary) => { 3024 const names = summary?.names ?? distinctEntityNames(activities); 3025 if (kind === "contact") { 3026 const leads = `${entityCount} ${entityCount === 1 ? "lead" : "leads"}`; 3027 return names.length === 0 ? `${entityCount} of your saved leads` : `${leads} from ${nameList(names, "companies", summary?.name_count)}`; 3028 } 3029 if (entityCount <= 3 && names.length >= entityCount) return nameList(names.slice(0, entityCount), "companies"); 3030 if (names.length >= 3) return `${names[0]}, ${names[1]}, ${names[2]} and ${entityCount - 3} more of your saved companies`; 3031 if (names.length === 2) return `${names[0]}, ${names[1]} and ${entityCount - 2} more of your saved companies`; 3032 return `${entityCount} companies from your saved leads`; 3033 }; 3034 aggregatedMessage = (eventType, entityCount, activities, summary) => { 3035 const subject = aggregatedSubject(activities, entityCount, eventType === "position_changed" || eventType === "location_changed" ? "contact" : "company", summary); 3036 switch (eventType) { 3037 case "email_added": return `${subject} have new email addresses`; 3038 case "technology_added": return `${subject} have added new technologies`; 3039 case "linkedin_job_added": return `${subject} have published new job openings`; 3040 case "funding_added": { 3041 const smallestFundingRound = summary ? summary.funding_round === null ? null : Object.keys(seriesMapping)[summary.funding_round - 1] : getSmallestFundingRound(activities); 3042 return smallestFundingRound === null ? `${subject} raised funding rounds` : `${subject} raised ${smallestFundingRound} or later funding rounds`; 3043 } 3044 case "technology_deleted": return `${subject} have removed technologies`; 3045 case "executive_added": return `${subject} have added new executives`; 3046 case "spike_metric": return `${subject} have spiked metrics`; 3047 case "hiring_volume": return `${subject} have multiple open roles`; 3048 case "hiring_spike": return `${subject} are accelerating their hiring`; 3049 case "company_growth": return `${subject} are growing their headcount`; 3050 case "position_changed": return `${subject} have changed positions`; 3051 case "location_changed": return `${subject} have changed locations`; 3052 default: return null; 3053 } 3054 }; 3055 newEmailsMessage = (activities) => { 3056 const meta = representativeActivity(activities).activity.associated_record_data; 3057 const company = meta?.company_name || meta?.value || "This company"; 3058 const seen = /* @__PURE__ */ new Set(); 3059 let decisionMakers = 0; 3060 for (const activity of activities) { 3061 const data = activity.activity.associated_record_data;
3062 const key = data?.id != null ? `email-${data.id}` : `activity-${teamActivityKey(activity)}`; 3063 if (seen.has(key)) continue; 3064 seen.add(key); 3065 if (data?.decision_maker) decisionMakers += 1; 3066 } 3067 const count = seen.size; 3068 const base = `${company} has ${count} new email address${count === 1 ? "" : "es"}`; 3069 return decisionMakers > 0 ? `${base}, including ${decisionMakers} decision maker${decisionMakers === 1 ? "" : "s"}` : base; 3070 }; 3071 singleActivityMessage = (eventType, activity) => { 3072 const meta = activity.activity.associated_record_data; 3073 const contact = meta?.full_email || "A contact"; 3074 switch (eventType) { 3075 case "position_changed": { 3076 const companyPart = meta?.company_name ? ` from ${meta.company_name}` : ""; 3077 const toRole = activity.activity.metadata?.item_to?.trim(); 3078 return toRole ? `${contact}${companyPart} is now ${toRole}` : `${contact}${companyPart} has changed position`; 3079 } 3080 case "location_changed": { 3081 const from = meta?.company_name ? ` from ${meta.company_name}` : ""; 3082 const to = activity.activity.metadata?.item_to?.trim(); 3083 return to ? `${contact}${from} has moved to ${to}` : `${contact}${from} has changed location`; 3084 } 3085 default: return null; 3086 } 3087 }; 3088 SINGLE_ACTIVITY_CATEGORY_TITLES = { 3089 email_added: "New email addresses", 3090 technology_added: "New technologies", 3091 technology_deleted: "Removed technologies", 3092 linkedin_job_added: "New job opening", 3093 funding_added: "New funding round", 3094 executive_added: "New executive", 3095 spike_metric: "Activity spike", 3096 hiring_volume: "Open roles", 3097 hiring_spike: "Hiring spike", 3098 company_growth: "Headcount growth", 3099 position_changed: "Position change", 3100 location_changed: "Location change" 3101 }; 3102 MULTI_ACTIVITY_CATEGORY_TITLES = { 3103 email_added: "New email addresses", 3104 technology_added: "New technologies", 3105 technology_deleted: "Removed technologies", 3106 linkedin_job_added: "New job openings", 3107 funding_added: "New funding rounds", 3108 executive_added: "New executives", 3109 spike_metric: "Activity spikes", 3110 hiring_volume: "Open roles", 3111 hiring_spike: "Hiring spikes", 3112 company_growth: "Headcount growth", 3113 position_changed: "Position changes", 3114 location_changed: "Location changes" 3115 }; 3116 TECHNOLOGY_CATEGORY_TITLE_KEYS = { 3117 technology_added: "js.dashboard.lead_signals.technology_added_title", 3118 technology_deleted: "js.dashboard.lead_signals.technology_deleted_title" 3119 }; 3120 categoryTitleFor = (eventType, count) => count > 1 ? MULTI_ACTIVITY_CATEGORY_TITLES[eventType] : SINGLE_ACTIVITY_CATEGORY_TITLES[eventType]; 3121 sidepanelTitleFor = (signal) => { 3122 if (signal.entityCount > 1) return signal.signal; 3123 const technologyTitleKey = dictLookup(TECHNOLOGY_CATEGORY_TITLE_KEYS, signal.eventType); 3124 if (technologyTitleKey) return i18n.t(technologyTitleKey); 3125 return categoryTitleFor(signal.eventType, signal.activityCount ?? signal.activities.length); 3126 }; 3127 })))(); 3128} 3129//#endregion 3130//#region app/frontend/react/helpers/lead-signal-filters.ts 3131function filterSignalPeople(rows, filters) { 3132 const queries = new Set(filters.jobTitleQueries); 3133 const titles = /* @__PURE__ */ new Set(); 3134 const keywords = []; 3135 for (const title of filters.jobTitles) if (queries.has(title)) keywords.push(title.toLowerCase()); 3136 else titles.add(title); 3137 const departments = new Set(filters.departments); 3138 const seniorities = new Set(filters.seniorities); 3139 const includedLocations = filters.locations.filter((location) => location.included); 3140 const excludedLocations = filters.locations.filter((location) => !location.included); 3141 const matchesLocation = (person, selected) => personLocations(person).some((location) => location.type === selected.type && location.value === selected.value); 3142 return rows.filter((person) => (filters.jobTitles.length === 0 || titles.has(person.filterJobTitle ?? "") || keywords.some((keyword) => person.filterJobTitle?.toLowerCase().includes(keyword))) && (departments.size === 0 || departments.has(person.result.department ?? "")) && (seniorities.size === 0 || seniorities.has(personSeniority(person) ?? "")) && !excludedLocations.some((location) => matchesLocation(person, location)) && (includedLocations.length === 0 || includedLocations.some((location) => matchesLocation(person, location)))); 3143} 3144function signalEventFacets({ activity }) { 3145 const metadata = isObject(activity.metadata) && !Array.isArray(activity.metadata) ? activity.metadata : {}; 3146 const technology = activity.event_type === "technology_added" || activity.event_type === "technology_deleted" ? activity.associated_record_data?.technology_name ?? (isString(metadata.item_slug) ? metadata.item_slug : null) : null; 3147 const technologyLabel = activity.event_type === "technology_added" || activity.event_type === "technology_deleted" ? activity.associated_record_data?.technology_name ?? technology : null; 3148 const departments = activity.event_type === "hiring_volume" ? hiringRoleGroups(activity.metadata).map((group) => group.name) : activity.event_type === "linkedin_job_added" ? activity.associated_record_data?.departments ?? [] : []; 3149 const title = activity.event_type === "linkedin_job_added" ? activity.associated_record_data?.title : null; 3150 const total = metadata.total_active; 3151 return { 3152 technology, 3153 technologyLabel, 3154 departments, 3155 title, 3156 totalActive: isNumber(total) && Number.isInteger(total) && total >= 0 ? total : null 3157 }; 3158} 3159function signalCompanyMatcher(filters) { 3160 const { technologies, departments, jobTitle, openRoles } = filters; 3161 if (!technologies.length && !departments.length && !jobTitle.trim() && openRoles.from == null && openRoles.to == null) return () => true; 3162 const technologySet = new Set(technologies); 3163 const departmentSet = new Set(departments); 3164 const keyword = jobTitle.trim().toLowerCase(); 3165 return (activities) => activities.some((activity) => { 3166 const facets = signalEventFacets(activity); 3167 return (technologySet.size === 0 || technologySet.has(facets.technology ?? "")) && (departmentSet.size === 0 || facets.departments.some((department) => departmentSet.has(department))) && (!keyword || (facets.title ?? "").toLowerCase().includes(keyword)) && (openRoles.from == null || facets.totalActive != null && facets.totalActive >= openRoles.from) && (openRoles.to == null || facets.totalActive != null && facets.totalActive <= openRoles.to); 3168 }); 3169} 3170function locationOptions({ country, state, city }) { 3171 const options = []; 3172 const countryLabel = country && /^[a-z]{2}$/i.test(country) ? regionNames.of(country.toUpperCase()) ?? country : country; 3173 if (country) options.push({ 3174 type: "country", 3175 value: country, 3176 label: countryLabel ?? country, 3177 included: true 3178 }); 3179 if (state) options.push({ 3180 type: "state", 3181 value: state,
3182 label: [state, countryLabel].filter(Boolean).join(", "), 3183 included: true 3184 }); 3185 if (city) options.push({ 3186 type: "city", 3187 value: [ 3188 city, 3189 state ?? "", 3190 country ?? "" 3191 ].join(","), 3192 label: [ 3193 city, 3194 state, 3195 countryLabel 3196 ].filter(Boolean).join(", "), 3197 included: true 3198 }); 3199 return options; 3200} 3201function personLocations(person) { 3202 if (!person.location) return []; 3203 const country = countryCodes.get(person.location.trim().toLowerCase()); 3204 if (country) return locationOptions({ country }); 3205 const parts = person.location.split(",").map((part) => part.trim()); 3206 const suffix = parts.length > 1 ? parts[parts.length - 1] : null; 3207 const stateOnly = parts.length === 2 && suffix != null && /^[a-z]{2}$/i.test(suffix) && (usStateCodes.has(suffix.toUpperCase()) || regionNames.of(suffix.toUpperCase()) === suffix.toUpperCase()); 3208 return locationOptions({ 3209 city: parts[0], 3210 country: stateOnly ? null : suffix ? countryCodes.get(suffix.toLowerCase()) ?? suffix : null, 3211 state: stateOnly ? suffix : parts.length > 2 ? parts.slice(1, -1).join(", ") : null 3212 }); 3213} 3214function uniqueLocations(locations) { 3215 return [...new Map(locations.map((location) => [`${location.type}:${location.value}`, location])).values()]; 3216} 3217function filterSignalCompanies(companies, activities, filters) { 3218 const matchesSignal = signalCompanyMatcher(filters); 3219 return filterCompanies(companies, filters.company).filter((company) => matchesSignal(activities[company.id] ?? [])); 3220} 3221function signalCompanyFiltersSet(filters) { 3222 return !!(filters.company.location.length || filters.company.companySizes.length || filters.company.companyTypes.length || filters.technologies.length || filters.departments.length || filters.jobTitle.trim() || filters.openRoles.from != null || filters.openRoles.to != null || filters.company.funding); 3223} 3224var emptySignalPeopleFilters, emptySignalCompanyFilters, emptyCompanyFilters, uniqueFilterValues, personSeniority, regionNames, usStateCodes, countryCodes, knownRegionNames; 3225function init_lead_signal_filters() { 3226 return (init_lead_signal_filters = __esmMin((() => { 3227 init_company_filters(); 3228 init_signal_content(); 3229 emptySignalPeopleFilters = () => ({ 3230 jobTitles: [], 3231 jobTitleQueries: [], 3232 departments: [], 3233 seniorities: [], 3234 locations: [] 3235 }); 3236 emptySignalCompanyFilters = () => ({ 3237 company: emptyCompanyFilters(), 3238 technologies: [], 3239 departments: [], 3240 jobTitle: "", 3241 openRoles: {} 3242 }); 3243 emptyCompanyFilters = () => ({ 3244 location: [], 3245 industries: [], 3246 companySizes: [], 3247 companyTypes: [], 3248 technologies: [] 3249 }); 3250 uniqueFilterValues = (values) => [...new Set(values.filter((value) => isString(value) && value.trim() !== ""))].sort((a, b) => a.localeCompare(b)); 3251 personSeniority = (person) => person.result.seniorityLevel ?? person.result.seniority; 3252 regionNames = new Intl.DisplayNames(["en"], { type: "region" }); 3253 usStateCodes = new Set("AL AK AZ AR CA CO CT DE DC FL GA HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND OH OK OR PA RI SC SD TN TX UT VT VA WA WV WI WY".split(" ")); 3254 countryCodes = /* @__PURE__ */ new Map(); 3255 knownRegionNames = new Intl.DisplayNames(["en"], { 3256 type: "region", 3257 fallback: "none" 3258 }); 3259 for (let first = 65; first <= 90; first++) for (let second = 65; second <= 90; second++) { 3260 const code = String.fromCharCode(first, second); 3261 const name = knownRegionNames.of(code); 3262 if (!name) continue; 3263 const canonicalCode = new Intl.Locale(`und-${code}`).region ?? code; 3264 countryCodes.set(code.toLowerCase(), canonicalCode); 3265 countryCodes.set(name.toLowerCase(), canonicalCode); 3266 } 3267 })))(); 3268} 3269//#endregion 3270//#region app/frontend/react/components/lead-signal-filters.tsx 3271function FilterMenu({ label, values, onClear, children, onOpen, onApply, disabled = false }) { 3272 const [open, setOpen] = (0, import_react$9.useState)(false); 3273 return /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)("div", { 3274 className: "h-filter", 3275 children: [/* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(DropdownMenu, { 3276 open, 3277 onOpenChange: (nextOpen) => { 3278 setOpen(nextOpen); 3279 if (nextOpen) onOpen?.(); 3280 }, 3281 children: [/* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(DropdownMenuTrigger, { 3282 asChild: true, 3283 children: /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(Button, { 3284 size: "sm", 3285 className: "h-filter__button",
3286 children: [label, /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Icon, { icon: "angle-down" })] 3287 }) 3288 }), /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(DropdownMenuContent, { 3289 className: "lead-signal-filter-menu", 3290 tabNavigable: true, 3291 children: [children, /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(DropdownMenuSubmit, { 3292 disabled, 3293 onClick: () => { 3294 onApply(); 3295 setOpen(false); 3296 }, 3297 children: t("apply") 3298 })] 3299 })] 3300 }), values.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(FilterValue, { 3301 labels: values, 3302 removeAriaLabel: i18n.t("js.filters.remove"), 3303 onRemove: onClear 3304 })] 3305 }); 3306} 3307function OptionsFilter(t0) { 3308 const $ = (0, import_compiler_runtime$8.c)(52); 3309 const { label, options, value, onChange } = t0; 3310 const id = (0, import_react$9.useId)(); 3311 const [draft, setDraft] = (0, import_react$9.useState)(value); 3312 const [query, setQuery] = (0, import_react$9.useState)(""); 3313 let t1; 3314 if ($[0] !== draft) { 3315 t1 = new Set(draft); 3316 $[0] = draft; 3317 $[1] = t1; 3318 } else t1 = $[1]; 3319 const selected = t1; 3320 let T0; 3321 let shown; 3322 let t2; 3323 let t3; 3324 let t4; 3325 let t5; 3326 let t6; 3327 let t7; 3328 let t8; 3329 let t9; 3330 if ($[2] !== draft || $[3] !== id || $[4] !== label || $[5] !== onChange || $[6] !== options || $[7] !== query || $[8] !== selected || $[9] !== value) { 3331 const labels = new Map(options.map(_temp$3)); 3332 let t10; 3333 if ($[20] !== query) { 3334 t10 = (option_0) => option_0.label.toLowerCase().includes(query.toLowerCase()); 3335 $[20] = query; 3336 $[21] = t10; 3337 } else t10 = $[21]; 3338 shown = options.filter(t10); 3339 T0 = FilterMenu; 3340 t4 = label; 3341 t5 = value.map((item) => labels.get(item) ?? item); 3342 if ($[22] !== onChange) { 3343 t6 = () => onChange([]); 3344 $[22] = onChange; 3345 $[23] = t6; 3346 } else t6 = $[23]; 3347 if ($[24] !== draft || $[25] !== onChange) { 3348 t7 = () => onChange(draft); 3349 $[24] = draft; 3350 $[25] = onChange; 3351 $[26] = t7; 3352 } else t7 = $[26]; 3353 if ($[27] !== value) { 3354 t8 = () => { 3355 setDraft(value); 3356 setQuery(""); 3357 }; 3358 $[27] = value; 3359 $[28] = t8; 3360 } else t8 = $[28]; 3361 let t11; 3362 let t12; 3363 let t13; 3364 if ($[29] === Symbol.for("react.memo_cache_sentinel")) { 3365 t11 = t("search_options"); 3366 t12 = t("search_options"); 3367 t13 = (event) => setQuery(event.target.value); 3368 $[29] = t11; 3369 $[30] = t12; 3370 $[31] = t13; 3371 } else { 3372 t11 = $[29]; 3373 t12 = $[30]; 3374 t13 = $[31]; 3375 } 3376 if ($[32] !== query) { 3377 t9 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("div", { 3378 className: "p-3", 3379 onKeyDown: stopMenuTypeahead, 3380 children: /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Input, { 3381 type: "search", 3382 value: query, 3383 "aria-label": t11, 3384 placeholder: t12, 3385 onChange: t13 3386 }) 3387 }); 3388 $[32] = query; 3389 $[33] = t9; 3390 } else t9 = $[33]; 3391 t2 = "lead-signal-filter-menu__options"; 3392 let t14; 3393 if ($[34] !== id || $[35] !== selected) { 3394 t14 = (option_1, index) => /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(DropdownMenuItem, { children: [/* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Checkbox, { 3395 id: `${id}-${index}`, 3396 checked: selected.has(option_1.value), 3397 onChange: () => setDraft((previous) => { 3398 const next = new Set(previous); 3399 if (next.has(option_1.value)) next.delete(option_1.value); 3400 else next.add(option_1.value); 3401 return [...next]; 3402 }) 3403 }), /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("label", { 3404 htmlFor: `${id}-${index}`, 3405 children: option_1.label 3406 })] }, option_1.value); 3407 $[34] = id; 3408 $[35] = selected; 3409 $[36] = t14; 3410 } else t14 = $[36]; 3411 t3 = shown.map(t14); 3412 $[2] = draft; 3413 $[3] = id; 3414 $[4] = label; 3415 $[5] = onChange; 3416 $[6] = options; 3417 $[7] = query; 3418 $[8] = selected; 3419 $[9] = value; 3420 $[10] = T0; 3421 $[11] = shown; 3422 $[12] = t2; 3423 $[13] = t3; 3424 $[14] = t4; 3425 $[15] = t5; 3426 $[16] = t6; 3427 $[17] = t7; 3428 $[18] = t8; 3429 $[19] = t9; 3430 } else { 3431 T0 = $[10]; 3432 shown = $[11]; 3433 t2 = $[12]; 3434 t3 = $[13]; 3435 t4 = $[14]; 3436 t5 = $[15]; 3437 t6 = $[16]; 3438 t7 = $[17]; 3439 t8 = $[18]; 3440 t9 = $[19]; 3441 } 3442 let t10; 3443 if ($[37] !== shown.length) { 3444 t10 = shown.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("p", { 3445 className: "p-3 text-muted mb-0", 3446 children: t("no_options") 3447 }); 3448 $[37] = shown.length; 3449 $[38] = t10; 3450 } else t10 = $[38]; 3451 let t11; 3452 if ($[39] !== t10 || $[40] !== t2 || $[41] !== t3) { 3453 t11 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)("div", { 3454 className: t2, 3455 children: [t3, t10] 3456 }); 3457 $[39] = t10; 3458 $[40] = t2; 3459 $[41] = t3; 3460 $[42] = t11; 3461 } else t11 = $[42]; 3462 let t12; 3463 if ($[43] !== T0 || $[44] !== t11 || $[45] !== t4 || $[46] !== t5 || $[47] !== t6 || $[48] !== t7 || $[49] !== t8 || $[50] !== t9) { 3464 t12 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(T0, { 3465 label: t4, 3466 values: t5, 3467 onClear: t6, 3468 onApply: t7, 3469 onOpen: t8, 3470 children: [t9, t11] 3471 }); 3472 $[43] = T0; 3473 $[44] = t11; 3474 $[45] = t4; 3475 $[46] = t5; 3476 $[47] = t6; 3477 $[48] = t7; 3478 $[49] = t8; 3479 $[50] = t9; 3480 $[51] = t12; 3481 } else t12 = $[51]; 3482 return t12; 3483} 3484function _temp$3(option) { 3485 return [option.value, option.label]; 3486} 3487function OpenRolesFilter(t0) { 3488 const $ = (0, import_compiler_runtime$8.c)(49); 3489 const { value, onChange } = t0; 3490 const id = (0, import_react$9.useId)(); 3491 const [from, setFrom] = (0, import_react$9.useState)(""); 3492 const [to, setTo] = (0, import_react$9.useState)(""); 3493 const lower = from === "" ? void 0 : Number(from); 3494 const upper = to === "" ? void 0 : Number(to); 3495 let t1; 3496 if ($[0] !== lower || $[1] !== upper) { 3497 t1 = [lower, upper].some(_temp2$1) || lower != null && upper != null && lower > upper; 3498 $[0] = lower; 3499 $[1] = upper; 3500 $[2] = t1; 3501 } else t1 = $[2]; 3502 const invalid = t1; 3503 let t2; 3504 if ($[3] !== value.from || $[4] !== value.to) { 3505 t2 = value.from == null && value.to == null ? [] : [i18n.t("js.dashboard.lead_signals.filters.range", { 3506 from: value.from ?? 0, 3507 to: value.to ?? "â" 3508 })]; 3509 $[3] = value.from; 3510 $[4] = value.to; 3511 $[5] = t2; 3512 } else t2 = $[5]; 3513 const summary = t2; 3514 let t3; 3515 if ($[6] === Symbol.for("react.memo_cache_sentinel")) { 3516 t3 = t("open_roles"); 3517 $[6] = t3; 3518 } else t3 = $[6]; 3519 let t4; 3520 if ($[7] !== onChange) { 3521 t4 = () =>
3521 onChange({}); 3522 $[7] = onChange; 3523 $[8] = t4; 3524 } else t4 = $[8]; 3525 let t5; 3526 if ($[9] !== lower || $[10] !== onChange || $[11] !== upper) { 3527 t5 = () => onChange({ 3528 from: lower, 3529 to: upper 3530 }); 3531 $[9] = lower; 3532 $[10] = onChange; 3533 $[11] = upper; 3534 $[12] = t5; 3535 } else t5 = $[12]; 3536 let t6; 3537 if ($[13] !== value.from || $[14] !== value.to) { 3538 t6 = () => { 3539 setFrom(value.from?.toString() ?? ""); 3540 setTo(value.to?.toString() ?? ""); 3541 }; 3542 $[13] = value.from; 3543 $[14] = value.to; 3544 $[15] = t6; 3545 } else t6 = $[15]; 3546 const t7 = `${id}-from`; 3547 let t8; 3548 if ($[16] === Symbol.for("react.memo_cache_sentinel")) { 3549 t8 = t("from"); 3550 $[16] = t8; 3551 } else t8 = $[16]; 3552 let t9; 3553 if ($[17] !== t7) { 3554 t9 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Label, { 3555 htmlFor: t7, 3556 children: t8 3557 }); 3558 $[17] = t7; 3559 $[18] = t9; 3560 } else t9 = $[18]; 3561 const t10 = `${id}-from`; 3562 let t11; 3563 if ($[19] === Symbol.for("react.memo_cache_sentinel")) { 3564 t11 = (event) => setFrom(event.target.value); 3565 $[19] = t11; 3566 } else t11 = $[19]; 3567 let t12; 3568 if ($[20] !== from || $[21] !== t10) { 3569 t12 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Input, { 3570 id: t10, 3571 type: "number", 3572 min: 0, 3573 step: 1, 3574 value: from, 3575 onChange: t11 3576 }); 3577 $[20] = from; 3578 $[21] = t10; 3579 $[22] = t12; 3580 } else t12 = $[22]; 3581 let t13; 3582 if ($[23] !== t12 || $[24] !== t9) { 3583 t13 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)("div", { children: [t9, t12] }); 3584 $[23] = t12; 3585 $[24] = t9; 3586 $[25] = t13; 3587 } else t13 = $[25]; 3588 const t14 = `${id}-to`; 3589 let t15; 3590 if ($[26] === Symbol.for("react.memo_cache_sentinel")) { 3591 t15 = t("to"); 3592 $[26] = t15; 3593 } else t15 = $[26]; 3594 let t16; 3595 if ($[27] !== t14) { 3596 t16 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Label, { 3597 htmlFor: t14, 3598 children: t15 3599 }); 3600 $[27] = t14; 3601 $[28] = t16; 3602 } else t16 = $[28]; 3603 const t17 = `${id}-to`; 3604 let t18; 3605 if ($[29] === Symbol.for("react.memo_cache_sentinel")) { 3606 t18 = (event_0) => setTo(event_0.target.value); 3607 $[29] = t18; 3608 } else t18 = $[29]; 3609 let t19; 3610 if ($[30] !== t17 || $[31] !== to) { 3611 t19 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Input, { 3612 id: t17, 3613 type: "number", 3614 min: 0, 3615 step: 1, 3616 value: to, 3617 onChange: t18 3618 }); 3619 $[30] = t17; 3620 $[31] = to; 3621 $[32] = t19; 3622 } else t19 = $[32]; 3623 let t20; 3624 if ($[33] !== t16 || $[34] !== t19) { 3625 t20 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)("div", { children: [t16, t19] }); 3626 $[33] = t16; 3627 $[34] = t19; 3628 $[35] = t20; 3629 } else t20 = $[35]; 3630 let t21; 3631 if ($[36] !== t13 || $[37] !== t20) { 3632 t21 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)("div", { 3633 className: "p-3 d-flex gap-3", 3634 onKeyDown: stopMenuTypeahead, 3635 children: [t13, t20] 3636 }); 3637 $[36] = t13; 3638 $[37] = t20; 3639 $[38] = t21; 3640 } else t21 = $[38]; 3641 let t22; 3642 if ($[39] !== invalid) { 3643 t22 = invalid && /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("p", { 3644 className: "px-3 text-danger", 3645 role: "alert", 3646 children: t("invalid_range") 3647 }); 3648 $[39] = invalid; 3649 $[40] = t22; 3650 } else t22 = $[40]; 3651 let t23; 3652 if ($[41] !== invalid || $[42] !== summary || $[43] !== t21 || $[44] !== t22 || $[45] !== t4 || $[46] !== t5 || $[47] !== t6) { 3653 t23 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(FilterMenu, { 3654 label: t3, 3655 values: summary, 3656 onClear: t4, 3657 disabled: invalid, 3658 onApply: t5, 3659 onOpen: t6, 3660 children: [t21, t22] 3661 }); 3662 $[41] = invalid; 3663 $[42] = summary; 3664 $[43] = t21; 3665 $[44] = t22; 3666 $[45] = t4; 3667 $[46] = t5; 3668 $[47] = t6; 3669 $[48] = t23; 3670 } else t23 = $[48]; 3671 return t23; 3672} 3673function _temp2$1(bound) { 3674 return bound != null && (!Number.isInteger(bound) || bound < 0); 3675} 3676function JobKeywordFilter(t0) { 3677 const $ = (0, import_compiler_runtime$8.c)(20); 3678 const { value, onChange } = t0; 3679 const [draft, setDraft] = (0, import_react$9.useState)(value); 3680 let t1; 3681 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 3682 t1 = t("job_keywords"); 3683 $[0] = t1; 3684 } else t1 = $[0]; 3685 let t2; 3686 if ($[1] !== value) { 3687 t2 = value ? [value] : []; 3688 $[1] = value; 3689 $[2] = t2; 3690 } else t2 = $[2]; 3691 let t3; 3692 if ($[3] !== onChange) { 3693 t3 = () => onChange(""); 3694 $[3] = onChange; 3695 $[4] = t3; 3696 } else t3 = $[4]; 3697 let t4; 3698 if ($[5] !== draft || $[6] !== onChange) { 3699 t4 = () =>
3699 onChange(draft.trim()); 3700 $[5] = draft; 3701 $[6] = onChange; 3702 $[7] = t4; 3703 } else t4 = $[7]; 3704 let t5; 3705 if ($[8] !== value) { 3706 t5 = () => setDraft(value); 3707 $[8] = value; 3708 $[9] = t5; 3709 } else t5 = $[9]; 3710 let t6; 3711 if ($[10] === Symbol.for("react.memo_cache_sentinel")) { 3712 t6 = t("job_keywords"); 3713 $[10] = t6; 3714 } else t6 = $[10]; 3715 let t7; 3716 if ($[11] === Symbol.for("react.memo_cache_sentinel")) { 3717 t7 = (event) => setDraft(event.target.value); 3718 $[11] = t7; 3719 } else t7 = $[11]; 3720 let t8; 3721 if ($[12] !== draft) { 3722 t8 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("div", { 3723 className: "p-3", 3724 onKeyDown: stopMenuTypeahead, 3725 children: /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Input, { 3726 "aria-label": t6, 3727 value: draft, 3728 onChange: t7 3729 }) 3730 }); 3731 $[12] = draft; 3732 $[13] = t8; 3733 } else t8 = $[13]; 3734 let t9; 3735 if ($[14] !== t2 || $[15] !== t3 || $[16] !== t4 || $[17] !== t5 || $[18] !== t8) { 3736 t9 = /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(FilterMenu, { 3737 label: t1, 3738 values: t2, 3739 onClear: t3, 3740 onApply: t4, 3741 onOpen: t5, 3742 children: t8 3743 }); 3744 $[14] = t2; 3745 $[15] = t3; 3746 $[16] = t4; 3747 $[17] = t5; 3748 $[18] = t8; 3749 $[19] = t9; 3750 } else t9 = $[19]; 3751 return t9; 3752} 3753function SignalPeopleFilters({ people, value, onChange, showLocation = false }) { 3754 const { taxonomy } = usePositionTaxonomy(); 3755 const { optionValues, jobTitles, jobTitleCounts } = (0, import_react$9.useMemo)(() => { 3756 const departments = uniqueFilterValues(people.map((person) => person.result.department)); 3757 const seniorities = uniqueFilterValues(people.map(personSeniority)); 3758 const seniorityOrder = [...CLASSIFIED_SENIORITY_LEVELS, ...taxonomy?.levels.map((level) => level.value) ?? []]; 3759 const titles = uniqueFilterValues(people.map((person_0) => person_0.filterJobTitle)); 3760 const counts = {}; 3761 for (const person_1 of people) if (person_1.filterJobTitle) counts[person_1.filterJobTitle]
3761= (counts[person_1.filterJobTitle] ?? 0) + 1; 3762 return { 3763 jobTitles: titles, 3764 jobTitleCounts: counts, 3765 optionValues: { 3766 departments: Object.fromEntries(departments.map((department) => [department, taxonomy?.departments.find((entry) => entry.value === department || entry.canonical_value === department)?.label ?? humanizeDepartment(department)])), 3767 seniority: seniorities.sort((a, b) => seniorityOrder.indexOf(a) - seniorityOrder.indexOf(b)).map((seniority) => ({ 3768 value: seniority, 3769 label: CLASSIFIED_SENIORITY_LEVELS.some((level_0) => level_0 === seniority) ? i18n.t(`js.multi_domain_search.filters.seniority.classified_levels.${seniority}`) : taxonomy?.levels.find((entry_0) => entry_0.value === seniority || entry_0.canonical_value === seniority)?.label ?? humanizeDepartment(seniority) 3770 })), 3771 locations: uniqueLocations(people.flatMap(personLocations)) 3772 } 3773 }; 3774 }, [people, taxonomy]); 3775 const filters = (0, import_react$9.useMemo)(() => ({ 3776 jobTitles: value.jobTitles, 3777 customJobTitleQueries: value.jobTitleQueries, 3778 departments: value.departments, 3779 levels: value.seniorities, 3780 location: value.locations, 3781 requiredFields: [], 3782 verificationStatus: [], 3783 hideAlreadySaved: false 3784 }), [value]); 3785 const visibleFilters = [ 3786 "job_title", 3787 "department", 3788 "seniority", 3789 ...showLocation ? ["location"] : [] 3790 ]; 3791 return /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("div", { 3792 className: "lead-signals-sidepanel__filters", 3793 role: "group", 3794 "aria-label": t("people"), 3795 children: /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(Filters, { 3796 possibleFilters: visibleFilters, 3797 initialVisibleFilters: visibleFilters, 3798 optionValues, 3799 filters, 3800 setFilters: (next) => { 3801 const selectedTitles = new Set(next.jobTitles); 3802 onChange({ 3803 jobTitles: next.jobTitles, 3804 jobTitleQueries: (next.customJobTitleQueries ?? []).filter((query) => selectedTitles.has(query)), 3805 departments: next.departments, 3806 seniorities: next.levels ?? [], 3807 locations: next.location 3808 }); 3809 }, 3810 jobTitles, 3811 jobTitleCounts, 3812 anyFiltersSet: Object.values(value).some((items) => items.length > 0), 3813 clearFilters: () => onChange(emptySignalPeopleFilters()) 3814 }) 3815 }); 3816} 3817function SignalCompanyFilters({ companies, activities, eventType, value, onChange, jobFiltersReady = true }) { 3818 const facets = activities.map(signalEventFacets); 3819 const optionValues = (0, import_react$9.useMemo)(() => ({ 3820 locations: uniqueLocations(companies.flatMap(locationOptions)), 3821 sizes: uniqueFilterValues(companies.map((company) => company.size)), 3822 types: uniqueFilterValues(companies.map((company_0) => company_0.company_type)) 3823 }), [companies]); 3824 const technologyOptions = optionsFor(facets.map((facet) => facet.technology)).map((option) => ({ 3825 ...option, 3826 label: facets.find((facet_0) => facet_0.technology === option.value)?.technologyLabel ?? option.value 3827 })); 3828 const filters = (0, import_react$9.useMemo)(() => ({ 3829 ...value.company, 3830 technologies: value.technologies 3831 }), [value.company, value.technologies]); 3832 return /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)("div", { 3833 className: "lead-signals-sidepanel__filters", 3834 role: "group", 3835 "aria-label": t("companies"), 3836 children: /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(CompanyFilters, { 3837 visibleFilters: [ 3838 ...eventType === "technology_added" || eventType === "technology_deleted" ? ["technologies"] : [], 3839 "location", 3840 "size", 3841 "type", 3842 ...eventType === "funding_added" ? ["funding"] : [] 3843 ], 3844 optionValues, 3845 fundingTypes: uniqueFilterValues(companies.flatMap((company_1) => (company_1.fundings ?? []).map((round) => round.type))), 3846 technologies: technologyOptions.map((option_0) => ({ 3847 slug: option_0.value, 3848 name: option_0.label, 3849 category: "" 3850 })), 3851 filters, 3852 setFilters: ({ technologies, ...company_2 }) => onChange({ 3853 ...value, 3854 company: { 3855 ...company_2, 3856 technologies: [] 3857 }, 3858 technologies 3859 }), 3860 clearFilters: () => onChange(emptySignalCompanyFilters()), 3861 anyFiltersSet: signalCompanyFiltersSet(value), 3862 additionalFilters: /* @__PURE__ */ (0, import_jsx_runtime$10.jsxs)(import_jsx_runtime$10.Fragment, { children: [ 3863 (eventType === "hiring_volume" || eventType === "hiring_spike") && /* @__P
3863URE__ */ (0, import_jsx_runtime$10.jsx)(OpenRolesFilter, { 3864 value: value.openRoles, 3865 onChange: (openRoles) => onChange({ 3866 ...value, 3867 openRoles 3868 }) 3869 }), 3870 (eventType === "hiring_volume" || eventType === "linkedin_job_added" && jobFiltersReady) && /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(OptionsFilter, { 3871 label: t(eventType === "hiring_volume" ? "role_groups" : "departments"), 3872 options: optionsFor(facets.flatMap((facet_1) => facet_1.departments)).map((option_1) => ({ 3873 ...option_1, 3874 label: humanizeDepartment(option_1.value) 3875 })), 3876 value: value.departments, 3877 onChange: (departments) => onChange({ 3878 ...value, 3879 departments 3880 }) 3881 }), 3882 eventType === "linkedin_job_added" && jobFiltersReady && /* @__PURE__ */ (0, import_jsx_runtime$10.jsx)(JobKeywordFilter, { 3883 value: value.jobTitle, 3884 onChange: (jobTitle) => onChange({ 3885 ...value, 3886 jobTitle 3887 }) 3888 }) 3889 ] }) 3890 }) 3891 }); 3892} 3893var import_compiler_runtime$8, import_react$9, import_jsx_runtime$10, t, optionsFor; 3894function init_lead_signal_filters$1() { 3895 return (init_lead_signal_filters$1 = __esmMin((() => { 3896 import_compiler_runtime$8 = require_compiler_runtime(); 3897 import_react$9 = require_react(); 3898 init_button(); 3899 init_checkbox(); 3900 init_icon(); 3901 init_input(); 3902 init_label(); 3903 init_dropdown_menu(); 3904 init_FilterValue(); 3905 init_filters(); 3906 init_company_filters(); 3907 init_usePositionTaxonomy(); 3908 init_seniority(); 3909 init_lead_signal_filters(); 3910 import_jsx_runtime$10 = require_jsx_runtime(); 3911 t = (key) => i18n.t(`js.dashboard.lead_signals.filters.${key}`); 3912 optionsFor = (values) => uniqueFilterValues(values).map((value) => ({ 3913 value, 3914 label: value 3915 })); 3916 })))(); 3917} 3918//#endregion 3919//#region app/frontend/react/components/lead-signals-sidepanel.tsx 3920function contactLead(emailId, teamActivity, hydratedResult) { 3921 const data = teamActivity.activity.associated_record_data; 3922 const domainValue = data?.domain ?? null; 3923 const company = data?.company_name ?? null; 3924 const generic = Boolean(data?.generic); 3925 const decisionMaker = !generic && Boolean(data?.decision_maker); 3926 const locationTo = teamActivity.activity.event_type === "location_changed" ? teamActivity.activity.metadata?.item_to?.trim() : null; 3927 const secondaryInfo = locationTo ? i18n.t("js.dashboard.lead_signals.location_changed", { location: locationTo }) : null; 3928 const secondaryInfoMeta = secondaryInfo ? formatTimeAgo(new Date(teamActivity.created_at)) : null; 3929 const activityResult = { 3930 id: emailId, 3931 name: generic ? null : data?.full_email ?? null, 3932 email: data?.email ?? null, 3933 jobTitle: generic ? null : data?.job_title ?? null, 3934 companyName: company, 3935 companyLogo: domainValue ? buildCompanyLogoUrl(domainValue) : null, 3936 domain: domainValue, 3937 revealed: Boolean(data?.revealed), 3938 verification: { 3939 status: data?.verification_status ?? "unknown", 3940 date: data?.verification_date ? new Date(data.verification_date) : null 3941 }, 3942 decisionMaker, 3943 linkedinPresent: !generic && Boolean(data?.linkedin_present), 3944 linkedin: generic ? null : data?.linkedin ?? null, 3945 twitterPresent: false, 3946 sources: [], 3947 type: generic ? "generic" : "personal", 3948 phoneNumberExists: false, 3949 fullNameExists: true, 3950 lead: data?.lead_id ? { 3951 id: data.lead_id, 3952 leads_list_ids: [], 3953 campaign_ids: [] 3954 } : null 3955 }; 3956 const domain = domainValue ? { 3957 id: 0, 3958 value: domainValue, 3959 company_name: company, 3960 company_type: null, 3961 size: null, 3962 founded_year: null, 3963 address: null, 3964 category: null, 3965 number_of_emails: 0 3966 } : null; 3967 const companyId = isNumber(data?.domain_id) ? data.domain_id : teamActivity.activity.activity_type === "domain" && isNumber(teamActivity.activity.activity_id) ? teamActivity.activity.activity_id : null; 3968 const result = hydratedResult ? { 3969 ...hydratedResult, 3970 jobTitle: teamActivity.activity.event_type === "position_changed" ? activityResult.jobTitle ?? hydratedResult.jobTitle : hydratedResult.jobTitle 3971 } : activityResult; 3972 return { 3973 filterJobTitle: teamActivity.activity.event_type === "position_changed" && isObject(teamActivity.activity.metadata) && isString(teamActivity.activity.metadata.item_to) ? teamActivity.activity.metadata.item_to : result.jobTitle ?? null, 3974 location: locationTo ?? null, 3975 emailId, 3976 leadId: result.lead?.id ?? data?.lead_id ?? null, 3977 result, 3978 domain, 3979 decisionMaker: Boolean(result.decisionMaker), 3980 generic: result.type === "generic",
3981 companyId, 3982 secondaryInfo, 3983 secondaryInfoMeta 3984 }; 3985} 3986function UnderlyingSignal(t0) { 3987 const $ = (0, import_compiler_runtime$7.c)(17); 3988 const { signal } = t0; 3989 let t1; 3990 if ($[0] !== signal.icon) { 3991 t1 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("div", { 3992 className: "lead-signals-sidepanel__signal-icon", 3993 children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Icon, { 3994 icon: signal.icon, 3995 size: "sm" 3996 }) 3997 }); 3998 $[0] = signal.icon; 3999 $[1] = t1; 4000 } else t1 = $[1]; 4001 let t2; 4002 if ($[2] !== signal.title) { 4003 t2 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("span", { 4004 className: "lead-signals-sidepanel__signal-title", 4005 children: signal.title 4006 }); 4007 $[2] = signal.title; 4008 $[3] = t2; 4009 } else t2 = $[3]; 4010 let t3; 4011 if ($[4] !== signal.description) { 4012 t3 = signal.description && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("span", { 4013 className: "lead-signals-sidepanel__signal-description", 4014 children: signal.description 4015 }); 4016 $[4] = signal.description; 4017 $[5] = t3; 4018 } else t3 = $[5]; 4019 let t4; 4020 if ($[6] !== t2 || $[7] !== t3) { 4021 t4 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)("div", { 4022 className: "lead-signals-sidepanel__signal-main", 4023 children: [t2, t3] 4024 }); 4025 $[6] = t2; 4026 $[7] = t3; 4027 $[8] = t4; 4028 } else t4 = $[8]; 4029 let t5; 4030 if ($[9] !== signal.createdAt) { 4031 t5 = formatTimeAgo(new Date(signal.createdAt)); 4032 $[9] = signal.createdAt; 4033 $[10] = t5; 4034 } else t5 = $[10]; 4035 let t6; 4036 if ($[11] !== t5) { 4037 t6 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("span", { 4038 className: "lead-signals-sidepanel__timestamp", 4039 children: t5 4040 }); 4041 $[11] = t5; 4042 $[12] = t6; 4043 } else t6 = $[12]; 4044 let t7; 4045 if ($[13] !== t1 || $[14] !== t4 || $[15] !== t6) { 4046 t7 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)("div", { 4047 className: "lead-signals-sidepanel__signal", 4048 children: [ 4049 t1, 4050 t4, 4051 t6 4052 ] 4053 }); 4054 $[13] = t1; 4055 $[14] = t4; 4056 $[15] = t6; 4057 $[16] = t7; 4058 } else t7 = $[16]; 4059 return t7; 4060} 4061function LeadSignalsSidepanel(props) { 4062 const $ = (0, import_compiler_runtime$7.c)(12); 4063 let t0; 4064 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 4065 t0 = {}; 4066 $[0] = t0; 4067 } else t0 = $[0]; 4068 const [jobContent, setJobContent] = (0, import_react$8.useState)(t0); 4069 let t1; 4070 if ($[1] === Symbol.for("react.memo_cache_sentinel")) { 4071 t1 = /* @__PURE__ */ new Set(); 4072 $[1] = t1; 4073 } else t1 = $[1]; 4074 const requestedJobIds = (0, import_react$8.useRef)(t1); 4075 const instanceKey = props.isOpen && props.signal ? props.signal.id : "closed"; 4076 let t2; 4077 if ($[2] !== instanceKey || $[3] !== jobContent || $[4] !== props) {
4078 t2 = props.pendingContent ?? /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(LeadSignalsSidepanelContent, { 4079 ...props, 4080 jobContent, 4081 requestedJobIds, 4082 setJobContent 4083 }, instanceKey); 4084 $[2] = instanceKey; 4085 $[3] = jobContent; 4086 $[4] = props; 4087 $[5] = t2; 4088 } else t2 = $[5]; 4089 let t3; 4090 if ($[6] !== t2) { 4091 t3 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetContent, { 4092 size: "lg", 4093 className: "lead-signals-sidepanel p-0", 4094 children: t2 4095 }); 4096 $[6] = t2; 4097 $[7] = t3; 4098 } else t3 = $[7]; 4099 let t4; 4100 if ($[8] !== props.isOpen || $[9] !== props.onOpenChange || $[10] !== t3) { 4101 t4 = /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Sheet, { 4102 open: props.isOpen, 4103 onOpenChange: props.onOpenChange, 4104 children: t3 4105 }); 4106 $[8] = props.isOpen; 4107 $[9] = props.onOpenChange; 4108 $[10] = t3; 4109 $[11] = t4; 4110 } else t4 = $[11]; 4111 return t4; 4112} 4113function LeadSignalsSidepanelContent({ signal, isOpen, leadsLists: initialLeadsLists = [], folders = [], campaigns: initialCampaigns = [], jobContent, requestedJobIds, setJobContent }) { 4114 const [leadsLists, setLeadsLists] = (0, import_react$8.useState)(initialLeadsLists); 4115 const [campaigns, setCampaigns] = (0, import_react$8.useState)(initialCampaigns); 4116 const [companyFilters, setCompanyFilters] = (0, import_react$8.useState)(emptySignalCompanyFilters); 4117 const [peopleFilters, setPeopleFilters] = (0, import_react$8.useState)(emptySignalPeopleFilters); 4118 const sortedActivities = (0, import_react$8.useMemo)(() => { 4119 if (!signal) return []; 4120 return [...signal.activities].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); 4121 }, [signal]); 4122 const jobActivities = (0, import_react$8.useMemo)(() => sortedActivities.filter((teamActivity) => teamActivity.activity.event_type === "linkedin_job_added"), [sortedActivities]); 4123 const [jobDetailsLoading, setJobDetailsLoading] = (0, import_react$8.useState)(() => jobActivities.some((activity) => !requestedJobIds.current.has(teamActivityKey(activity)))); 4124 const [jobDetailsError, setJobDetailsError] = (0, import_react$8.useState)(false); 4125 (0, import_react$8.useEffect)(() => { 4126 if (!isOpen) return; 4127 const missingActivities = jobActivities.filter((activity_0) => !requestedJobIds.current.has(teamActivityKey(activity_0))); 4128 if (missingActivities.length === 0) return; 4129 const controller = new AbortController(); 4130 const abortSignal = controller.signal; 4131 const missingActivityCount = missingActivities.length; 4132 (async () => { 4133 let offset = 0; 4134 let batchSize = MAX_JOB_IDS_PER_REQUEST; 4135 while (offset < missingActivityCount) { 4136 if (abortSignal.aborted) return; 4137 const batchActivities = missingActivities.slice(offset, offset + batchSize); 4138 let processedCount = batchActivities.length; 4139 try { 4140 const path = dashboard_signal_details_path({ format: "json" }); 4141 const response = await fetch(path, { 4142 method: "POST", 4143 headers: { 4144 Accept: "application/json", 4145 "Content-Type": "application/json", 4146 "X-CSRF-Token": getCsrfToken() 4147 }, 4148 credentials: "same-origin", 4149 body: JSON.stringify({ activities: batchActivities.map(teamActivityReference) }), 4150 signal: abortSignal 4151 }); 4152 if (response.ok) { 4153 const data = await response.json(); 4154 if (abortSignal.aborted) return; 4155 const serverLimit = Number.isInteger(data.max_activity_ids) && Number(data.max_activity_ids) > 0 ? Number(data.max_activity_ids) : batchActivities.length; 4156 processedCount = Math.min(serverLimit, batchActivities.length); 4157 batchSize = Math.min(MAX_JOB_IDS_PER_REQUEST, serverLimit); 4158 batchActivities.slice(0, processedCount).forEach((activity_1) => requestedJobIds.current.add(teamActivityKey(activity_1))); 4159 const content = data.job_content ?? {}; 4160 if (Object.keys(content).length > 0) setJobContent((previous) => ({ 4161 ...previous, 4162 ...content 4163 })); 4164 } else if (!abortSignal.aborted) setJobDetailsError(true); 4165 } catch { 4166 if (abortSignal.aborted) return; 4167 setJobDetailsError(true); 4168 } 4169 offset += processedCount; 4170 } 4171 if (!abortSignal.aborted) setJobDetailsLoading(false); 4172 })(); 4173 return () => controller.abort(); 4174 }, [ 4175 isOpen, 4176 jobActivities, 4177 requestedJobIds, 4178 setJobContent 4179 ]); 4180 const enrichedActivities = (0, import_react$8.useMemo)(() => { 4181 if (Object.keys(jobContent).length === 0) return sortedActivities; 4182 return sortedActivities.map((teamActivity_0) => {
4183 const content_0 = jobContent[teamActivityKey(teamActivity_0)]; 4184 if (!content_0) return teamActivity_0; 4185 return { 4186 ...teamActivity_0, 4187 activity: { 4188 ...teamActivity_0.activity, 4189 associated_record_data: { 4190 ...teamActivity_0.activity.associated_record_data ?? {}, 4191 ...content_0 4192 } 4193 } 4194 }; 4195 }); 4196 }, [sortedActivities, jobContent]); 4197 const emailIds = (0, import_react$8.useMemo)(() => { 4198 const ids = /* @__PURE__ */ new Set(); 4199 for (const teamActivity_1 of enrichedActivities) { 4200 if (teamActivity_1.activity.activity_type !== "email" && !PEOPLE_SIGNAL_EVENT_TYPES.has(teamActivity_1.activity.event_type)) continue; 4201 const data_0 = teamActivity_1.activity.associated_record_data; 4202 if (isNumber(data_0?.email_id)) ids.add(data_0.email_id); 4203 } 4204 return [...ids]; 4205 }, [enrichedActivities]); 4206 const { results, setResults, revealingIds, revealedIds, lastRevealedIds, revealedLeadsListId, handleReveal, handleRevealMultiple } = useEmailSearch({ 4207 source: "dashboard", 4208 context: `dashboard-signal-${signal?.id ?? "closed"}` 4209 }); 4210 const [emailsLoading, setEmailsLoading] = (0, import_react$8.useState)(emailIds.length > 0); 4211 const [emailsError, setEmailsError] = (0, import_react$8.useState)(false); 4212 (0, import_react$8.useEffect)(() => { 4213 if (!isOpen || emailIds.length === 0) return; 4214 const controller_0 = new AbortController(); 4215 fetch(multi_domain_search_emails_search_index_path(), { 4216 method: "POST", 4217 headers: { 4218 "Content-Type": "application/json", 4219 Accept: "application/json", 4220 "X-CSRF-Token": getCsrfToken() 4221 }, 4222 body: JSON.stringify({ email_ids: emailIds }), 4223 signal: controller_0.signal 4224 }).then((response_0) => { 4225 if (!response_0.ok) { 4226 if (!controller_0.signal.aborted) { 4227 setEmailsError(true); 4228 setEmailsLoading(false); 4229 } 4230 return null; 4231 } 4232 return response_0.json(); 4233 }).then((data_1) => { 4234 if (!data_1 || controller_0.signal.aborted) return; 4235 setResults(data_1.emails ?? []); 4236 setEmailsLoading(false); 4237 }).catch((error) => { 4238 if (error.name === "AbortError" || controller_0.signal.aborted) return; 4239 setEmailsError(true); 4240 setEmailsLoading(false); 4241 }); 4242 return () => controller_0.abort(); 4243 }, [ 4244 emailIds,
4245 isOpen, 4246 setResults 4247 ]); 4248 const resultsById = (0, import_react$8.useMemo)(() => new Map(results.map((result) => [result.id, result])), [results]); 4249 const handleCampaignsSave = useCampaignsSave(results, campaigns, setCampaigns, setResults); 4250 const handleLeadsListSave = useLeadsListSave(results, setLeadsLists, setResults); 4251 const domainIds = (0, import_react$8.useMemo)(() => { 4252 const ids_0 = (signal?.eventType === "email_added" ? sortNewEmailActivitiesByVolume(sortedActivities) : sortedActivities).filter((ta) => ta.activity.activity_type === "domain").map((ta_0) => ta_0.activity.activity_id).filter((id) => isNumber(id)); 4253 return Array.from(new Set(ids_0)); 4254 }, [sortedActivities, signal?.eventType]); 4255 const hasCompanies = domainIds.length > 0; 4256 const signalsByCompanyId = (0, import_react$8.useMemo)(() => { 4257 const map = {}; 4258 for (const teamActivity_2 of enrichedActivities) { 4259 if (teamActivity_2.activity.activity_type !== "domain") continue; 4260 const id_0 = teamActivity_2.activity.activity_id; 4261 if (!isNumber(id_0)) continue; 4262 (map[id_0] ||= []).push(teamActivity_2); 4263 } 4264 return map; 4265 }, [enrichedActivities]); 4266 const { companies, isLoading, error: companiesError, selectedIds, handleSelect, clearSelection, retry } = useCompanySearch({ 4267 domainIds, 4268 enabled: isOpen && hasCompanies, 4269 timeoutMs: 15e3 4270 }); 4271 const filteredCompanies = (0, import_react$8.useMemo)(() => filterSignalCompanies(companies, signalsByCompanyId, companyFilters), [ 4272 companies, 4273 signalsByCompanyId, 4274 companyFilters 4275 ]); 4276 const changeCompanyFilters = (filters) => { 4277 clearSelection(); 4278 setCompanyFilters(filters); 4279 }; 4280 const showCompaniesList = hasCompanies && (isLoading || companies.length > 0); 4281 const loadedCompanyIds = (0, import_react$8.useMemo)(() => new Set(companies.map((company) => company.id)), [companies]); 4282 const companylessActivities = (0, import_react$8.useMemo)(() => { 4283 if (!showCompaniesList) return enrichedActivities; 4284 return enrichedActivities.filter((teamActivity_3) => { 4285 if (teamActivity_3.activity.activity_type !== "domain") return true; 4286 if (isLoading) return false; 4287 const id_1 = teamActivity_3.activity.activity_id; 4288 return !isNumber(id_1) || !loadedCompanyIds.has(id_1); 4289 }); 4290 }, [ 4291 showCompaniesList, 4292 isLoading, 4293 enrichedActivities, 4294 loadedCompanyIds 4295 ]); 4296 const [showAllSignals, setShowAllSignals] = (0, import_react$8.useState)(false); 4297 const contactLeads = (0, import_react$8.useMemo)(() => { 4298 const seen = /* @__PURE__ */ new Set(); 4299 const rows = []; 4300 for (const teamActivity_4 of companylessActivities) { 4301 const data_2 = teamActivity_4.activity.associated_record_data; 4302 const leadId = data_2?.lead_id; 4303 const emailId = data_2?.email_id; 4304 if (!isNumber(emailId)) continue; 4305 if (!isNumber(leadId) || seen.has(leadId)) continue; 4306 seen.add(leadId); 4307 rows.push(contactLead(emailId, teamActivity_4, resultsById.get(emailId))); 4308 } 4309 return rows; 4310 }, [companylessActivities, resultsById]); 4311 const hasContactLeads = signal?.entityType === "email" && contactLeads.length > 0; 4312 const newPeople = (0, import_react$8.useMemo)(() => { 4313 if (!signal || !PEOPLE_SIGNAL_EVENT_TYPES.has(signal.eventType)) return []; 4314 const seen_0 = /* @__PURE__ */ new Set(); 4315 const rows_0 = []; 4316 for (const teamActivity_5 of enrichedActivities) { 4317 if (!PEOPLE_SIGNAL_EVENT_TYPES.has(teamActivity_5.activity.event_type)) continue; 4318 const emailId_0 = teamActivity_5.activity.associated_record_data?.email_id; 4319 if (!isNumber(emailId_0) || seen_0.has(emailId_0)) continue; 4320 seen_0.add(emailId_0); 4321 rows_0.push(contactLead(emailId_0, teamActivity_5, resultsById.get(emailId_0))); 4322 } 4323 return rows_0; 4324 }, [ 4325 signal, 4326 enrichedActivities, 4327 resultsById 4328 ]); 4329 const hasNewPeople = newPeople.length > 0; 4330 const isPeopleSignal = !!signal && PEOPLE_SIGNAL_EVENT_TYPES.has(signal.eventType); 4331 const isExecutiveSignal = signal?.eventType === "executive_added"; 4332 const newPeopleCompanies = new Set(newPeople.map((person) => person.companyId ?? person.result.companyName ?? person.result.domain).filter((company_0) => company_0 != null)); 4333 const directNewPeople = isPeopleSignal && hasNewPeople && domainIds.length <= 1; 4334 const singleCompanyPeople = directNewPeople && newPeopleCompanies.size === 1; 4335 const newPeopleCompany = (signal?.entity?.kind === "company" ? signal.entity.name : null) ?? newPeople[0]?.result.companyName ?? null; 4336 const selectedNewPeople = (0, import_react$8.useMemo)(() => { 4337 if (directNewPeople || !hasCompanies) return newPeople; 4338 const selection = new Set(selectedIds); 4339 const selectedCompanyIds = /* @__PURE__ */ new Set(); 4340 for (const company_1 of filteredCompanies) if (selection.size === 0 || selection.has(company_1.id)) selectedCompanyIds.add(company_1.id); 4341 return newPeople.filter((person_0) => person_0.companyId != null && selectedCompanyIds.has(person_0.companyId)); 4342 }, [ 4343 newPeople, 4344 selectedIds, 4345 filteredCompanies, 4346 directNewPeople, 4347 hasCompanies 4348 ]); 4349 const [contactTab, setContactTab] = (0, import_react$8.useState)("people"); 4350 const nonLeadActivities = (0, import_react$8.useMemo)(() => { 4351 if (!hasContactLeads) return companylessActivities; 4352 const leadIds = new Set(contactLeads.map((lead) => lead.leadId)); 4353 return companylessActivities.filter((teamActivity_6) => { 4354 const leadId_0 = teamActivity_6.activity.associated_record_data?.lead_id; 4355 return !isNumber(leadId_0) || !leadIds.has(leadId_0); 4356 }); 4357 }, [ 4358 hasContactLeads, 4359 companylessActivities, 4360 contactLeads 4361 ]); 4362 const [selectedLeadIds, setSelectedLeadIds] = (0, import_react$8.useState)([]); 4363 const changePeopleFilters = (filters_0) => { 4364 setSelectedLeadIds([]); 4365 setPeopleFilters(filters_0); 4366 }; 4367 const toggleLead = (0, import_react$8.useCallback)((id_2) => setSelectedLeadIds((previous_0) => { 4368 const selected = new Set(previous_0); 4369 if (selected.has(id_2)) selected.delete(id_2); 4370 else selected.add(id_2); 4371 return [...selected]; 4372 }), []); 4373 const [isAddingToList, setIsAddingToList] = (0, import_react$8.useState)(false); 4374 const handleAddToList = (0, import_react$8.useCallback)(async (listId, leadIds_0) => { 4375 if (leadIds_0.length === 0) return false;
4376 setIsAddingToList(true); 4377 return fetch(leads_bulk_path(), { 4378 method: "PUT", 4379 headers: { 4380 "Content-Type": "application/json", 4381 Accept: "application/json", 4382 "X-CSRF-Token": getCsrfToken() 4383 }, 4384 body: JSON.stringify({ 4385 to_leads_list_id: listId, 4386 lead_ids: leadIds_0 4387 }) 4388 }).then(async (response_1) => { 4389 if (!response_1.ok) { 4390 const data_3 = await response_1.json().catch(() => ({})); 4391 toast.danger(data_3?.message || i18n.t("js.dashboard.lead_signals.list_save.error")); 4392 return false; 4393 } 4394 const data_4 = await response_1.json().catch(() => ({})); 4395 toast.success(data_4?.message || i18n.t("js.dashboard.lead_signals.list_save.success")); 4396 return true; 4397 }).catch(() => { 4398 toast.danger(i18n.t("js.dashboard.lead_signals.list_save.error")); 4399 return false; 4400 }).finally(() => setIsAddingToList(false)); 4401 }, []); 4402 const [view, setView] = (0, import_react$8.useState)(() => directNewPeople ? "new_people" : "companies"); 4403 const [newPeopleCompanyId, setNewPeopleCompanyId] = (0, import_react$8.useState)(null); 4404 const newPeopleInView = (0, import_react$8.useMemo)(() => { 4405 if (newPeopleCompanyId == null) return selectedNewPeople; 4406 const rowEmailIds = new Set((signalsByCompanyId[newPeopleCompanyId] ?? []).flatMap((teamActivity_7) => { 4407 const emailId_1 = teamActivity_7.activity.associated_record_data?.email_id; 4408 return isNumber(emailId_1) ? [emailId_1] : []; 4409 })); 4410 return newPeople.filter((person_1) => rowEmailIds.has(person_1.emailId)); 4411 }, [ 4412 newPeopleCompanyId, 4413 newPeople, 4414 selectedNewPeople, 4415 signalsByCompanyId 4416 ]); 4417 const newPeopleViewCompany = (0, import_react$8.useMemo)(() => { 4418 if (newPeopleCompanyId == null) return null; 4419 const company_2 = companies.find((candidate) => candidate.id === newPeopleCompanyId); 4420 return company_2?.company_name || company_2?.value || null; 4421 }, [companies, newPeopleCompanyId]); 4422 const [peopleProps, setPeopleProps] = (0, import_react$8.useState)(null); 4423 const [peopleError, setPeopleError] = (0, import_react$8.useState)(null); 4424 const peopleAbortRef = (0, import_react$8.useRef)(null); 4425 (0, import_react$8.useEffect)(() => () => peopleAbortRef.current?.abort(), []); 4426 const selectedCompanies = (0, import_react$8.useMemo)(() => { 4427 const selectedCompanyIds_0 = new Set(selectedIds); 4428 return filteredCompanies.filter((company_3) => selectedCompanyIds_0.has(company_3.id)); 4429 }, [filteredCompanies, selectedIds]); 4430 const hasSelection = selectedCompanies.length > 0; 4431 const targetedCompanies = hasSelection ? selectedCompanies : filteredCompanies; 4432 const peopleCount = (0, import_react$8.useMemo)(() => targetedCompanies.reduce((sum, company_4) => sum + (company_4.number_of_emails || 0), 0), [targetedCompanies]); 4433 const findPeopleText = hasSelection ? i18n.t("js.discover.index.find_people", { 4434 count: peopleCount, 4435 results: CurrencyHelper.numberWithDelimiter(peopleCount) 4436 }) : i18n.t("js.discover.index.find_all_people", { 4437 count: peopleCount, 4438 results: CurrencyHelper.numberWithDelimiter(peopleCount) 4439 }); 4440 const domainsParam = targetedCompanies.map((company_5) => company_5.value).join("|"); 4441 const fetchPeopleProps = (0, import_react$8.useCallback)(async () => { 4442 peopleAbortRef.current?.abort(); 4443 const controller_1 = new AbortController(); 4444 peopleAbortRef.current = controller_1; 4445 try { 4446 const response_2 = await fetch(`${leads_companies_people_path()}.json?from=companies&metadata_only=true`, { 4447 headers: { 4448 Accept: "application/json", 4449 "X-CSRF-Token": getCsrfToken() 4450 }, 4451 signal: controller_1.signal 4452 }); 4453 if (!response_2.ok) { 4454 if (!controller_1.signal.aborted) setPeopleError("Failed to load. Please try again."); 4455 return; 4456 } 4457 const data_5 = await response_2.json(); 4458 if (!controller_1.signal.aborted) setPeopleProps(data_5); 4459 } catch (error_0) { 4460 if (controller_1.signal.aborted || error_0?.name === "AbortError") return; 4461 setPeopleError("Failed to load. Please try again."); 4462 } 4463 }, []); 4464 const openPeopleLayer = (0, import_react$8.useCallback)(() => { 4465 setView("people"); 4466 if (!peopleProps && !peopleError) fetchPeopleProps(); 4467 }, [ 4468 peopleProps, 4469 peopleError, 4470 fetchPeopleProps 4471 ]); 4472 const leavePeopleLayer = (0, import_react$8.useCallback)(() => { 4473 peopleAbortRef.current?.abort(); 4474 setView("companies"); 4475 setPeopleProps(null); 4476 setPeopleError(null); 4477 setSelectedLeadIds([]); 4478 setContactTab("people"); 4479 setNewPeopleCompanyId(null); 4480 setPeopleFilters(emptySignalPeopleFilters()); 4481 }, []); 4482 const openNewPeopleLayer = (0, import_react$8.useCallback)((companyId = null) => { 4483 setSelectedLeadIds([]); 4484 setContactTab("people"); 4485 setNewPeopleCompanyId(companyId); 4486 setPeopleFilters(emptySignalPeopleFilters()); 4487 setView("new_people"); 4488 }, []); 4489 const renderContactRows = (rows_1, mode) => { 4490 const filteredRows = filterSignalPeople(rows_1, peopleFilters); 4491 const peopleRows = filteredRows.filter((lead_0) => !lead_0.generic); 4492 const genericRows = filteredRows.filter((lead_1) => lead_1.generic); 4493 const decisionMakers = filteredRows.filter((lead_2) => lead_2.decisionMaker).length; 4494 const visible = contactTab === "decision_makers" ? filteredRows.filter((lead_3) => lead_3.decisionMaker) : contactTab === "generic" ? genericRows : peo
4494pleRows; 4495 const selectedEmailIds = new Set(selectedLeadIds); 4496 const revealingEmailIds = new Set(revealingIds); 4497 const newlyRevealedEmailIds = new Set(revealedIds); 4498 const visibleByEmailId = new Map(visible.map((lead_4) => [lead_4.emailId, lead_4])); 4499 const targetIds = []; 4500 for (const lead_5 of visible) if (selectedEmailIds.size === 0 || selectedEmailIds.has(lead_5.emailId)) targetIds.push(lead_5.emailId); 4501 const unrevealedTargets = mode === "reveal" ? targetIds.filter((id_3) => !visibleByEmailId.get(id_3)?.result.revealed && !newlyRevealedEmailIds.has(id_3)) : []; 4502 const unrevealedRows = unrevealedTargets.flatMap((id_4) => { 4503 const row = visibleByEmailId.get(id_4); 4504 return row ? [row] : []; 4505 }); 4506 let personalRevealUnits = 0; 4507 const genericRevealDomains = /* @__PURE__ */ new Set(); 4508 for (const row_0 of unrevealedRows) if (row_0.generic) genericRevealDomains.add(row_0.result.domain ?? `email:${row_0.emailId}`); 4509 else personalRevealUnits += 1; 4510 const revealUnits = personalRevealUnits + genericRevealDomains.size; 4511 const existingLeadIds = targetIds.map((emailId_2) => visibleByEmailId.get(emailId_2)?.leadId).filter((leadId_1) => isNumber(leadId_1)); 4512 return /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(import_jsx_runtime$9.Fragment, { children: [ 4513 (mode === "reveal" || rows_1.length > 1) && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SignalPeopleFilters, { 4514 people: rows_1, 4515 value: peopleFilters, 4516 onChange: changePeopleFilters, 4517 showLocation: signal?.eventType === "location_changed" 4518 }), 4519 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(PillTabs, { 4520 className: "lead-signals-sidepanel__contacts-tabs", 4521 value: contactTab, 4522 onValueChange: (value) => { 4523 setSelectedLeadIds([]); 4524 setContactTab(value); 4525 }, 4526 children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsList, { 4527 size: "md", 4528 fullWidth: true, 4529 children: [ 4530 /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsTrigger, { 4531 value: "people", 4532 children: [/* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(PillTabsTriggerName, { children: i18n.t("js.dashboard.lead_signals.tabs.people") }
4532), /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsTriggerHint, { children: ["· ", CurrencyHelper.numberWithDelimiter(peopleRows.length)] })] 4533 }), 4534 /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsTrigger, { 4535 value: "decision_makers", 4536 children: [ 4537 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Icon, { 4538 icon: "crown", 4539 size: "sm", 4540 "aria-hidden": "true" 4541 }), 4542 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(PillTabsTriggerName, { children: i18n.t("js.dashboard.lead_signals.tabs.decision_makers") }), 4543 /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsTriggerHint, { children: ["· ", CurrencyHelper.numberWithDelimiter(decisionMakers)] }) 4544 ] 4545 }), 4546 rows_1.some((lead_6) => lead_6.generic) && /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsTrigger, { 4547 value: "generic", 4548 children: [/* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(PillTabsTriggerName, { children: i18n.t("js.dashboard.lead_signals.tabs.generic") }
4548), /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(PillTabsTriggerHint, { children: ["· ", CurrencyHelper.numberWithDelimiter(genericRows.length)] })] 4549 }) 4550 ] 4551 }) 4552 }), 4553 /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)("section", { 4554 className: "lead-signals-sidepanel__contacts", 4555 children: [ 4556 mode === "reveal" && contactTab === "generic" && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(GenericEmailRevealAlert, { 4557 results: genericRows.map((lead_7) => lead_7.result), 4558 numberOfCompanies: new Set(rows_1.map((lead_8) => lead_8.result.domain)).size, 4559 revealingIds, 4560 onReveal: handleReveal 4561 }), 4562 visible.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("p", { 4563 className: "py-4 text-muted", 4564 role: "status", 4565 children: i18n.t("js.dashboard.lead_signals.filters.no_people") 4566 }), 4567 visible.map((lead_9, index) => { 4568 const revealed = lead_9.result.revealed || newlyRevealedEmailIds.has(lead_9.emailId); 4569 return /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Result, { 4570 result: revealed === lead_9.result.revealed ? lead_9.result : { 4571 ...lead_9.result, 4572 revealed 4573 }, 4574 domain: lead_9.domain, 4575 secondaryInfo: lead_9.secondaryInfo, 4576 secondaryInfoMeta: lead_9.secondaryInfoMeta, 4577 selected: selectedEmailIds.has(lead_9.emailId), 4578 onSelect: (ids_1) => toggleLead(ids_1[0]), 4579 revealing: revealingEmailIds.has(lead_9.emailId), 4580 handleReveal, 4581 handleLeadsListSave, 4582 handleCampaignsSave, 4583 leadsLists, 4584 folders, 4585 campaigns, 4586 hideActions: mode === "add" || !revealed, 4587 isLast: index === visible.length - 1 4588 }, lead_9.emailId); 4589 }) 4590 ] 4591 }), 4592 mode === "reveal" && lastRevealedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("div", { 4593 className: "lead-signals-sidepanel__reveal-outcome", 4594 children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(Snackbar, { children: [ 4595 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Icon, { 4596 icon: "check-circle", 4597 variant: "solid", 4598 size: "sm", 4599 className: "text-success" 4600 }), 4601 /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(SnackbarText, { children: [ 4602 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("strong", { 4603 className: "fw-medium", 4604 children: i18n.t("js.multi_domain_search.footer.leads", { 4605 count: lastRevealedIds.length, 4606 pretty_count: CurrencyHelper.numberWithDelimiter(lastRevealedIds.length) 4607 }) 4608 }), 4609 "\xA0", 4610 revealedLeadsListId ? i18n.t("js.multi_domain_search.footer.saved_in_list", { listName: leadsLists.find((list) => list.id === revealedLeadsListId)?.name }) : i18n.t("js.multi_domain_search.footer.saved") 4611 ] }), 4612 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SnackbarLink, { 4613 href: leads_path(revealedLeadsListId ? { leads_list_id: revealedLeadsListId } : {}), 4614 turboFrame: "_top", 4615 children: i18n.t("js.multi_domain_search.footer.see_in_leads") 4616 }) 4617 ] }) 4618 }), 4619 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetFooter, { 4620 sticky: true, 4621 className: "lead-signals-sidepanel__footer", 4622 children: mode === "reveal" ? /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(RevealSaveDropdown, { 4623 id: `lead-signals-reveal-${signal?.id}`, 4624 label: unrevealedTargets.length === 0 ? "Save to a list" : selectedLeadIds.length > 0 ? `Reveal ${CurrencyHelper.numberWithDelimiter(unrevealedTargets.length)} lead${unrevealedTargets.length === 1 ? "" : "s"}` : "Reveal all", 4625 cost: unrevealedTargets.length > 0 ? CostCalculator.calculateVisualRepresentation("search", revealUnits) : void 0, 4626 count: unrevealedTargets.length, 4627 disabled: targetIds.length === 0 || isAddingToList || revealingIds.length > 0, 4628 leadsLists, 4629 folders, 4630 onReveal: () => void handleRevealMultiple(targetIds), 4631 onSave: (name, listId_0) => { 4632 setLeadsLists((previous_1) => previous_1.some((list_0) => list_0.id === listId_0) ? previous_1 : [...previous_1, { 4633 id: listId_0, 4634 name 4635 }]); 4636 handleRevealMultiple(targetIds, { 4637 leads_list_id: listId_0, 4638 leads_list_name: name 4639 }); 4640 } 4641 }) : /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(LeadSaveManager, { 4642 id: `lead-signals-add-${signal?.id}`, 4643 leadsLists, 4644 folders, 4645 onSave: (_name, listId_1) => handleAddToList(listId_1, existingLeadIds), 4646 disableButtons: isAddingToList, 4647 cost: null, 4648 children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Button, { 4649 variant: "primary", 4650 disabled: targetIds.length === 0 || isAddingToList, 4651 children: selectedLeadIds.length > 0 ? `Add ${CurrencyHelper.numberWithDelimiter(selectedLeadIds.length)} lead${selectedLeadIds.length === 1 ? "" : "s"} to a list` : `Add all leads to a list · ${CurrencyHelper.numberWithDelimiter(targetIds.length)}` 4652 }) 4653 }) 4654 }) 4655 ] }); 4656 }; 4657 const nonLeadSignalRows = (0, import_react$8.useMemo)(() => signalContentRowsFor(nonLeadActivities), [nonLeadActivities]); 4658 const visibleSignals = showAllSignals ? nonLeadSignalRows : nonLeadSignalRows.slice(0, 1); 4659 return /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(import_jsx_runtime$9.Fragment, { children: !signal ? null : view === "people" && hasCompanies ? /* @__P
4659URE__ */ (0, import_jsx_runtime$9.jsxs)(import_jsx_runtime$9.Fragment, { children: [ 4660 /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(SheetHeader, { 4661 className: "sidepanel__header--with-back", 4662 children: [/* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetBack, { 4663 "aria-label": "Back to companies", 4664 onClick: leavePeopleLayer 4665 }), /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetTitle, { children: i18n.t("js.multi_domain_search.header") })] 4666 }), 4667 !peopleProps && !peopleError && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("div", { 4668 className: "d-flex flex-grow-1 align-items-center justify-content-center grey", 4669 children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Icon, { 4670 loading: true, 4671 "aria-label": "Loading" 4672 }) 4673 }), 4674 peopleError && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("div", { 4675 className: "m-8 text-center text-danger", 4676 children: peopleError 4677 }), 4678 peopleProps && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(MultiDomainSearchLegacy, { 4679 ...peopleProps, 4680 type: "domains", 4681 source: "companies", 4682 filterParams: { domains: domainsParam }, 4683 numberOfCompanies: targetedCompanies.length, 4684 numberOfResults: peopleCount, 4685 genericEmailsCount: 0, 4686 personalEmailsCount: 0, 4687 hideHeader: true 4688 }) 4689 ] }) : view === "new_people" && hasNewPeople ? /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(import_jsx_runtime$9.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(SheetHeader, { 4690 className: directNewPeople ? void 0 : "sidepanel__header--with-back", 4691 children: [!directNewPeople && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetBack, { 4692 "aria-label": "Back to companies", 4693 onClick: leavePeopleLayer 4694 }), /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetTitle, { children: newPeopleViewCompany ? i18n.t("js.dashboard.lead_signals.new_people_heading.single_company", { 4695 title: categoryTitleFor(signal.eventType, newPeopleInView.length), 4696 company: newPeopleViewCompany 4697 }) : singleCompanyPeople && newPeopleCompany ? i18n.t("js.dashboard.lead_signals.new_people_heading.single_company", { 4698 title: sidepanelTitleFor(signal), 4699 company: newPeopleCompany 4700 }) : directNewPeople ? sidepanelTitleFor(signal) : i18n.t("js.dashboard.lead_signals.new_people_heading.multiple_companies") })] 4701 }
4701), emailsLoading ? /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("div", { 4702 className: "d-flex flex-grow-1 align-items-center justify-content-center grey", 4703 children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Icon, { 4704 loading: true, 4705 "aria-label": "Loading" 4706 }) 4707 }) : emailsError ? /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("div", { 4708 className: "m-8 text-center text-danger", 4709 role: "alert", 4710 children: "Couldnât load these contacts. Close the panel and try again." 4711 }) : renderContactRows(newPeopleInView, "reveal")] }) : /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(import_jsx_runtime$9.Fragment, { children: [ 4712 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetTitle, { children: sidepanelTitleFor(signal) }) }), 4713 hasContactLeads && renderContactRows(contactLeads, "add"), 4714 nonLeadSignalRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)("section", { 4715 className: "lead-signals-sidepanel__signals", 4716 children: [
4717 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("h3", { 4718 className: "lead-signals-sidepanel__heading", 4719 children: i18n.t("js.dashboard.lead_signals.signals_heading") 4720 }), 4721 visibleSignals.map((signalRow) => /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(UnderlyingSignal, { signal: signalRow }, signalRow.key)), 4722 nonLeadSignalRows.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Button, { 4723 variant: "link", 4724 size: "sm", 4725 className: "lead-signals-sidepanel__show-all", 4726 onClick: () => setShowAllSignals((value_0) => !value_0), 4727 children: showAllSignals ? i18n.t("js.dashboard.lead_signals.show_less") : i18n.t("js.dashboard.lead_signals.show_all", { count: nonLeadSignalRows.length }) 4728 }) 4729 ] 4730 }), 4731 hasCompanies && companiesError && /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)("div", { 4732 className: "m-8 text-center", 4733 role: "alert", 4734 children: [/* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("p", { 4735 className: "text-danger mb-4", 4736 children: "Couldnât load companies. Please try again." 4737 }), /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(Button, { 4738 size: "sm", 4739 onClick: retry, 4740 children: [/* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Icon, { 4741 icon: "refresh", 4742 size: "sm", 4743 "aria-hidden": "true" 4744 }), "Retry"] 4745 })] 4746 }), 4747 showCompaniesList && /* @__PURE__ */ (0, import_jsx_runtime$9.jsxs)(import_jsx_runtime$9.Fragment, { children: [ 4748 companies.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SignalCompanyFilters, { 4749 companies, 4750 activities: companies.flatMap((company_6) => signalsByCompanyId[company_6.id] ?? []), 4751 eventType: signal.eventType, 4752 value: companyFilters, 4753 onChange: changeCompanyFilters, 4754 jobFiltersReady: !jobDetailsLoading && !jobDetailsError 4755 }), 4756 companies.length >
4756 1 && (jobDetailsLoading || jobDetailsError) && /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)("p", { 4757 className: "px-6 text-muted", 4758 role: jobDetailsError ? "alert" : "status", 4759 children: i18n.t(`js.dashboard.lead_signals.filters.${jobDetailsError ? "job_error" : "job_loading"}`) 4760 }), 4761 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(CompaniesList, { 4762 className: "px-4", 4763 companies: filteredCompanies, 4764 selectedIds, 4765 onSelect: handleSelect, 4766 isLoading, 4767 emptyMessage: "No companies found", 4768 signalsByCompanyId, 4769 onShowNewPeople: hasNewPeople ? openNewPeopleLayer : void 0 4770 }, signal.id), 4771 /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(SheetFooter, { 4772 sticky: true, 4773 className: "lead-signals-sidepanel__footer", 4774 children: hasNewPeople ? /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Button, { 4775 variant: "primary", 4776 disabled: isLoading || selectedNewPeople.length === 0, 4777 onClick: () => openNewPeopleLayer(), 4778 children: isExecutiveSignal ? i18n.t("js.dashboard.lead_signals.show_new_executives_cta", { 4779 count: selectedNewPeople.length, 4780 formatted_count: CurrencyHelper.numberWithDelimiter(selectedNewPeople.length) 4781 }) : `Find new people · ${CurrencyHelper.numberWithDelimiter(selectedNewPeople.length)}` 4782 }) : /* @__PURE__ */ (0, import_jsx_runtime$9.jsx)(Button, { 4783 variant: "primary", 4784 disabled: isLoading || peopleCount === 0, 4785 onClick: openPeopleLayer, 4786 children: findPeopleText 4787 }) 4788 }) 4789 ] }) 4790 ] }) }); 4791} 4792var import_compiler_runtime$7, import_react$8, import_jsx_runtime$9, PEOPLE_SIGNAL_EVENT_TYPES, MAX_JOB_IDS_PER_REQUEST; 4793function init_lead_signals_sidepanel() { 4794 return (init_lead_signals_sidepanel = __esmMin((() => { 4795 import_compiler_runtime$7 = require_compiler_runtime(); 4796 import_react$8 = require_react(); 4797 init_sheet(); 4798 init_button(); 4799 init_icon(); 4800 init_toast(); 4801 init_reveal_save_dropdown(); 4802 init_generic_email_reveal_alert(); 4803 init_snackbar(); 4804 init_costCalculator(); 4805 init_pill_tabs(); 4806 init_lead_save_manager(); 4807 init_result(); 4808 init_company_logo(); 4809 init_currency_helper(); 4810 init_routes(); 4811 init_utils(); 4812 init_useCompanySearch(); 4813 init_companies_list(); 4814 init_multi_domain_search(); 4815 init_lead_signal_item(); 4816 init_signal_content(); 4817 init_lead_signals_aggregator(); 4818 init_useEmailSearch(); 4819 init_useCampaignsSave(); 4820 init_useLeadsListSave(); 4821 init_lead_signal_filters$1(); 4822 init_lead_signal_filters(); 4823 import_jsx_runtime$9 = require_jsx_runtime(); 4824 PEOPLE_SIGNAL_EVENT_TYPES = /* @__PURE__ */ new Set(["email_added", "executive_added"]); 4825 MAX_JOB_IDS_PER_REQUEST = 2e3; 4826 })))(); 4827} 4828//#endregion 4829//#region app/frontend/react/hooks/use-grouped-lead-signals.ts 4830async function markGroupedSignalSeen(groupToken) { 4831 let cursor = null; 4832 do { 4833 const response = await fetch(dashboard_seen_grouped_lead_signals_path(), { 4834 method: "POST", 4835 headers: { 4836 "Content-Type": "application/json", 4837 "X-CSRF-Token": getCsrfToken() 4838 }, 4839 body: JSON.stringify({ 4840 group_token: groupToken, 4841 timezone: leadSignalsTimezone(), 4842 cursor 4843 }) 4844 }); 4845 if (!response.ok) return; 4846 cursor = (await response.json()).next_cursor; 4847 } while (cursor); 4848} 4849async function requestPage(controller, cursor) { 4850 const deadline = setTimeout(() => controller.abort(), 45e3); 4851 const cancelDeadline = () => clearTimeout(deadline); 4852 controller.signal.addEventListener("abort", cancelDeadline, { once: true }); 4853 try { 4854 const params = { timezone: leadSignalsTimezone() }; 4855 if (cursor) params.cursor = cursor; 4856 const response = await fetch(dashboard_grouped_lead_signals_path(params), { 4857 signal: controller.signal, 4858 headers: { Accept: "application/json" } 4859 }); 4860 if (response.redirected) return { 4861 status: "redirect", 4862 url: response.url 4863 }; 4864 if (!response.ok) return { status: response.status === 422 ? "expired" : "error" }; 4865 const page = await response.json(); 4866 if (controller.signal.aborted) return { status: "error" }; 4867 return { 4868 status: "success", 4869 signals: page.groups.map(groupedLeadSignalToSignal), 4870 cursor: page.next_cursor 4871 }; 4872 } catch { 4873 return { status: "error" }; 4874 } finally { 4875 clearTimeout(deadline); 4876 controller.signal.removeEventListener("abort", cancelDeadline); 4877 } 4878} 4879function useGroupedLeadSignals(enabled) { 4880 const $ = (0, import_compiler_runtime$6.c)(14); 4881 const [signals, setSignals] = (0, import_react$7.useState)(); 4882 const [cursor, setCursor] = (0, import_react$7.useState)(null); 4883 const [state, setState] = (0, import_react$7.useState)(enabled ? "loading" : "idle"); 4884 const [needsRefresh, setNeedsRefresh] = (0, import_react$7.useState)(false); 4885 const request = (0, import_react$7.useRef)(null); 4886 const generation = (0, import_react$7.useRef)(0); 4887 let t0; 4888 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 4889 t0 = (nextCursor, replace) => { 4890 if (replace) { 4891 request.current?.abort(); 4892 generation.current = generation.current + 1; 4893 } else if (request.current) return Promise.resolve(); 4894 const currentGeneration = generation.current; 4895 const controller = new AbortController(); 4896 request.current = controller; 4897 return requestPage(controller, nextCursor).then((result) => { 4898 if (request.current === controller) request.current = null; 4899 if (generation.current !== currentGeneration) return; 4900 if (result.status === "redirect") { 4901 window.location.href = result.url; 4902 return; 4903 } 4904 if (result.status !== "success") { 4905 if (replace) setSignals([]); 4906 setNeedsRefresh(result.status === "expired"); 4907 setState("error"); 4908 return; 4909 } 4910 setSignals((previous) => { 4911 if (replace) return result.signals; 4912 const byId = new Map((previous ?? []).map(_temp$2));
4913 result.signals.forEach((signal_0) => byId.set(signal_0.id, signal_0)); 4914 return [...byId.values()]; 4915 }); 4916 setCursor(result.cursor); 4917 setState("idle"); 4918 }); 4919 }; 4920 $[0] = t0; 4921 } else t0 = $[0]; 4922 const load = t0; 4923 let t1; 4924 if ($[1] === Symbol.for("react.memo_cache_sentinel")) { 4925 t1 = () => { 4926 setNeedsRefresh(false); 4927 setSignals(void 0); 4928 setCursor(null); 4929 setState("loading"); 4930 return load(null, true); 4931 }; 4932 $[1] = t1; 4933 } else t1 = $[1]; 4934 const refresh = t1; 4935 let t2; 4936 if ($[2] !== cursor || $[3] !== needsRefresh) { 4937 t2 = () => { 4938 if (needsRefresh) return refresh(); 4939 if (!cursor) return Promise.resolve(); 4940 setState("loading"); 4941 return load(cursor, false); 4942 }; 4943 $[2] = cursor; 4944 $[3] = needsRefresh; 4945 $[4] = t2; 4946 } else t2 = $[4]; 4947 const loadMore = t2; 4948 let t3; 4949 if ($[5] === Symbol.for("react.memo_cache_sentinel")) { 4950 t3 = (leadId) => setSignals((previous_0) => previous_0?.map((signal_1) => signal_1.entity?.kind === "lead" && signal_1.entity.leadId === leadId ? { 4951 ...signal_1, 4952 entity: { 4953 ...signal_1.entity, 4954 leadId: null 4955 } 4956 } : signal_1)); 4957 $[5] = t3; 4958 } else t3 = $[5]; 4959 const clearLead = t3; 4960 let t4; 4961 let t5; 4962 if ($[6] !== enabled) { 4963 t4 = () => { 4964 if (enabled) load(null, true); 4965 return () => { 4966 generation.current = generation.current + 1; 4967 request.current?.abort(); 4968 request.current = null; 4969 }; 4970 }; 4971 t5 = [enabled, load]; 4972 $[6] = enabled; 4973 $[7] = t4; 4974 $[8] = t5; 4975 } else { 4976 t4 = $[7]; 4977 t5 = $[8]; 4978 } 4979 (0, import_react$7.useEffect)(t4, t5); 4980 let t6; 4981 if ($[9] !== cursor || $[10] !== loadMore || $[11] !== signals || $[12] !== state) { 4982 t6 = { 4983 signals, 4984 cursor, 4985 state, 4986 refresh, 4987 loadMore, 4988 clearLead 4989 }; 4990 $[9] = cursor; 4991 $[10] = loadMore; 4992 $[11] = signals; 4993 $[12] = state; 4994 $[13] = t6; 4995 } else t6 = $[13]; 4996 return t6; 4997} 4998function _temp$2(signal) { 4999 return [signal.id, signal]; 5000} 5001var import_compiler_runtime$6, import_react$7, leadSignalsTimezone; 5002function init_use_grouped_lead_signals() { 5003 return (init_use_grouped_lead_signals = __esmMin((() => { 5004 import_compiler_runtime$6 = require_compiler_runtime(); 5005 import_react$7 = require_react(); 5006 init_routes(); 5007 init_utils(); 5008 init_lead_signals_aggregator(); 5009 leadSignalsTimezone = () => Intl.DateTimeFormat().resolvedOptions().timeZone; 5010 })))(); 5011} 5012//#endregion 5013//#region app/frontend/react/components/grouped-lead-signals-sidepanel.tsx 5014async function requestGroupDetails(groupToken, controller) { 5015 const rows = []; 5016 let cursor = null; 5017 let deadline; 5018 const cancelDeadline = () => clearTimeout(deadline); 5019 controller.signal.addEventListener("abort", cancelDeadline, { once: true }); 5020 try { 5021 do { 5022 deadline = setTimeout(() => controller.abort(), 45e3); 5023 const response = await fetch(dashboard_grouped_signal_details_path(), { 5024 method: "POST", 5025 headers: { 5026 "Content-Type": "application/json", 5027 "X-CSRF-Token": getCsrfToken() 5028 }, 5029 body: JSON.stringify({ 5030 group_token: groupToken, 5031 timezone: leadSignalsTimezone(), 5032 cursor 5033 }), 5034 signal: controller.signal 5035 }); 5036 if (!response.ok) return { status: response.status === 409 || response.status === 422 ? "changed" : "unavailable" }; 5037 const page = await response.json(); 5038 if (controller.signal.aborted) return { status: "unavailable" }; 5039 clearTimeout(deadline); 5040 if (cursor === null) markGroupedSignalSeen(groupToken).catch(() => {}); 5041 rows.push(...page.team_activities); 5042 cursor = page.next_cursor; 5043 } while (cursor); 5044 return { 5045 status: "complete", 5046 activities: rows 5047 }; 5048 } catch { 5049 return { status: "unavailable" }; 5050 } finally { 5051 clearTimeout(deadline); 5052 controller.signal.removeEventListener("abort", cancelDeadline); 5053 } 5054} 5055function GroupDetails(t0) { 5056 const $ = (0, import_compiler_runtime$5.c)(5); 5057 const { onRefresh, ...props } = t0; 5058 const { signal, onOpenChange } = props; 5059 const [activities, setActivities] = (0, import_react$6.useState)(null); 5060 const [error, setError] = (0, import_react$6.useState)(null); 5061 const [attempt, setAttempt] = (0, import_react$6.useState)(0); 5062 const groupToken = signal?.groupToken; 5063 (0, import_react$6.useEffect)(() => { 5064 if (!groupToken) return; 5065 const controller = new AbortController(); 5066 let active = true;
5067 requestGroupDetails(groupToken, controller).then((result) => { 5068 if (!active) return; 5069 if (result.status === "complete") setActivities(result.activities); 5070 else setError(result.status); 5071 }); 5072 return () => { 5073 active = false; 5074 controller.abort(); 5075 }; 5076 }, [groupToken, attempt]); 5077 if (!signal) return null; 5078 const T0 = LeadSignalsSidepanel; 5079 const t1 = activities ? { 5080 ...signal, 5081 activities 5082 } : signal; 5083 const t2 = activities ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)(import_jsx_runtime$8.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(SheetHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(SheetTitle, { children: sidepanelTitleFor(signal) }) }), /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(SheetBody, { children: error ? /* @__PURE__ */ (0, import_jsx_runtime$8.jsxs)("div", { 5084 role: "alert", 5085 children: [/* @__PURE__ */ (0, import_jsx_runtime$8.jsx)("p", { children: i18n.t(`js.dashboard.lead_signals.details_${error}`) }), /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(Button, { 5086 onClick: () => { 5087 if (error === "changed") { 5088 onOpenChange(false); 5089 onRefresh(); 5090 } else { 5091 setError(null); 5092 setAttempt(_temp$1); 5093 } 5094 }, 5095 children: i18n.t("js.dashboard.lead_signals.details_retry") 5096 })] 5097 }) : /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(LoadingSpinner, { text: i18n.t("js.dashboard.lead_signals.details_loading") }) })] }); 5098 let t3; 5099 if ($[0] !== T0 || $[1] !== props || $[2] !== t1 || $[3] !== t2) { 5100 t3 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(T0, { 5101 ...props, 5102 signal: t1, 5103 pendingContent: t2 5104 }); 5105 $[0] = T0; 5106 $[1] = props; 5107 $[2] = t1; 5108 $[3] = t2; 5109 $[4] = t3; 5110 } else t3 = $[4]; 5111 return t3; 5112} 5113function _temp$1(value) { 5114 return value + 1; 5115} 5116function GroupedLeadSignalsSidepanel(props) { 5117 const $ = (0, import_compiler_runtime$5.c)(4); 5118 if (!props.isOpen || !props.signal?.groupToken) { 5119 let t0; 5120 if ($[0] !== props) { 5121 t0 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(LeadSignalsSidepanel, { ...props }); 5122 $[0] = props; 5123 $[1] = t0; 5124 } else t0 = $[1]; 5125 return t0; 5126 } 5127 let t0; 5128 if ($[2] !== props) { 5129 t0 = /* @__PURE__ */ (0, import_jsx_runtime$8.jsx)(GroupDetails, { ...props }, props.signal.groupToken); 5130 $[2] = props; 5131 $[3] = t0; 5132 } else t0 = $[3]; 5133 return t0; 5134} 5135var import_compiler_runtime$5, import_react$6, import_jsx_runtime$8; 5136function init_grouped_lead_signals_sidepanel() { 5137 return (init_grouped_lead_signals_sidepanel = __esmMin((() => { 5138 import_compiler_runtime$5 = require_compiler_runtime(); 5139 import_react$6 = require_react(); 5140 init_lead_signals_sidepanel(); 5141 init_sheet(); 5142 init_button(); 5143 init_loading_spinner(); 5144 init_routes(); 5145 init_utils(); 5146 init_use_grouped_lead_signals(); 5147 init_lead_signals_aggregator(); 5148 import_jsx_runtime$8 = require_jsx_runtime(); 5149 })))(); 5150} 5151//#endregion 5152//#region app/frontend/react/components/dashboard/lead-signals-config-dialog.tsx 5153function LeadSignalsConfigDialog({ isOpen, onClose, watchedSignalTypes, availableSignalTypes, onSaved }) { 5154 const { dialogRef, handleBackdropClick, stopPropagation, close, createAbortController } = useDialog({ 5155 isOpen, 5156 onClose 5157 }); 5158 const [tooltipContainer, setTooltipContainer] = import_react$5.useState(null); 5159 import_react$5.useEffect(() => { 5160 if (isOpen) setTooltipContainer(dialogRef.current); 5161 }, [isOpen, dialogRef]); 5162 const companyKeys = import_react$5.useMemo(() => availableKeys(COMPANY_KEYS, availableSignalTypes), [availableSignalTypes]); 5163 const peopleKeys = import_react$5.useMemo(() => availableKeys(PEOPLE_KEYS, availableSignalTypes), [availableSignalTypes]); 5164 const [companyEvents, setCompanyEvents] = import_react$5.useState(() => toSignalStates(companyKeys, watchedSignalTypes)); 5165 const [peopleEvents, setPeopleEvents] = import_react$5.useState(() => toSignalStates(peopleKeys, watchedSignalTypes)); 5166 const [isSaving, setIsSaving] = import_react$5.useState(false); 5167 const [hasError, setHasError] = import_react$5.useState(false); 5168 const sessionRef = import_react$5.useRef(0); 5169 import_react$5.useEffect(() => { 5170 sessionRef.current += 1; 5171 }, [isOpen]); 5172 import_react$5.useEffect(() => { 5173 if (!isOpen) return; 5174 setCompanyEvents(toSignalStates(companyKeys, watchedSignalTypes)); 5175 setPeopleEvents(toSignalStates(peopleKeys, watchedSignalTypes)); 5176 setIsSaving(false); 5177 setHasError(false); 5178 }, [ 5179 isOpen, 5180 watchedSignalTypes, 5181 companyKeys, 5182 peopleKeys 5183 ]); 5184 const toggle = (setter, key) => { 5185 setter((current) => current.map((s) =>
5185 s.key === key ? { 5186 ...s, 5187 enabled: !s.enabled 5188 } : s)); 5189 }; 5190 const handleSave = async () => { 5191 const session = sessionRef.current; 5192 setIsSaving(true); 5193 setHasError(false); 5194 const controller = createAbortController(); 5195 const enabledKeys = new Set([...companyEvents, ...peopleEvents].filter((s_0) => s_0.enabled).map((s_1) => s_1.key)); 5196 const excluded = [...companyKeys, ...peopleKeys].filter((key_0) => !enabledKeys.has(key_0)); 5197 try { 5198 const response = await fetch(dashboard_lead_signal_preferences_path(), { 5199 method: "PATCH", 5200 headers: { 5201 "Content-Type": "application/json", 5202 "X-CSRF-Token": getCsrfToken() 5203 }, 5204 body: JSON.stringify({ excluded_lead_signal_types: excluded }), 5205 signal: controller.signal 5206 }); 5207 if (controller.signal.aborted || session !== sessionRef.current) return; 5208 if (response.redirected) { 5209 window.location.href = response.url; 5210 return; 5211 } 5212 if (!response.ok) { 5213 setHasError(true); 5214 setIsSaving(false); 5215 return; 5216 } 5217 const data = await response.json(); 5218 if (controller.signal.aborted || session !== sessionRef.current) return; 5219 setIsSaving(false); 5220 onSaved(data); 5221 } catch { 5222 if (controller.signal.aborted || session !== sessionRef.current) return; 5223 setHasError(true); 5224 setIsSaving(false); 5225 } 5226 }; 5227 if (!isOpen) return null; 5228 const [descriptionBefore, descriptionAfter] = i18n.t("js.dashboard.lead_signals_config.description", { link: LINK_PLACEHOLDER }).split(LINK_PLACEHOLDER); 5229 return /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Dialog, { 5230 ref: dialogRef, 5231 onClick: handleBackdropClick, 5232 onPointerDown: stopPropagation, 5233 onMouseDown: stopPropagation, 5234 className: "lead-signals-config-dialog", 5235 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(DialogContent, { children: [ 5236 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(DialogHeader, { 5237 variant: "plain", 5238 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(DialogTitle, { children: i18n.t("js.dashboard.lead_signals_config.title") }
5238), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(DialogCloseButton, { onClick: close })] 5239 }), 5240 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(DialogBody, { 5241 className: "lead-signals-config-dialog__body", 5242 children: [ 5243 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("p", { 5244 className: "lead-signals-config-dialog__description", 5245 children: [ 5246 descriptionBefore, 5247 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Button, { 5248 variant: "linkGrey", 5249 asChild: true, 5250 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("a", { 5251 href: edit_user_registration_path({ tab: "notifications" }), 5252 children: i18n.t("js.dashboard.lead_signals_config.account_settings_link") 5253 }) 5254 }), 5255 descriptionAfter 5256 ] 5257 }), 5258 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SignalSection, { 5259 title: i18n.t("js.dashboard.lead_signals_config.sections.company"), 5260 signals: companyEvents, 5261 onToggle: (key_1) => toggle(setCompanyEvents, key_1), 5262 tooltipContainer, 5263 showColumnHeader: true 5264 }), 5265 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(SignalSection, { 5266 title: i18n.t("js.dashboard.lead_signals_config.sections.people"), 5267 signals: peopleEvents, 5268 onToggle: (key_2) => toggle(setPeopleEvents, key_2), 5269 tooltipContainer 5270 }) 5271 ] 5272 }), 5273 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(DialogFooter, { 5274 className: "lead-signals-config-dialog__footer", 5275 children: [hasError && /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("p", { 5276 className: "lead-signals-config-dialog__error", 5277 role: "alert", 5278 children: i18n.t("js.dashboard.lead_signals_config.save_error") 5279 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Button, { 5280 variant: "primary", 5281 onClick: handleSave, 5282 disabled: isSaving, 5283 children: isSaving ? i18n.t("js.dashboard.lead_signals_config.saving") : i18n.t("js.dashboard.lead_signals_config.save") 5284 })] 5285 }) 5286 ] }) 5287 }); 5288} 5289function SignalSection({ title, signals, onToggle, tooltipContainer, showColumnHeader = false }) { 5290 return /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("fieldset", { 5291 className: "lead-signals-config-dialog__section", 5292 children: [ 5293 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("legend", { 5294 className: "sr-only", 5295 children: title 5296 }), 5297 /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("header", { 5298 className: "lead-signals-config-dialog__section-header", 5299 "aria-hidden": "true", 5300 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("span", { 5301 className: "lead-signals-config-dialog__section-title", 5302 children: title 5303 }), showColumnHeader && /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("span", { 5304 className: "lead-signals-config-dialog__column-header", 5305 children: i18n.t("js.dashboard.lead_signals_config.watch_column") 5306 })] 5307 }), 5308 /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("ul", { 5309 className: "lead-signals-config-dialog__list", 5310 children: signals.map((signal) => { 5311 const id = `lead-signals-config-${signal.key}`; 5312 const label = i18n.t(`js.dashboard.lead_signals_config.signals.${signal.key}.label`); 5313 const tooltip = i18n.t(`js.dashboard.lead_signals_config.signals.${signal.key}.tooltip`); 5314 return /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("li", { 5315 className: "lead-signals-config-dialog__row", 5316 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)("div", { 5317 className: "lead-signals-config-dialog__row-main", 5318 children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)("label", { 5319 htmlFor: id, 5320 className: "lead-signals-config-dialog__row-label", 5321 children: label 5322 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(TooltipTrigger, { 5323 asChild: true, 5324 children: /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Icon, { 5325 icon: "question-circle", 5326 size: "sm", 5327 className: "lead-signals-config-dialog__tooltip-trigger", 5328 tabIndex: 0, 5329 role: "img", 5330 "aria-hidden": false, 5331 "aria-label": i18n.t("js.dashboard.lead_signals_config.why_signal_matters", { label }) 5332 }) 5333 }
5333), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(TooltipContent, { 5334 className: "tooltip-md", 5335 container: tooltipContainer, 5336 children: tooltip 5337 })] }) })] 5338 }), /* @__PURE__ */ (0, import_jsx_runtime$7.jsx)(Checkbox, { 5339 id, 5340 checked: signal.enabled, 5341 onChange: () => onToggle(signal.key) 5342 })] 5343 }, signal.key); 5344 }) 5345 }) 5346 ] 5347 }); 5348} 5349var import_react$5, import_jsx_runtime$7, COMPANY_KEYS, PEOPLE_KEYS, LINK_PLACEHOLDER, toSignalStates, availableKeys; 5350function init_lead_signals_config_dialog() { 5351 return (init_lead_signals_config_dialog = __esmMin((() => { 5352 import_react$5 = /* @__PURE__ */ __toESM(require_react()); 5353 init_dialog(); 5354 init_button(); 5355 init_checkbox(); 5356 init_icon(); 5357 init_tooltip(); 5358 init_useDialog(); 5359 init_utils(); 5360 init_routes(); 5361 import_jsx_runtime$7 = require_jsx_runtime(); 5362 COMPANY_KEYS = [ 5363 "executive_added", 5364 "funding_added", 5365 "linkedin_job_added", 5366 "hiring_volume", 5367 "hiring_spike", 5368 "company_growth", 5369 "technology_added", 5370 "technology_deleted" 5371 ]; 5372 PEOPLE_KEYS = [ 5373 "email_added", 5374 "location_changed", 5375 "position_changed" 5376 ]; 5377 LINK_PLACEHOLDER = "__ACCOUNT_SETTINGS_LINK__"; 5378 toSignalStates = (keys, watched) => keys.map((key) => ({ 5379 key, 5380 enabled: watched.includes(key) 5381 })); 5382 availableKeys = (keys, available) => available ? keys.filter((key) => available.includes(key)) : keys; 5383 })))(); 5384} 5385//#endregion 5386//#region app/frontend/react/ui/callout-alert.tsx 5387var import_compiler_runtime$4, import_react$4, import_jsx_runtime$6, calloutAlertVariants, CalloutAlertIcon, CalloutAlertBody, CalloutAlertTitle, CalloutAlertDescription, CalloutAlertActions, CalloutAlertDismiss, CalloutAlert; 5388function init_callout_alert() { 5389 return (init_callout_alert = __esmMin((() => { 5390 import_compiler_runtime$4 = require_compiler_runtime(); 5391 import_react$4 = /* @__PURE__ */ __toESM(require_react()); 5392 init_dist(); 5393 init_utils(); 5394 init_icon(); 5395 import_jsx_runtime$6 = require_jsx_runtime(); 5396 calloutAlertVariants = cva("h-callout-alert", { 5397 variants: { variant: { dark: "h-callout-alert--dark" } }, 5398 defaultVariants: { variant: "dark" } 5399 }); 5400 CalloutAlertIcon = import_react$4.forwardRef((t0, ref) => { 5401 const $ = (0, import_compiler_runtime$4.c)(13); 5402 let className; 5403 let icon; 5404 let props; 5405 if ($[0] !== t0) { 5406 ({className, icon, ...props} = t0); 5407 $[0] = t0; 5408 $[1] = className; 5409 $[2] = icon; 5410 $[3] = props; 5411 } else { 5412 className = $[1]; 5413 icon = $[2]; 5414 props = $[3]; 5415 } 5416 if (icon === null) return null; 5417 let t1; 5418 if ($[4] !== className) { 5419 t1 = cn("h-callout-alert__icon-wrapper", className); 5420 $[4] = className; 5421 $[5] = t1; 5422 } else t1 = $[5]; 5423 const t2 = icon ?? "sparkle"; 5424 let t3; 5425 if ($[6] !== t2) { 5426 t3 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("span", { 5427 className: "h-callout-alert__icon", 5428 children: /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(Icon, { 5429 icon: t2, 5430 size: "sm" 5431 }) 5432 }); 5433 $[6] = t2; 5434 $[7] = t3; 5435 } else t3 = $[7]; 5436 let t4; 5437 if ($[8] !== props || $[9] !== ref || $[10] !== t1 || $[11] !== t3) { 5438 t4 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 5439 ref, 5440 className: t1, 5441 "data-slot": "icon", 5442 ...props, 5443 children: t3 5444 }); 5445 $[8] = props; 5446 $[9] = ref; 5447 $[10] = t1; 5448 $[11] = t3; 5449 $[12] = t4; 5450 } else t4 = $[12]; 5451 return t4; 5452 }); 5453 CalloutAlertIcon.displayName = "CalloutAlertIcon"; 5454 CalloutAlertBody = import_react$4.forwardRef((t0, ref) => { 5455 const $ = (0, import_compiler_runtime$4.c)(9); 5456 let className; 5457 let props; 5458 if ($[0] !== t0) { 5459 ({className, ...props} = t0); 5460 $[0] = t0; 5461 $[1] = className; 5462 $[2] = props; 5463 } else { 5464 className = $[1]; 5465 props = $[2]; 5466 } 5467 let t1; 5468 if ($[3] !== className) { 5469 t1 = cn("h-callout-alert__content", className); 5470 $[3] = className; 5471 $[4] = t1; 5472 } else t1 = $[4]; 5473 let t2; 5474 if ($[5] !== props || $[6] !== ref || $[7] !== t1) { 5475 t2 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 5476 ref, 5477 className: t1, 5478 "data-slot": "content", 5479 ...props 5480 }); 5481 $[5] = props; 5482 $[6] = ref; 5483 $[7] = t1; 5484 $[8] = t2; 5485 } else t2 = $[8]; 5486 return t2; 5487 }); 5488 CalloutAlertBody.displayName = "CalloutAlertBody"; 5489 CalloutAlertTitle = import_react$4.forwardRef((t0, ref) => { 5490 const $ = (0, import_compiler_runtime$4.c)(9); 5491 let className; 5492 let props; 5493 if ($[0] !== t0) { 5494 ({className, ...props} = t0); 5495 $[0] = t0; 5496 $[1] = className; 5497 $[2] = props; 5498 } else { 5499 className = $[1]; 5500 props = $[2]; 5501 } 5502 let t1; 5503 if ($[3] !== className) { 5504 t1 = cn("h-callout-alert__title", className); 5505 $[3] = className; 5506 $[4] = t1; 5507 } else t1 = $[4]; 5508 let t2; 5509 if ($[5] !== props || $[6] !== ref || $[7] !== t1) { 5510 t2 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 5511 ref, 5512 className: t1, 5513 "data-slot": "title", 5514 ...props 5515 }); 5516 $[5] = props; 5517 $[6] = ref; 5518 $[7] = t1; 5519 $[8] = t2; 5520 } else t2 = $[8]; 5521 return t2; 5522 }); 5523 CalloutAlertTitle.displayName = "CalloutAlertTitle"; 5524 CalloutAlertDescription = import_react$4.forwardRef((t0, ref) => { 5525 const $ = (0, import_compiler_runtime$4.c)(9); 5526 let className; 5527 let props; 5528 if ($[0] !== t0) { 5529 ({className, ...props} = t0); 5530 $[0] = t0; 5531 $[1] = className; 5532 $[2] = props; 5533 } else { 5534 className = $[1]; 5535 props = $[2]; 5536 } 5537 let t1; 5538 if ($[3] !== className) { 5539 t1 = cn("h-callout-alert__description", className); 5540 $[3] = className; 5541 $[4] = t1; 5542 } else t1 = $[4]; 5543 let t2; 5544 if ($[5] !== props || $[6] !== ref || $[7] !== t1) { 5545 t2 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 5546 ref, 5547 className: t1, 5548 "data-slot": "description", 5549 ...props 5550 }); 5551 $[5] = props; 5552 $[6] = ref; 5553 $[7] = t1; 5554 $[8] = t2; 5555 } else t2 = $[8]; 5556 return t2; 5557 }); 5558 CalloutAlertDescription.displayName = "CalloutAlertDescription"; 5559 CalloutAlertActions = import_react$4.forwardRef((t0, ref) => { 5560 const $ = (0, import_compiler_runtime$4.c)(9); 5561 let className; 5562 let props; 5563 if ($[0] !== t0) { 5564 ({className, ...props} = t0); 5565 $[0] = t0; 5566 $[1] = className; 5567 $[2] = props; 5568 } else { 5569 className = $[1]; 5570 props = $[2]; 5571 } 5572 let t1; 5573 if ($[3] !== className) { 5574 t1 = cn("h-callout-alert__actions", className); 5575 $[3] = className; 5576 $[4] = t1; 5577 } else t1 = $[4]; 5578 let t2; 5579 if ($[5] !== props || $[6] !== ref || $[7] !== t1) { 5580 t2 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("div", { 5581 ref, 5582 className: t1, 5583 "data-slot": "actions", 5584 ...props 5585 }); 5586 $[5] = props; 5587 $[6] = ref; 5588 $[7] = t1; 5589 $[8] = t2; 5590 } else t2 = $[8]; 5591 return t2; 5592 }); 5593 CalloutAlertActions.displayName = "CalloutAlertActions"; 5594 CalloutAlertDismiss = (t0) => { 5595 const $ = (0, import_compiler_runtime$4.c)(3); 5596 const { onDismiss } = t0; 5597 let t1; 5598 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 5599 t1 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(Icon, { 5600 icon: "times", 5601 size: "sm" 5602 }); 5603 $[0] = t1; 5604 } else t1 = $[0]; 5605 let t2; 5606 if ($[1] !== onDismiss) { 5607 t2 = /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("button", { 5608 type: "button", 5609 className: "h-callout-alert__dismiss", 5610 "aria-label": "Dismiss", 5611 onClick: onDismiss, 5612 "data-slot": "dismiss", 5613 children: t1 5614 }); 5615 $[1] = onDismiss; 5616 $[2] = t2; 5617 } else t2 = $[2]; 5618 return t2; 5619 }; 5620 CalloutAlertDismiss.displayName = "CalloutAlertDismiss"; 5621 CalloutAlert = import_react$4.forwardRef(({ className, variant = "dark", title, description, icon = "sparkle", actions, dismissable, onDismiss, showCaret = true, children, ...props }, ref) => { 5622 return /* @__PURE__ */ (0, import_jsx_runtime$6.jsxs)("div", { 5623 ref, 5624 role: "alert", 5625 "data-component": "CalloutAlert", 5626 "data-variant": variant ?? "dark", 5627 className: cn(calloutAlertVariants({ variant }
5627), dismissable && "h-callout-alert--dismissable", showCaret && "h-callout-alert--with-caret", className), 5628 ...props, 5629 children: [ 5630 /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(CalloutAlertIcon, { icon }), 5631 /* @__PURE__ */ (0, import_jsx_runtime$6.jsxs)(CalloutAlertBody, { children: [ 5632 title ? /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(CalloutAlertTitle, { children: title }) : null, 5633 description ? /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(CalloutAlertDescription, { children: description }) : null, 5634 children, 5635 actions ? /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(CalloutAlertActions, { children: actions }) : null 5636 ] }), 5637 dismissable ? /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)(CalloutAlertDismiss, { onDismiss }) : null, 5638 showCaret ? /* @__PURE__ */ (0, import_jsx_runtime$6.jsx)("span", { 5639 className: "h-callout-alert__caret", 5640 "aria-hidden": "true" 5641 }) : null 5642 ] 5643 }); 5644 }); 5645 CalloutAlert.displayName = "CalloutAlert"; 5646 })))(); 5647} 5648//#endregion 5649//#region app/frontend/react/components/dashboard-provider.tsx 5650var import_jsx_runtime$5, DashboardProvider; 5651function init_dashboard_provider() { 5652 return (init_dashboard_provider = __esmMin((() => { 5653 init_campaigns_context(); 5654 init_leads_lists_context(); 5655 init_company_lists_context(); 5656 import_jsx_runtime$5 = require_jsx_runtime(); 5657 DashboardProvider = ({ initialCampaigns, initialLeadsLists, initialFolders = [], initialCompanyLists, children }) => { 5658 return /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(CampaignsProvider, { 5659 initialCampaigns, 5660 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(LeadsListsProvider, { 5661 initialLeadsLists, 5662 initialFolders, 5663 children: /* @__PURE__ */ (0, import_jsx_runtime$5.jsx)(CompanyListsProvider, { 5664 initialCompanyLists, 5665 children 5666 }) 5667 }) 5668 }); 5669 }; 5670 })))(); 5671} 5672//#endregion 5673//#region app/frontend/react/components/dashboard-health.tsx 5674function DashboardHealth() { 5675 const $ = (0, import_compiler_runtime$3.c)(20); 5676 const [data, setData] = (0, import_react$3.useState)(null); 5677 const [loading, setLoading] = (0, import_react$3.useState)(true); 5678 let t0; 5679 let t1; 5680 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 5681 t0 = () => { 5682 let cancelled = false; 5683 fetch(email_domains_health_reports_path({ format: "json" }), { 5684 headers: { Accept: "application/json" }, 5685 credentials: "same-origin" 5686 }).then(_temp).then((payload) => { 5687 if (cancelled) return; 5688 setData(payload); 5689 }).catch(_temp2).finally(() => { 5690 if (!cancelled) setLoading(false); 5691 }); 5692 return () => { 5693 cancelled = true; 5694 }; 5695 }; 5696 t1 = []; 5697 $[0] = t0; 5698 $[1] = t1; 5699 } else { 5700 t0 = $[0]; 5701 t1 = $[1]; 5702 } 5703 (0, import_react$3.useEffect)(t0, t1); 5704 const useNoScoreYetCopy = !(data?.has_accounts === false); 5705 let t2; 5706 let t3; 5707 if ($[2] === Symbol.for("react.memo_cache_sentinel")) { 5708 t2 = i18n.t("js.dashboard.email_accounts_health.title"); 5709 t3 = i18n.t("js.dashboard.email_accounts_health.tooltip"); 5710 $[2] = t2; 5711 $[3] = t3; 5712 } else { 5713 t2 = $[2]; 5714 t3 = $[3]; 5715 } 5716 let t4; 5717 if ($[4] !== useNoScoreYetCopy) { 5718 t4 = i18n.t(useNoScoreYetCopy ? "js.dashboard.email_accounts_health.no_score_yet.title" : "js.dashboard.email_accounts_health.empty_state.title"); 5719 $[4] = useNoScoreYetCopy; 5720 $[5] = t4; 5721 } else t4 = $[5]; 5722 let t5; 5723 if ($[6] !== useNoScoreYetCopy) { 5724 t5 = i18n.t(useNoScoreYetCopy ? "js.dashboard.email_accounts_health.no_score_yet.description" : "js.dashboard.email_accounts_health.empty_state.description"); 5725 $[6] = useNoScoreYetCopy; 5726 $[7] = t5; 5727 } else t5 = $[7]; 5728 let t6; 5729 if ($[8] === Symbol.for("react.memo_cache_sentinel")) { 5730 t6 = sequences_email_accounts_center_path(); 5731 $[8] = t6; 5732 } else t6 = $[8]; 5733 let t7; 5734 if ($[9] !== useNoScoreYetCopy) { 5735 t7 = i18n.t(useNoScoreYetCopy ? "js.dashboard.email_accounts_health.no_score_yet.cta" : "js.dashboard.email_accounts_health.empty_state.cta"); 5736 $[9] = useNoScoreYetCopy; 5737 $[10] = t7; 5738 } else t7 = $[10]; 5739 let t8; 5740 if ($[11] !== t7) { 5741 t8 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(Button, { 5742 asChild: true, 5743 size: "sm", 5744 fullWidth: true, 5745 children: /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("a", { 5746 href: t6, 5747 children: t7 5748 }) 5749 }); 5750 $[11] = t7; 5751 $[12] = t8; 5752 } else t8 = $[12]; 5753 let t9; 5754 if ($[13] === Symbol.for("react.memo_cache_sentinel")) { 5755 t9 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(Button, { 5756 asChild: true, 5757 variant: "default", 5758 size: "sm", 5759 fullWidth: true, 5760 children: /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)("a", { 5761 href: sequences_email_accounts_center_path(), 5762 children: i18n.t("js.dashboard.email_accounts_health.show_details") 5763 }) 5764 }); 5765 $[13] = t9; 5766 } else t9 = $[13]; 5767 let t10; 5768 if ($[14] !== data || $[15] !== loading || $[16] !== t4 || $[17] !== t5 || $[18] !== t8) { 5769 t10 = /* @__PURE__ */ (0, import_jsx_runtime$4.jsx)(GlobalHealthWidget, { 5770 data, 5771 loading, 5772 title: t2, 5773 tooltip: t3, 5774 emptyStateTitle: t4, 5775 emptyStateDescription: t5,
5776 emptyStateAction: t8, 5777 action: t9 5778 }); 5779 $[14] = data; 5780 $[15] = loading; 5781 $[16] = t4; 5782 $[17] = t5; 5783 $[18] = t8; 5784 $[19] = t10; 5785 } else t10 = $[19]; 5786 return t10; 5787} 5788function _temp2(error) { 5789 console.error("Failed to fetch dashboard health:", error); 5790} 5791function _temp(res) { 5792 if (!res.ok) throw new Error(`HTTP ${res.status}`); 5793 return res.json(); 5794} 5795var import_compiler_runtime$3, import_react$3, import_jsx_runtime$4; 5796function init_dashboard_health() { 5797 return (init_dashboard_health = __esmMin((() => { 5798 import_compiler_runtime$3 = require_compiler_runtime(); 5799 import_react$3 = require_react(); 5800 init_global_health_widget(); 5801 init_button(); 5802 init_routes(); 5803 import_jsx_runtime$4 = require_jsx_runtime(); 5804 })))(); 5805} 5806//#endregion 5807//#region app/frontend/react/components/dashboard-content-block.tsx 5808function DashboardContentBlock(t0) { 5809 const $ = (0, import_compiler_runtime$2.c)(24); 5810 const { tag, tagVariant, title, description, ctaLabel, ctaHref, onCtaClick, onDismiss, dismissAriaLabel, className } = t0; 5811 const hasCta = ctaLabel && (ctaHref || onCtaClick); 5812 let t1; 5813 if ($[0] !== className) { 5814 t1 = cn("dashboard-content-block", className); 5815 $[0] = className; 5816 $[1] = t1; 5817 } else t1 = $[1]; 5818 let t2; 5819 if ($[2] !== dismissAriaLabel || $[3] !== onDismiss || $[4] !== tag || $[5] !== tagVariant) { 5820 t2 = (tag || onDismiss) && /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(CardTitle, { 5821 className: "dashboard-content-block__header", 5822 children: [tag && /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Tag, { 5823 variant: tagVariant, 5824 size: "sm", 5825 label: tag 5826 }
5826), onDismiss && /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(CardTitleActions, { children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Button, { 5827 variant: "ghost", 5828 size: "xs", 5829 iconOnly: true, 5830 onClick: onDismiss, 5831 "aria-label": dismissAriaLabel ?? "Dismiss", 5832 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Icon, { 5833 icon: "xmark", 5834 "aria-hidden": "true", 5835 size: "sm" 5836 }) 5837 }) })] 5838 }); 5839 $[2] = dismissAriaLabel; 5840 $[3] = onDismiss; 5841 $[4] = tag; 5842 $[5] = tagVariant; 5843 $[6] = t2; 5844 } else t2 = $[6]; 5845 let t3; 5846 if ($[7] !== title) { 5847 t3 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("h3", { 5848 className: "dashboard-content-block__title", 5849 children: title 5850 }); 5851 $[7] = title; 5852 $[8] = t3; 5853 } else t3 = $[8]; 5854 let t4; 5855 if ($[9] !== description) { 5856 t4 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("p", { 5857 className: "dashboard-content-block__description", 5858 children: description 5859 }); 5860 $[9] = description; 5861 $[10] = t4; 5862 } else t4 = $[10]; 5863 let t5; 5864 if ($[11] !== ctaHref || $[12] !== ctaLabel || $[13] !== hasCta || $[14] !== onCtaClick) { 5865 t5 = hasCta && (ctaHref ? /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Button, { 5866 asChild: true, 5867 fullWidth: true, 5868 size: "sm", 5869 className: "dashboard-content-block__cta", 5870 children: /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)("a", { 5871 href: ctaHref, 5872 children: ctaLabel 5873 }) 5874 }) : /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Button, { 5875 onClick: onCtaClick, 5876 fullWidth: true, 5877 size: "sm", 5878 className: "dashboard-content-block__cta", 5879 children: ctaLabel 5880 })); 5881 $[11] = ctaHref; 5882 $[12] = ctaLabel; 5883 $[13] = hasCta; 5884 $[14] = onCtaClick; 5885 $[15] = t5; 5886 } else t5 = $[15]; 5887 let t6; 5888 if ($[16] !== t2 || $[17] !== t3 || $[18] !== t4 || $[19] !== t5) { 5889 t6 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsxs)(CardContent, { children: [ 5890 t2, 5891 t3, 5892 t4, 5893 t5 5894 ] }); 5895 $[16] = t2; 5896 $[17] = t3; 5897 $[18] = t4; 5898 $[19] = t5; 5899 $[20] = t6; 5900 } else t6 = $[20]; 5901 let t7; 5902 if ($[21] !== t1 || $[22] !== t6) { 5903 t7 = /* @__PURE__ */ (0, import_jsx_runtime$3.jsx)(Card, { 5904 className: t1, 5905 children: t6 5906 }); 5907 $[21] = t1; 5908 $[22] = t6; 5909 $[23] = t7; 5910 } else t7 = $[23]; 5911 return t7; 5912} 5913var import_compiler_runtime$2, import_jsx_runtime$3; 5914function init_dashboard_content_block() { 5915 return (init_dashboard_content_block = __esmMin((() => { 5916 import_compiler_runtime$2 = require_compiler_runtime(); 5917 init_card(); 5918 init_tag(); 5919 init_button(); 5920 init_icon(); 5921 init_utils(); 5922 import_jsx_runtime$3 = require_jsx_runtime(); 5923 })))(); 5924} 5925//#endregion 5926//#region app/frontend/react/components/dashboard-announcement.tsx 5927function DashboardAnnouncement({ maxCount = 1 }) { 5928 const [announcements, setAnnouncements] = (0, import_react$2.useState)([]); 5929 const [dismissedIds, setDismissedIds] = (0, import_react$2.useState)(() => readDismissedIds()); 5930 (0, import_react$2.useEffect)(() => { 5931 let cancelled = false; 5932 fetch(dashboard_announcement_path({ 5933 format: "json", 5934 limit: maxCount 5935 }), { 5936 headers: { Accept: "application/json" }, 5937 credentials: "same-origin" 5938 }).then((res) => { 5939 if (!res.ok) throw new Error(`HTTP ${res.status}`); 5940 return res.json(); 5941 }).then((data) => { 5942 if (cancelled || !data) return; 5943 setAnnouncements(data); 5944 }).catch((error) => { 5945 console.error("Failed to fetch dashboard announcement:", error); 5946 }); 5947 return () => { 5948 cancelled = true; 5949 }; 5950 }, [maxCount]); 5951 const handleDismiss = (0, import_react$2.useCallback)((id) => { 5952 const ids = readDismissedIds(); 5953 if (!ids.includes(id)) writeDismissedIds([...ids, id]); 5954 setDismissedIds((previous) => previous.includes(id) ? previous : [...previous, id]); 5955 }, []); 5956 const visible = announcements.filter((announcement) => !dismissedIds.includes(announcement.id)).slice(0, maxCount); 5957 if (visible.length === 0) return null; 5958 return /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(import_jsx_runtime$2.Fragment, { children: visible.map((announcement_0) => { 5959 const description = announcement_0.webinar_time_at ? `${formatWebinarTime(announcement_0.webinar_time_at)}. ${announcement_0.description}` : announcement_0.description; 5960 return /* @__PURE__ */ (0, import_jsx_runtime$2.jsx)(DashboardContentBlock, { 5961 tag: getCategoryLabel(announcement_0.category), 5962 title: announcement_0.title, 5963 description, 5964 ctaLabel: announcement_0.cta_label ?? void 0, 5965 ctaHref: announcement_0.cta_url ?? void 0, 5966 onDismiss: () => handleDismiss(announcement_0.id), 5967 dismissAriaLabel: i18n.t("js.dashboard.dashboard_announcement.dismiss") 5968 }, announcement_0.id); 5969 }) }); 5970} 5971var import_react$2, import_jsx_runtime$2, DISMISSED_STORAGE_KEY, readDismissedIds, writeDismissedIds, isSameDay, formatWebinarTime, getCategoryLabel; 5972function init_dashboard_announcement() { 5973 return (init_dashboard_announcement = __esmMin((() => { 5974 import_react$2 = require_react(); 5975 init_dashboard_content_block(); 5976 init_routes(); 5977 import_jsx_runtime$2 = require_jsx_runtime(); 5978 DISMISSED_STORAGE_KEY = "hunter:dashboard:dismissed-announcements"; 5979 readDismissedIds = () => { 5980 try { 5981 const raw = window.localStorage.getItem(DISMISSED_STORAGE_KEY); 5982 if (!raw) return []; 5983 const parsed = JSON.parse(raw); 5984 return Array.isArray(parsed) ? parsed.filter((id) => isNumber(id)) : []; 5985 } catch { 5986 return []; 5987 } 5988 }; 5989 writeDismissedIds = (ids) => { 5990 try { 5991 window.localStorage.setItem(DISMISSED_STORAGE_KEY, JSON.stringify(ids)); 5992 } catch {} 5993 }; 5994 isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); 5995 formatWebinarTime = (iso) => { 5996 const date = new Date(iso); 5997 const now = /* @__PURE__ */ new Date(); 5998 const tomorrow = new Date(now); 5999 tomorrow.setDate(now.getDate() + 1); 6000 const time = new Intl.DateTimeFormat(void 0, { 6001 hour: "numeric", 6002 minute: "2-digit" 6003 }).format(date); 6004 if (isSameDay(date, now)) return `Today at ${time}`; 6005 if (isSameDay(date, tomorrow)) return `Tomorrow at ${time}`;
6006 const daysAhead = Math.floor((date.getTime() - now.getTime()) / 864e5); 6007 if (daysAhead >= 0 && daysAhead < 7) return `${new Intl.DateTimeFormat(void 0, { weekday: "long" }).format(date)} at ${time}`; 6008 return `${new Intl.DateTimeFormat(void 0, { 6009 month: "short", 6010 day: "numeric" 6011 }).format(date)} at ${time}`; 6012 }; 6013 getCategoryLabel = (category) => { 6014 return i18n.t(`js.dashboard.dashboard_announcement.categories.${category}`); 6015 }; 6016 })))(); 6017} 6018//#endregion 6019//#region app/frontend/react/helpers/format-opportunity-signals-updated-at.ts 6020function formatOpportunitySignalsUpdatedAt(dateString, now = /* @__PURE__ */ new Date()) { 6021 const date = new Date(dateString); 6022 if (Number.isNaN(date.getTime())) return ""; 6023 const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); 6024 const days = Math.round((startOfDay(now) - startOfDay(date)) / 864e5); 6025 if (days === 0) return "Today"; 6026 if (days === 1) return "Yesterday"; 6027 if (days > 1 && days < 7) return date.toLocaleDateString([], { weekday: "long" }); 6028 const options = { 6029 month: "short", 6030 day: "numeric" 6031 }; 6032 if (date.getFullYear() !== now.getFullYear()) options.year = "numeric"; 6033 return date.toLocaleDateString([], options); 6034} 6035//#endregion 6036//#region app/frontend/react/helpers/post-team-activities-seen.ts 6037function postTeamActivitiesSeen(activities) { 6038 for (let offset = 0; offset < activities.length; offset += IDS_PER_REQUEST) fetch(dashboard_seen_lead_signals_path(), { 6039 method: "POST", 6040 headers: { 6041 "Content-Type": "application/json", 6042 "X-CSRF-Token": getCsrfToken() 6043 }, 6044 body: JSON.stringify({ activities: activities.slice(offset, offset + IDS_PER_REQUEST).map(teamActivityReference) }) 6045 }).catch(() => void 0); 6046} 6047var IDS_PER_REQUEST; 6048function init_post_team_activities_seen() { 6049 return (init_post_team_activities_seen = __esmMin((() => { 6050 init_routes(); 6051 init_utils(); 6052 IDS_PER_REQUEST = 1e3; 6053 })))(); 6054} 6055//#endregion 6056//#region app/frontend/react/components/dashboard.tsx 6057function readCalloutDismissed(identity) { 6058 if (!identity) return false; 6059 try { 6060 return sessionStorage.getItem(CALLOUT_DISMISSED_KEY) === identity; 6061 } catch { 6062 return false; 6063 } 6064} 6065function writeCalloutDismissed(identity) { 6066 if (!identity) return; 6067 try { 6068 sessionStorage.setItem(CALLOUT_DISMISSED_KEY, identity); 6069 } catch {} 6070} 6071async function requestLeadSignalsPage(signal, cursor) { 6072 try { 6073 const response = await fetch(cursor ? dashboard_lead_signals_path({ cursor }) : dashboard_lead_signals_path(), { 6074 method: "GET", 6075 headers: { Accept: "application/json" }, 6076 credentials: "same-origin", 6077 signal 6078 }); 6079 if (response.redirected) return { 6080 status: "redirect", 6081 url: response.url 6082 }; 6083 if (!response.ok) return { status: "error" }; 6084 const page = await response.json(); 6085 return isLeadSignalsPage(page) ? { 6086 status: "success", 6087 page 6088 } : { status: "error" }; 6089 } catch { 6090 return { status: "error" }; 6091 } 6092} 6093function OpportunitySignalsSkeleton({ rows = 3 }) { 6094 return /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("div", { 6095 className: "dashboard-v2__opportunity-skeleton", 6096 "aria-hidden": "true", 6097 children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_react$1.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6098 className: "dashboard-v2__opportunity-skeleton-item", 6099 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6100 className: "dashboard-v2__opportunity-skeleton-content", 6101 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6102 className: "dashboard-v2__opportunity-skeleton-header", 6103 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6104 size: "freeform", 6105 className: "dashboard-v2__skeleton-bar--title" 6106 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6107 size: "freeform", 6108 className: "dashboard-v2__skeleton-bar--tag" 6109 })] 6110 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6111 className: "dashboard-v2__opportunity-skeleton-description", 6112 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6113 size: "freeform", 6114 className: "dashboard-v2__skeleton-bar--line" 6115 }
6115), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6116 size: "freeform", 6117 className: "dashboard-v2__skeleton-bar--line-short" 6118 })] 6119 })] 6120 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6121 className: "dashboard-v2__opportunity-skeleton-footer", 6122 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6123 className: "dashboard-v2__opportunity-skeleton-badges", 6124 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6125 size: "freeform", 6126 className: "dashboard-v2__skeleton-bar--badge" 6127 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6128 size: "freeform", 6129 className: "dashboard-v2__skeleton-bar--badge-sm" 6130 })] 6131 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6132 size: "freeform", 6133 className: "dashboard-v2__skeleton-bar--button" 6134 })] 6135 })] 6136 }), index < rows - 1 && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" })] }, index)) 6137 }); 6138} 6139function LeadSignalsSkeleton({ rows = 5 }) { 6140 return /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("div", { 6141 className: "dashboard-v2__lead-signal-skeleton", 6142 "aria-hidden": "true", 6143 children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_react$1.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6144 className: "dashboard-v2__lead-signal-skeleton-item", 6145 children: [
6146 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6147 size: "freeform", 6148 className: "dashboard-v2__skeleton-bar--avatar" 6149 }), 6150 /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6151 className: "dashboard-v2__lead-signal-skeleton-main", 6152 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6153 className: "dashboard-v2__lead-signal-skeleton-message", 6154 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6155 size: "freeform", 6156 className: "dashboard-v2__skeleton-bar--line" 6157 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6158 size: "freeform", 6159 className: "dashboard-v2__skeleton-bar--signal-line-short" 6160 })] 6161 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6162 size: "freeform", 6163 className: "dashboard-v2__skeleton-bar--signal-time" 6164 })] 6165 }), 6166 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Skeleton, { 6167 size: "freeform", 6168 className: "dashboard-v2__skeleton-bar--dot" 6169 }) 6170 ] 6171 }), index < rows - 1 && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" })] }, index)) 6172 }); 6173} 6174function SectionUnavailable() { 6175 const $ = (0, import_compiler_runtime$1.c)(2); 6176 let t0; 6177 if ($[0] === Symbol.for("react.memo_cache_sentinel")) { 6178 t0 = /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateTitle, { children: i18n.t("js.dashboard.unavailable.title") }); 6179 $[0] = t0; 6180 } else t0 = $[0]; 6181 let t1; 6182 if ($[1] === Symbol.for("react.memo_cache_sentinel")) { 6183 t1 = /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(EmptyState, { 6184 size: "small", 6185 fill: true, 6186 children: [t0, /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateDescription, { children: i18n.t("js.dashboard.unavailable.description") })] 6187 }); 6188 $[1] = t1; 6189 } else t1 = $[1]; 6190 return t1; 6191} 6192function Dashboard({ user, numberOfCreditsLeft, isPremium = false, campaigns = [], has_syncable_apps = false, leads_lists = [], folders = [], company_lists = [], leads_count = 0, leads_without_email = 0, verified_leads = 0, unread_inbox_messages = 0, team_activities, grouped_lead_signals = false, lead_signals_next_cursor = null, team_activities_degraded = false, watched_lead_signal_types = [], available_lead_signal_types, user_lookalikes, user_lookalikes_degraded = false, opportunity_signals_updated_at = null, show_opportunities_callout = false, saved_searches_with_new_results, saved_searches_degraded = false, account_established = false, celebration_pending = false, opportunities_threshold_reached = false, unread_opportunity_cta_variant = null, custom_attributes = [], form_options }) { 6193 const groupedFeed = useGroupedLeadSignals(grouped_lead_signals); 6194 const opportunitiesPending = user_lookalikes === void 0 || saved_searches_with_new_results === void 0; 6195 const unreadCtaPrimary = unread_opportunity_cta_variant === "test"; 6196 const unreadCtaVariant = unread_opportunity_cta_variant ?? "unassigned"; 6197 const opportunitiesAnyDegraded = user_lookalikes_degraded || saved_searches_degraded; 6198 const lookalikes = import_react$1.useMemo(() => user_lookalikes ?? [], [user_lookalikes]); 6199 const savedSearches = import_react$1.useMemo(() => saved_searches_with_new_results ?? [], [saved_searches_with_new_results]); 6200 const [onboardingDismissed, setOnboardingDismissed] = import_react$1.useState(false); 6201 const hasLatchedFirstSignalRef = import_react$1.useRef(false); 6202 const calloutIdentity = user?.email ?? ""; 6203 const [showOpportunitiesCallout, setShowOpportunitiesCallout] = import_react$1.useState(() => show_opportunities_callout && !readCalloutDismissed(calloutIdentity)); 6204 const requestedOpportunitiesCalloutSeen = import_react$1.useRef(false); 6205 const [openSignalId, setOpenSignalId] = import_react$1.useState(null); 6206 const [openOpportunityId, setOpenOpportunityId] = import_react$1.useState(null); 6207 const [leadPanelLead, setLeadPanelLead] = import_react$1.useState(null); 6208 const [leadPanelOpen, setLeadPanelOpen] = import_react$1.useState(false); 6209 const leadPanelRequestRef = import_react$1.useRef(null);
6210 const [companyPanel, setCompanyPanel] = import_react$1.useState(null); 6211 const [isLeadSignalsConfigOpen, setIsLeadSignalsConfigOpen] = import_react$1.useState(false); 6212 const [seenActivityIds, setSeenActivityIds] = import_react$1.useState(/* @__PURE__ */ new Set()); 6213 const [seenOpportunityIds, setSeenOpportunityIds] = import_react$1.useState(/* @__PURE__ */ new Set()); 6214 const requestedOpportunitySeenIds = import_react$1.useRef(/* @__PURE__ */ new Set()); 6215 const [teamActivities, setTeamActivitiesState] = import_react$1.useState(team_activities); 6216 const teamActivitiesRef = import_react$1.useRef(team_activities); 6217 const replaceTeamActivities = import_react$1.useCallback((activities) => { 6218 teamActivitiesRef.current = activities; 6219 setTeamActivitiesState(activities); 6220 }, []); 6221 const updateTeamActivities = import_react$1.useCallback((updater) => { 6222 replaceTeamActivities(updater(teamActivitiesRef.current)); 6223 }, [replaceTeamActivities]); 6224 const [legacyLeadSignalsNextCursor, setLeadSignalsNextCursor] = import_react$1.useState(lead_signals_next_cursor); 6225 const [visibleLeadSignalsCount, setVisibleLeadSignalsCount] = import_react$1.useState(LEAD_SIGNALS_BATCH_SIZE); 6226 const [legacyLeadSignalsPaginationState, setLeadSignalsPaginationState] = import_react$1.useState("idle"); 6227 const activeLeadSignalsRequest = import_react$1.useRef(null); 6228 const leadSignalsRequestSession = import_react$1.useRef(0); 6229 const leadSignalsBoundaryRef = import_react$1.useRef(null); 6230 const leadSignalsBoundaryInView = useIsInView(leadSignalsBoundaryRef); 6231 const leadSignalsBoundaryHandled = import_react$1.useRef(false); 6232 const [legacySignalsDegraded, setSignalsDegraded] = import_react$1.useState(team_activities_degraded); 6233 const leadSignalsNextCursor = grouped_lead_signals ? groupedFeed.cursor : legacyLeadSignalsNextCursor; 6234 const leadSignalsPaginationState = grouped_lead_signals ? groupedFeed.state : legacyLeadSignalsPaginationState; 6235 const signalsDegraded = grouped_lead_signals ? groupedFeed.state === "error" && !groupedFeed.signals?.length : legacySignalsDegraded; 6236 const deferredTeamActivitiesInvalidated = import_react$1.useRef(false); 6237 import_react$1.useEffect(() => { 6238 if (grouped_lead_signals) return; 6239 if (team_activities === void 0) return; 6240 if (deferredTeamActivitiesInvalidated.current) return; 6241 activeLeadSignalsRequest.current?.abort(); 6242 activeLeadSignalsRequest.current = null; 6243 leadSignalsRequestSession.current += 1; 6244 replaceTeamActivities(team_activities); 6245 setLeadSignalsNextCursor(lead_signals_next_cursor); 6246 setVisibleLeadSignalsCount(LEAD_SIGNALS_BATCH_SIZE); 6247 setLeadSignalsPaginationState("idle"); 6248 leadSignalsBoundaryHandled.current = false; 6249 setSignalsDegraded(team_activities_degraded); 6250 }, [ 6251 team_activities, 6252 team_activities_degraded, 6253 lead_signals_next_cursor, 6254 replaceTeamActivities, 6255 grouped_lead_signals 6256 ]); 6257 const [watchedSignalTypes, setWatchedSignalTypes] = import_react$1.useState(watched_lead_signal_types); 6258 const userName = user?.firstName || user?.email || "there"; 6259 const signalsPending = grouped_lead_signals ? groupedFeed.signals === void 0 : teamActivities === void 0; 6260 const aggregatedLeadSignals = import_react$1.useMemo(() => grouped_lead_signals ? groupedFeed.signals ?? [] : teamActivitiesToLeadSignals(teamActivities ?? []), [ 6261 grouped_lead_signals, 6262 groupedFeed.signals, 6263 teamActivities 6264 ]); 6265 const completeLeadSignals = import_react$1.useMemo(() => grouped_lead_signals ? aggregatedLeadSignals : completeLeadSignalsForPage(aggregatedLeadSignals, leadSignalsNextCursor), [ 6266 aggregatedLeadSignals, 6267 leadSignalsNextCursor, 6268 grouped_lead_signals 6269 ]); 6270 const leadSignals = completeLeadSignals.slice(0, visibleLeadSignalsCount); 6271 const openSignal = openSignalId !== null ? aggregatedLeadSignals.find((signal) => signal.id === openSignalId) ?? null : null; 6272 const canLoadMoreLeadSignals = completeLeadSignals.length > visibleLeadSignalsCount || leadSignalsNextCursor !== null; 6273 const loadMoreLeadSignals = import_react$1.useCallback(async () => { 6274 if (completeLeadSignals.length > visibleLeadSignalsCount) { 6275 setVisibleLeadSignalsCount((count) => Math.min(count + LEAD_SIGNALS_BATCH_SIZE, completeLeadSignals.length)); 6276 setLeadSignalsPaginationState("idle"); 6277 return; 6278 } 6279 if (grouped_lead_signals) { 6280 await groupedFeed.loadMore(); 6281 setVisibleLeadSignalsCount((count_0) => count_0 + LEAD_SIGNALS_BATCH_SIZE); 6282 return; 6283 } 6284 if (!leadSignalsNextCursor || activeLeadSignalsRequest.current) return; 6285 const controller = new AbortController(); 6286 const session = leadSignalsRequestSession.current; 6287 activeLeadSignalsRequest.current = controller; 6288 setLeadSignalsPaginationState("loading");
6289 const result = await requestLeadSignalsPage(controller.signal, leadSignalsNextCursor); 6290 if (controller.signal.aborted || session !== leadSignalsRequestSession.current) return; 6291 if (activeLeadSignalsRequest.current === controller) activeLeadSignalsRequest.current = null; 6292 if (result.status === "redirect") { 6293 window.location.href = result.url; 6294 return; 6295 } 6296 if (result.status === "error") { 6297 setLeadSignalsPaginationState("error"); 6298 return; 6299 } 6300 const { page } = result; 6301 const activitiesById = new Map((teamActivitiesRef.current ?? []).map((activity) => [teamActivityKey(activity), activity])); 6302 page.team_activities.forEach((activity_0) => activitiesById.set(teamActivityKey(activity_0), activity_0)); 6303 const mergedActivities = [...activitiesById.values()]; 6304 const mergedSignals = teamActivitiesToLeadSignals(mergedActivities); 6305 const completeCount = completeLeadSignalsForPage(mergedSignals, page.next_cursor).length; 6306 const exposedNewBuckets = completeCount > completeLeadSignals.length; 6307 replaceTeamActivities(mergedActivities); 6308 setLeadSignalsNextCursor(page.next_cursor); 6309 setLeadSignalsPaginationState("idle"); 6310 if (completeCount > visibleLeadSignalsCount) setVisibleLeadSignalsCount((count_1) => Math.min(count_1 + LEAD_SIGNALS_BATCH_SIZE, completeCount)); 6311 if (page.next_cursor && !exposedNewBuckets) leadSignalsBoundaryHandled.current = false; 6312 }, [ 6313 completeLeadSignals.length, 6314 leadSignalsNextCursor, 6315 replaceTeamActivities, 6316 visibleLeadSignalsCount, 6317 grouped_lead_signals, 6318 groupedFeed.loadMore 6319 ]); 6320 import_react$1.useEffect(() => { 6321 if (!leadSignalsBoundaryInView) { 6322 leadSignalsBoundaryHandled.current = false; 6323 return; 6324 } 6325 if (leadSignalsBoundaryHandled.current || !canLoadMoreLeadSignals || leadSignalsPaginationState === "error") return; 6326 leadSignalsBoundaryHandled.current = true; 6327 loadMoreLeadSignals(); 6328 }, [ 6329 canLoadMoreLeadSignals, 6330 leadSignalsBoundaryInView, 6331 leadSignalsPaginationState, 6332 loadMoreLeadSignals 6333 ]); 6334 import_react$1.useEffect(() => () => { 6335 leadSignalsRequestSession.current += 1; 6336 activeLeadSignalsRequest.current?.abort(); 6337 activeLeadSignalsRequest.current = null; 6338 }, []); 6339 const isSignalVisited = import_react$1.useCallback((signal_0) => signal_0.visited || (signal_0.groupToken ? seenActivityIds.has(`${signal_0.id}|${signal_0.membershipVersion}`) : signal_0.activities.every((activity_1) => seenActivityIds.has(teamActivityKey(activity_1)))), [seenActivityIds]); 6340 const markSignalSeen = (signal_1, deferUntilDetails = false) => { 6341 if (isSignalVisited(signal_1)) return; 6342 if (signal_1.groupToken) { 6343 setSeenActivityIds((previous) => new Set(previous).add(`${signal_1.id}|${signal_1.membershipVersion}`)); 6344 if (!deferUntilDetails) markGroupedSignalSeen(signal_1.groupToken).catch(() => {}); 6345 return; 6346 } 6347 const teamActivityIds = signal_1.activities.map(teamActivityKey); 6348 if (teamActivityIds.length === 0) return; 6349 setSeenActivityIds((previous_0) => { 6350 const next = new Set(previous_0); 6351 teamActivityIds.forEach((id) => next.add(id)); 6352 return next; 6353 }); 6354 postTeamActivitiesSeen(signal_1.activities); 6355 }; 6356 const openLeadPanel = import_react$1.useCallback(async (leadId) => { 6357 leadPanelRequestRef.current = leadId; 6358 setLeadPanelLead(null); 6359 setLeadPanelOpen(false); 6360 try { 6361 const response = await fetch(lead_path(leadId, { format: "json" }), { headers: { 6362 Accept: "application/json", 6363 "X-CSRF-Token": getCsrfToken() 6364 } }); 6365 if (!response.ok) { 6366 if (leadPanelRequestRef.current === leadId) leadPanelRequestRef.current = null; 6367 return; 6368 } 6369 const fetchedLead = await response.json(); 6370 if (leadPanelRequestRef.current !== leadId) return; 6371 setLeadPanelLead(fetchedLead); 6372 setLeadPanelOpen(true); 6373 } catch { 6374 if (leadPanelRequestRef.current === leadId) leadPanelRequestRef.current = null; 6375 } 6376 }, []); 6377 const isOpportunityVisited = import_react$1.useCallback((lookalike) => lookalike.seen_at != null || seenOpportunityIds.has(lookalike.id), [seenOpportunityIds]); 6378 const markOpportunitySeen = import_react$1.useCallback((lookalikeId) => { 6379 const lookalike_0 = lookalikes.find((item) => item.id === lookalikeId); 6380 if (!lookalike_0 || lookalike_0.seen_at != null) return; 6381 if (requestedOpportunitySeenIds.current.has(lookalikeId)) return; 6382 requestedOpportunitySeenIds.current.add(lookalikeId); 6383 setSeenOpportunityIds((previous_1) => new Set(previous_1).add(lookalikeId)); 6384 fetch(dashboard_seen_opportunities_path(), { 6385 method: "POST", 6386 headers: { 6387 "Content-Type": "application/json", 6388 "X-CSRF-Token": getCsrfToken() 6389 }, 6390 body: JSON.stringify({ user_lookalike_ids: [lookalikeId] }) 6391 }).catch(() => {}); 6392 }, [lookalikes]); 6393 const handleDismissOpportunitiesCallout = import_react$1.useCallback(() => { 6394 setShowOpportunitiesCallout(false); 6395 writeCalloutDismissed(calloutIdentity); 6396 if (requestedOpportunitiesCalloutSeen.current) return; 6397 requestedOpportunitiesCalloutSeen.current = true; 6398 fetch(onboarding_steps_path(), { 6399 method: "POST", 6400 headers: { 6401 "Content-Type": "application/json", 6402 "X-CSRF-Token": getCsrfToken() 6403 }, 6404 body: JSON.stringify({ onboarding_step: { 6405 name: "dashboard_opportunities_callout", 6406 seen: true 6407 } }), 6408 keepalive: true 6409 }).catch(() => {}); 6410 }, [calloutIdentity]); 6411 const opportunitySignals = import_react$1.useMemo(() => [...lookalikes.map((lookalike_1) => ({
6412 variant: OPPORTUNITY_SIGNAL_VARIANT.proactiveSuggestion, 6413 lookalike: lookalike_1 6414 })), ...savedSearches.map((savedSearch) => ({ 6415 variant: OPPORTUNITY_SIGNAL_VARIANT.savedSearchUpdate, 6416 savedSearch 6417 }))], [lookalikes, savedSearches]); 6418 const opportunitiesUnavailable = opportunitiesAnyDegraded && opportunitySignals.length === 0; 6419 const hasSignals = leadSignals.length > 0 || opportunitySignals.length > 0; 6420 const signalsResolved = !opportunitiesPending && !signalsPending; 6421 const anySignalsDegraded = opportunitiesAnyDegraded || signalsDegraded; 6422 const showOnboardingSuccess = celebration_pending && hasSignals; 6423 const showOnboarding = (showOnboardingSuccess || signalsResolved && !anySignalsDegraded && !hasSignals && (!account_established || leads_count === 0)) && !onboardingDismissed; 6424 import_react$1.useEffect(() => { 6425 if (!showOnboardingSuccess) return; 6426 if (hasLatchedFirstSignalRef.current) return; 6427 hasLatchedFirstSignalRef.current = true; 6428 fetch(dashboard_first_signal_path(), { 6429 method: "POST", 6430 headers: { "X-CSRF-Token": getCsrfToken() }, 6431 keepalive: true 6432 }).catch(() => {}); 6433 }, [showOnboardingSuccess]); 6434 const guidanceStage = account_established ? "restart" : leads_count === 0 ? "start" : opportunities_threshold_reached ? "pending" : "growing"; 6435 const opportunitiesEmptyStage = leads_count === 0 ? "no_leads" : account_established ? "established" : opportunities_threshold_reached ? "pending" : "growing"; 6436 const leadSignalsEmptyStage = leads_count === 0 ? "no_leads" : account_established ? "established" : "watching"; 6437 const viewedTrackedRef = import_react$1.useRef(false); 6438 import_react$1.useEffect(() => { 6439 if (!signalsResolved || viewedTrackedRef.current) return; 6440 viewedTrackedRef.current = true; 6441 captureSignalsEvent("signals-dashboard-viewed", { 6442 is_premium: isPremium, 6443 unread_cta_variant: unreadCtaVariant, 6444 degraded: anySignalsDegraded, 6445 suggestion_count: lookalikes.length, 6446 saved_search_count: savedSearches.length, 6447 existing_signal_count: completeLeadSignals.length, 6448 unseen_suggestion_count: lookalikes.filter((lookalike_2) => !isOpportunityVisited(lookalike_2)).length, 6449 unseen_signal_count: completeLeadSignals.filter((signal_2) => !isSignalVisited(signal_2)).length 6450 }); 6451 }, [ 6452 signalsResolved, 6453 anySignalsDegraded, 6454 isPremium, 6455 unreadCtaVariant, 6456 lookalikes, 6457 savedSearches, 6458 completeLeadSignals, 6459 isOpportunityVisited, 6460 isSignalVisited 6461 ]); 6462 const captureOpportunityOpened = import_react$1.useCallback((lookalikeId_0) => { 6463 const position = lookalikes.findIndex((lookalike_3) => lookalike_3.id === lookalikeId_0); 6464 if (position < 0) return; 6465 const lookalike_4 = lookalikes[position]; 6466 captureSignalsEvent("signals-opened", { 6467 signal_source: SIGNAL_SOURCE.hunterSuggestion, 6468 signal_id: lookalike_4.id, 6469 signal_type: null, 6470 entity: lookalikeSourceToEntityType(lookalike_4.lookalike_source), 6471 result_count: lookalike_4.count_of_results, 6472 position: position + 1, 6473 was_unseen: !isOpportunityVisited(lookalike_4), 6474 age_hours: ageHours(lookalike_4.created_at), 6475 unread_cta_variant: unreadCtaVariant 6476 }); 6477 }, [ 6478 lookalikes, 6479 isOpportunityVisited, 6480 unreadCtaVariant 6481 ]); 6482 import_react$1.useEffect(() => { 6483 const rawId = new URLSearchParams(window.location.search).get("opportunity_id"); 6484 if (!rawId) return; 6485 const id_0 = Number(rawId); 6486 if (!Number.isFinite(id_0) || id_0 <= 0) return; 6487 if (!lookalikes.some((lookalike_5) => lookalike_5.id === id_0)) return; 6488 if (openOpportunityId === id_0) return; 6489 setOpenOpportunityId(id_0); 6490 captureOpportunityOpened(id_0); 6491 markOpportunitySeen(id_0); 6492 }, [ 6493 lookalikes, 6494 openOpportunityId, 6495 markOpportunitySeen, 6496 captureOpportunityOpened 6497 ]); 6498 const handleLeadSignalsConfigSaved = import_react$1.useCallback(async (data) => { 6499 deferredTeamActivitiesInvalidated.current = true; 6500 setWatchedSignalTypes(data.watched_lead_signal_types); 6501 setIsLeadSignalsConfigOpen(false); 6502 if (grouped_lead_signals) { 6503 setOpenSignalId(null); 6504 setVisibleLeadSignalsCount(LEAD_SIGNALS_BATCH_SIZE); 6505 leadSignalsBoundaryHandled.current = false; 6506 await groupedFeed.refresh(); 6507 return; 6508 } 6509 activeLeadSignalsRequest.current?.abort(); 6510 leadSignalsRequestSession.current += 1; 6511 const session_0 = leadSignalsRequestSession.current; 6512 const controller_0 = new AbortController(); 6513 activeLeadSignalsRequest.current = controller_0; 6514 setSignalsDegraded(false); 6515 replaceTeamActivities(void 0); 6516 setLeadSignalsNextCursor(null); 6517 setVisibleLeadSignalsCount(LEAD_SIGNALS_BATCH_SIZE); 6518 setLeadSignalsPaginationState("idle"); 6519 leadSignalsBoundaryHandled.current = false;
6520 const deadlineId = setTimeout(() => controller_0.abort(), LEAD_SIGNALS_REFRESH_DEADLINE_MS); 6521 const result_0 = await requestLeadSignalsPage(controller_0.signal); 6522 clearTimeout(deadlineId); 6523 if (session_0 !== leadSignalsRequestSession.current) return; 6524 if (activeLeadSignalsRequest.current === controller_0) activeLeadSignalsRequest.current = null; 6525 if (result_0.status === "redirect") { 6526 window.location.href = result_0.url; 6527 return; 6528 } 6529 if (result_0.status === "error") { 6530 replaceTeamActivities([]); 6531 setSignalsDegraded(true); 6532 return; 6533 } 6534 replaceTeamActivities(result_0.page.team_activities); 6535 setLeadSignalsNextCursor(result_0.page.next_cursor); 6536 }, [ 6537 replaceTeamActivities, 6538 grouped_lead_signals, 6539 groupedFeed.refresh 6540 ]); 6541 const handleOpportunityOpenChange = import_react$1.useCallback((lookalikeId_1, open) => { 6542 setOpenOpportunityId(open ? lookalikeId_1 : null); 6543 if (open) { 6544 captureOpportunityOpened(lookalikeId_1); 6545 markOpportunitySeen(lookalikeId_1); 6546 } 6547 const params = new URLSearchParams(window.location.search); 6548 if (open) params.set("opportunity_id", String(lookalikeId_1)); 6549 else params.delete("opportunity_id"); 6550 const queryString = params.toString(); 6551 const nextUrl = `${window.location.pathname}${queryString ? `?${queryString}` : ""}${window.location.hash}`; 6552 if (nextUrl === `${window.location.pathname}${window.location.search}${window.location.hash}`) return; 6553 window.history.replaceState(window.history.state, "", nextUrl); 6554 if ("App" in globalThis && App.analytics?.posthog_capture) App.analytics.posthog_capture("$pageview", { $current_url: window.location.href }); 6555 }, [markOpportunitySeen, captureOpportunityOpened]); 6556 return /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(DashboardProvider, { 6557 initialCampaigns: campaigns, 6558 initialLeadsLists: leads_lists, 6559 initialFolders: folders, 6560 initialCompanyLists: company_lists, 6561 children: [ 6562 /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6563 className: "dashboard-container", 6564 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(PageHeader, { 6565 variant: "ghost", 6566 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(PageHeaderTitle, { children: i18n.t("js.dashboard.title") }) 6567 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("div", { 6568 className: "dashboard-v2__content", 6569 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6570 className: "dashboard-v2__content-inner", 6571 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("p", { 6572 className: "text-2xl-medium", 6573 children: GreetingHelper.getGreeting(userName) 6574 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6575 className: "dashboard-v2__layout", 6576 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6577 className: "dashboard-v2__main-column", 6578 children: [ 6579 showOnboarding && /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_jsx_runtime$1.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(DashboardShortcuts, { 6580 title: i18n.t(`js.dashboard.index.shortcuts.${guidanceStage}.title`), 6581 subtitle: i18n.t(`js.dashboard.index.shortcuts.${guidanceStage}.subtitle`), 6582 variant: showOnboardingSuccess ? "success" : "default", 6583 onClose: () => setOnboardingDismissed(true) 6584 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" })] }), 6585 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Card, { 6586 className: "dashboard-v2__overview", 6587 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CardContent, { children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(DashboardMetrics, { 6588 leads_count, 6589 leads_without_email, 6590 verified_leads, 6591 unread_inbox_messages 6592 }) }) 6593 }), 6594 showOpportunitiesCallout && isPremium && opportunitySignals.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CalloutAlert, { 6595 dismissable: true, 6596 onDismiss: handleDismissOpportunitiesCallout, 6597 title: i18n.t("js.dashboard.opportunities_callout.title"), 6598 description: i18n.t("js.dashboard.opportunities_callout.description") 6599 }),
6600 /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6601 className: "dashboard-v2__signals", 6602 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Card, { 6603 className: "dashboard-v2__opportunity-signals", 6604 grow: true, 6605 density: "compact", 6606 outerTitle: /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(CardTitle, { children: [ 6607 i18n.t("js.dashboard.opportunity_signals.title"), 6608 isPremium && !opportunitiesPending && !opportunitiesUnavailable && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CardTitleSuffix, { children: opportunitySignals.length.toString() }), 6609 isPremium && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CardTitleActions, { children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(TooltipTrigger, { 6610 asChild: true, 6611 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("span", { 6612 className: "dashboard-v2__opportunity-signals-meta has-tooltip", 6613 children: i18n.t("js.dashboard.opportunity_signals.updated_weekly") 6614 }) 6615 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(TooltipContent, { 6616 className: "tooltip-md", 6617 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("div", { children: i18n.t("js.dashboard.opportunity_signals.updated_weekly_tooltip") }), opportunity_signals_updated_at && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("div", { children: i18n.t("js.dashboard.opportunity_signals.updated_weekly_last_updated", { date: formatOpportunitySignalsUpdatedAt(opportunity_signals_updated_at) }) })] 6618 })] }) }) }) 6619 ] }), 6620 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CardContent, { children: !isPremium ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(UpgradeBlock, { 6621 variant: "centered", 6622 title: i18n.t("js.dashboard.opportunity_signals.upgrade.title"), 6623 description: i18n.t("js.dashboard.opportunity_signals.upgrade.description"), 6624 upgradeHref: checkout_upgrade_plan_path({ 6625 needed_credits: 0, 6626 needed_level: 1, 6627 operation: "subscription", 6628 direct_route: true, 6629 format: "turbo_stream" 6630 }), 6631 upgradeTurboFrame: "checkout-modal" 6632 }
6632) : opportunitiesUnavailable ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(SectionUnavailable, {}) : opportunitiesPending ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(OpportunitySignalsSkeleton, {}) : opportunitySignals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(EmptyState, { 6633 size: "small", 6634 fill: true, 6635 children: [ 6636 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateBackground, { backgroundUrl: "/empty-state/empty-state-star.svg" }), 6637 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateTitle, { children: i18n.t(`js.dashboard.opportunity_signals.empty_state.${opportunitiesEmptyStage}.title`) }), 6638 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateDescription, { children: i18n.t(`js.dashboard.opportunity_signals.empty_state.${opportunitiesEmptyStage}.description`) }), 6639 (opportunitiesEmptyStage === "no_leads" || opportunitiesEmptyStage === "growing") && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateActions, { children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Button, { 6640 size: "sm", 6641 onClick: () => { 6642 window.location.href = discovers_path(); 6643 }, 6644 "data-testid": "start-prospecting-btn", 6645 children: i18n.t("js.dashboard.opportunity_signals.empty_state.start_prospecting") 6646 }) }) 6647 ] 6648 }) : opportunitySignals.map((signal_3, index) => { 6649 const isLast = index === opportunitySignals.length - 1; 6650 const key = signal_3.variant === OPPORTUNITY_SIGNAL_VARIANT.proactiveSuggestion ? `lookalike-${signal_3.lookalike.id}` : `saved-search-${signal_3.savedSearch.id}`; 6651 return /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_react$1.Fragment, { children: [signal_3.variant === OPPORTUNITY_SIGNAL_VARIANT.proactiveSuggestion ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(OpportunitySignalItem, { 6652 variant: signal_3.variant, 6653 id: signal_3.lookalike.id, 6654 title: signal_3.lookalike.lookalike_type, 6655 visited: isOpportunityVisited(signal_3.lookalike), 6656 description: signal_3.lookalike.cluster_description, 6657 numberOfCreditsLeft, 6658 entityType: lookalikeSourceToEntityType(signal_3.lookalike.lookalike_source), 6659 entityCount: signal_3.lookalike.count_of_results, 6660 lookalikeIds: signal_3.lookalike.lookalike_ids, 6661 emphasizeUnread: unreadCtaPrimary, 6662 isOpen: openOpportunityId === signal_3.lookalike.id, 6663 onOpenChange: (open_0) => handleOpportunityOpenChange(signal_3.lookalike.id, open_0) 6664 }) : /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(OpportunitySignalItem, { 6665 variant: signal_3.variant, 6666 title: i18n.t("js.dashboard.opportunity_signals.saved_search_update_title"), 6667 visited: false, 6668 description: signal_3.savedSearch.name, 6669 entityType: "company", 6670 entityCount: signal_3.savedSearch.new_results_count ?? 0, 6671 entityCountApproximate: Boolean(signal_3.savedSearch.new_results_count_approximate), 6672 emphasizeUnread: unreadCtaPrimary, 6673 href: discovers_path({ view_id: signal_3.savedSearch.id }), 6674 onClick: () => captureSignalsEvent("signals-opened", { 6675 signal_source: SIGNAL_SOURCE.savedSearchUpdate, 6676 signal_id: signal_3.savedSearch.id, 6677 signal_type: null, 6678 entity: "company", 6679 result_count: signal_3.savedSearch.new_results_count ?? 0, 6680 position: index + 1, 6681 was_unseen: true, 6682 age_hours: ageHours(signal_3.savedSearch.last_results_counted_at), 6683 unread_cta_variant: unreadCtaVariant 6684 }) 6685 }), !isLast && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" })] }, key); 6686 }) }) 6687 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Card, { 6688 className: "dashboard-v2__lead-signals", 6689 grow: true, 6690 density: "compact", 6691 outerTitle: /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(CardTitle, { children: [i18n.t("js.dashboard.lead_signals.title"), /* @__P
6691URE__ */ (0, import_jsx_runtime$1.jsx)(CardTitleActions, { children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Button, { 6692 variant: "ghost", 6693 size: "xs", 6694 iconOnly: true, 6695 title: i18n.t("js.dashboard.lead_signals.configure"), 6696 "aria-label": i18n.t("js.dashboard.lead_signals.configure"), 6697 onClick: () => { 6698 captureSignalsEvent("signals-settings-opened"); 6699 setIsLeadSignalsConfigOpen(true); 6700 }, 6701 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Icon, { 6702 icon: "cog", 6703 "aria-hidden": "true", 6704 size: "sm" 6705 }) 6706 }) })] }), 6707 children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CardContent, { children: signalsDegraded ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(SectionUnavailable, {}) : signalsPending ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(LeadSignalsSkeleton, {}) : completeLeadSignals.length === 0 && leadSignalsNextCursor === null ? watchedSignalTypes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(EmptyState, { 6708 size: "small", 6709 fill: true, 6710 children: [ 6711 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateBackground, { backgroundUrl: "/empty-state/empty-state-star.svg" }), 6712 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateTitle, { children: i18n.t("js.dashboard.lead_signals.empty_state.no_types_selected_message") }), 6713 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateDescription, { children: i18n.t("js.dashboard.lead_signals.empty_state.no_types_selected_description") }) 6714 ] 6715 }) : /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(EmptyState, { 6716 size: "small", 6717 fill: true, 6718 children: [ 6719 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateBackground, { backgroundUrl: "/empty-state/empty-state-star.svg" }), 6720 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateTitle, { children: i18n.t(`js.dashboard.lead_signals.empty_state.${leadSignalsEmptyStage}.title`) }), 6721 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateDescription, { children: i18n.t(`js.dashboard.lead_signals.empty_state.${leadSignalsEmptyStage}.description`) }), 6722 leadSignalsEmptyStage === "no_leads" && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(EmptyStateActions, { children: /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Button, { 6723 size: "sm", 6724 onClick: () => { 6725 window.location.href = discovers_path(); 6726 }, 6727 "data-testid": "start-prospecting-btn", 6728 children: i18n.t("js.dashboard.lead_signals.empty_state.start_prospecting") 6729 }) }) 6730 ] 6731 }) : /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_jsx_runtime$1.Fragment, { children: [ 6732 leadSignals.map((signal_4, index_0) => /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_react$1.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(LeadSignalItem, { 6733 message: signal_4.signal, 6734 timestamp: signal_4.timestamp, 6735 eventType: signal_4.eventType, 6736 entityType: signal_4.entityType, 6737 count: signal_4.entityCount, 6738 entity: signal_4.entity, 6739 leadingCompanies: signal_4.leadingCompanies, 6740 leadingCompanyCount: signal_4.leadingCompanyCount, 6741 entityNames: signalEntityNames(signal_4), 6742 visited: isSignalVisited(signal_4), 6743 onClick: () => { 6744 captureSignalsEvent("signals-opened", { 6745 signal_source: SIGNAL_SOURCE.existingLeadsSignal, 6746 signal_id: signal_4.id, 6747 signal_type: signal_4.eventType, 6748 entity: signal_4.entityType === "email" ? "lead" : "company", 6749 result_count: signal_4.entityCount, 6750 position: index_0 + 1, 6751 was_unseen: !isSignalVisited(signal_4), 6752 age_hours: ageHours(signal_4.timestamp) 6753 }); 6754 const entity = signal_4.entity; 6755 const domainId = signal_4.activities[0]?.activity.activity_id; 6756 if (signal_4.entityCount === 1 && entity?.kind === "lead" && entity.leadId != null && CONTACT_CHANGE_SIDE_PANEL_TYPES.has(signal_4.eventType) && form_options) { 6757 markSignalSeen(signal_4); 6758 openLeadPanel(entity.leadId); 6759 } else if (signal_4.entityCount === 1 && entity?.kind === "company" && COMPANY_SIDE_PANEL_TYPES.has(signal_4.eventType) && domainId != null) { 6760 markSignalSeen(signal_4); 6761 leadPanelRequestRef.current = null; 6762 setCompanyPanel({ 6763 domainId, 6764 companyName: entity.name 6765 }); 6766 } else { 6767 markSignalSeen(signal_4, true); 6768 leadPanelRequestRef.current = null; 6769 setOpenSignalId(signal_4.id); 6770 } 6771 } 6772 }
6772), index_0 < leadSignals.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" })] }, signal_4.id)), 6773 canLoadMoreLeadSignals && /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_jsx_runtime$1.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(LeadSignalsLoadMore, { 6774 boundaryRef: leadSignalsBoundaryRef, 6775 paginationState: leadSignalsPaginationState, 6776 onLoadMore: () => { 6777 leadSignalsBoundaryHandled.current = true; 6778 loadMoreLeadSignals(); 6779 } 6780 })] }), 6781 !canLoadMoreLeadSignals && leadSignals.length < LEAD_SIGNALS_BATCH_SIZE && /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)(import_jsx_runtime$1.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { variant: "light" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("p", { 6782 className: "dashboard-v2__lead-signals-hint", 6783 children: i18n.t("js.dashboard.lead_signals.more_signals_hint") 6784 })] }) 6785 ] }) }) 6786 })] 6787 }), 6788 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(DashboardUsefulResources, {}) 6789 ] 6790 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6791 className: "dashboard-v2__rail", 6792 children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(Separator, { 6793 variant: "light", 6794 className: "dashboard-v2__rail-separator" 6795 }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { 6796 className: "dashboard-v2__rail-cards", 6797 children: [isPremium && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(DashboardHealth, {}), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(DashboardAnnouncement, { maxCount: 2 })] 6798 })] 6799 })] 6800 })] 6801 }) 6802 })] 6803 }), 6804 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(GroupedLeadSignalsSidepanel, { 6805 onRefresh: () => { 6806 groupedFeed.refresh(); 6807 }, 6808 signal: openSignal, 6809 isOpen: openSignal !== null, 6810 leadsLists: leads_lists, 6811 folders, 6812 campaigns, 6813 onOpenChange: (open_1) => { 6814 if (!open_1) setOpenSignalId(null); 6815 } 6816 }), 6817 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(CompanySidePanel, { 6818 company: companyPanel, 6819 open: companyPanel !== null, 6820 from: "dashboard", 6821 onOpenChange: (open_2) => { 6822 if (!open_2) setCompanyPanel(null); 6823 }, 6824 modal: true 6825 }),
6826 form_options && /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(LeadSidePanel, { 6827 lead: leadPanelLead, 6828 open: leadPanelOpen, 6829 onOpenChange: (open_3) => { 6830 setLeadPanelOpen(open_3); 6831 if (!open_3) leadPanelRequestRef.current = null; 6832 }, 6833 customAttributes: custom_attributes, 6834 formOptions: form_options, 6835 openInLeadsHref: leadPanelLead ? lead_path(leadPanelLead.id) : void 0, 6836 leadsLists: leads_lists, 6837 onLeadUpdated: (updatedLead) => setLeadPanelLead(updatedLead), 6838 hasSyncableApps: has_syncable_apps, 6839 onLeadDeleted: (deletedLeadId) => { 6840 if (grouped_lead_signals) groupedFeed.clearLead(deletedLeadId); 6841 updateTeamActivities((previous_2) => previous_2?.map((teamActivity) => { 6842 const data_0 = teamActivity.activity.associated_record_data; 6843 if (data_0?.lead_id !== deletedLeadId) return teamActivity; 6844 return { 6845 ...teamActivity, 6846 activity: { 6847 ...teamActivity.activity, 6848 associated_record_data: { 6849 ...data_0, 6850 lead_id: null 6851 } 6852 } 6853 }; 6854 })); 6855 setLeadPanelOpen(false); 6856 setLeadPanelLead(null); 6857 leadPanelRequestRef.current = null; 6858 }, 6859 modal: true 6860 }), 6861 /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(LeadSignalsConfigDialog, { 6862 isOpen: isLeadSignalsConfigOpen, 6863 onClose: () => setIsLeadSignalsConfigOpen(false), 6864 watchedSignalTypes, 6865 availableSignalTypes: available_lead_signal_types, 6866 onSaved: handleLeadSignalsConfigSaved 6867 }) 6868 ] 6869 }); 6870} 6871var import_compiler_runtime$1, import_react$1, import_jsx_runtime$1, COMPANY_SIDE_PANEL_TYPES, CONTACT_CHANGE_SIDE_PANEL_TYPES, LEAD_SIGNALS_BATCH_SIZE, CALLOUT_DISMISSED_KEY, LEAD_SIGNALS_REFRESH_DEADLINE_MS, sameLocalDay, completeLeadSignalsForPage, isLeadSignalsPage, lookalikeSourceToEntityType; 6872function init_dashboard() { 6873 return (init_dashboard = __esmMin((() => { 6874 import_compiler_runtime$1 = require_compiler_runtime(); 6875 import_react$1 = /* @__PURE__ */ __toESM(require_react()); 6876 init_card(); 6877 init_button(); 6878 init_icon(); 6879 init_page_header(); 6880 init_dashboard_metrics(); 6881 init_opportunity_signal_item(); 6882 init_dashboard_shortcuts(); 6883 init_lead_signal_item(); 6884 init_lead_signals_load_more(); 6885 init_grouped_lead_signals_sidepanel(); 6886 init_use_grouped_lead_signals(); 6887 init_lead_signals_config_dialog(); 6888 init_lead_signals_aggregator(); 6889 init_empty_state(); 6890 init_separator(); 6891 init_callout_alert(); 6892 init_skeleton(); 6893 init_tooltip(); 6894 init_upgrade_block(); 6895 init_dashboard_provider(); 6896 init_dashboard_health(); 6897 init_dashboard_announcement(); 6898 init_useful_resources(); 6899 init_greeting_helper(); 6900 init_routes(); 6901 init_utils(); 6902 init_LeadSidePanel(); 6903 init_CompanySidePanel(); 6904 init_signals_analytics(); 6905 init_post_team_activities_seen(); 6906 init_use_is_in_view(); 6907 import_jsx_runtime$1 = require_jsx_runtime(); 6908 COMPANY_SIDE_PANEL_TYPES = /* @__PURE__ */ new Set([ 6909 "funding_added", 6910 "spike_metric", 6911 "hiring_spike", 6912 "company_growth" 6913 ]); 6914 CONTACT_CHANGE_SIDE_PANEL_TYPES = /* @__PURE__ */ new Set(["position_changed", "location_changed"]); 6915 LEAD_SIGNALS_BATCH_SIZE = 10; 6916 CALLOUT_DISMISSED_KEY = "onboarding-callout:dismissed:dashboard_opportunities_callout"; 6917 LEAD_SIGNALS_REFRESH_DEADLINE_MS = 15e3; 6918 sameLocalDay = (left, right) => left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate(); 6919 completeLeadSignalsForPage = (signals, nextCursor) => { 6920 if (!nextCursor || signals.length === 0) return signals; 6921 const oldestLoadedDay = signals[signals.length - 1].timestamp; 6922 return signals.filter((signal) => !sameLocalDay(signal.timestamp, oldestLoadedDay)); 6923 }; 6924 isLeadSignalsPage = (value) => { 6925 if (typeof value !== "object" || value === null) return false; 6926 if (!("team_activities" in value) || !("next_cursor" in value)) return false; 6927 return Array.isArray(value.team_activities) && (typeof value.next_cursor === "string" || value.next_cursor === null); 6928 }; 6929 lookalikeSourceToEntityType = (source) => { 6930 return source === "leads" || source === "campaign_replies" ? "lead" : "company"; 6931 }; 6932 })))(); 6933} 6934//#endregion 6935//#region app/frontend/pages/Dashboard/Index.tsx 6936function parseSection(raw, fallback) { 6937 if (raw === void 0) return void 0; 6938 try { 6939 return JSON.parse(raw); 6940 } catch { 6941 return { 6942 data: fallback, 6943 degraded: true 6944 }; 6945 } 6946} 6947function Index() { 6948 const $ = (0, import_compiler_runtime.c)(18); 6949 const page = usePage(); 6950 const { dashboardProps, teamActivitiesJson, userLookalikesJson, savedSearchesJson } = page.props; 6951 useFlashMessages(page.props.flash); 6952 let t0; 6953 if ($[0] !== dashboardProps) { 6954 t0 = JSON.parse(dashboardProps); 6955 $[0] = dashboardProps; 6956 $[1] = t0; 6957 } else t0 = $[1]; 6958 const props = t0; 6959 let t1; 6960 if ($[2] !== teamActivitiesJson) { 6961 t1 = parseTeamActivitiesSection(teamActivitiesJson); 6962 $[2] = teamActivitiesJson; 6963 $[3] = t1; 6964 } else t1 = $[3]; 6965 const teamActivities = t1; 6966 let t2; 6967 if ($[4] !== userLookalikesJson) { 6968 t2 = parseSection(userLookalikesJson, []); 6969 $[4] = userLookalikesJson; 6970 $[5] = t2; 6971 } else t2 = $[5]; 6972 const userLookalikes = t2; 6973 let t3; 6974 if ($[6] !== savedSearchesJson) { 6975 t3 = parseSection(savedSearchesJson, []); 6976 $[6] = savedSearchesJson; 6977 $[7] = t3; 6978 } else t3 = $[7]; 6979 const savedSearches = t3; 6980 let t4; 6981 if ($[8] === Symbol.for("react.memo_cache_sentinel")) { 6982 t4 = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Head_default, { title: "Dashboard" }); 6983 $[8] = t4; 6984 } else t4 = $[8]; 6985 const t5 = teamActivities?.data.team_activities; 6986 const t6 = teamActivities?.data.next_cursor; 6987 const t7 = teamActivities?.degraded ?? false; 6988 const t8 = userLookalikes?.data; 6989 const t9 = userLookalikes?.degraded ?? false; 6990 const t10 = savedSearches?.data; 6991 const t11 = savedSearches?.degraded ?? false; 6992 let t12; 6993 if ($[9] !== props || $[10] !== t10 || $[11] !== t11 || $[12] !== t5 || $[13] !== t6 || $[14] !== t7 || $[15] !== t8 || $[16] !== t9) { 6994 t12 = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [t4, /* @__P
6994URE__ */ (0, import_jsx_runtime.jsx)(DashboardWithErrorBoundary, { 6995 ...props, 6996 team_activities: t5, 6997 lead_signals_next_cursor: t6, 6998 team_activities_degraded: t7, 6999 user_lookalikes: t8, 7000 user_lookalikes_degraded: t9, 7001 saved_searches_with_new_results: t10, 7002 saved_searches_degraded: t11 7003 })] }); 7004 $[9] = props; 7005 $[10] = t10; 7006 $[11] = t11; 7007 $[12] = t5; 7008 $[13] = t6; 7009 $[14] = t7; 7010 $[15] = t8; 7011 $[16] = t9; 7012 $[17] = t12; 7013 } else t12 = $[17]; 7014 return t12; 7015} 7016var import_compiler_runtime, import_react, import_jsx_runtime, DashboardWithErrorBoundary; 7017function init_Index() { 7018 return (init_Index = __esmMin((() => { 7019 import_compiler_runtime = require_compiler_runtime(); 7020 init_dist$1(); 7021 import_react = require_react(); 7022 init_AppLayout(); 7023 init_useFlashMessages(); 7024 init_dashboard(); 7025 init_sentry_error_boundary(); 7026 import_jsx_runtime = require_jsx_runtime(); 7027 DashboardWithErrorBoundary = withSentryErrorBoundary(Dashboard, "Dashboard"); 7028 Index.layout = (page) => (0, import_react.createElement)(AppLayout, null, page); 7029 })))(); 7030} 7031//#endregion 7032init_Index(); 7033export { Index as default };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.