@embedreach/components 0.3.43 → 0.3.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21134,6 +21134,13 @@ const createCampaignMetricsFactory = (campaignId, campaignName, channel, busines
21134
21134
  const roas = baseMetrics.spend > 0 ? baseMetrics.revenue / baseMetrics.spend : 0;
21135
21135
  const cpc = baseMetrics.clicks > 0 ? baseMetrics.spend / baseMetrics.clicks : 0;
21136
21136
  const ctr = baseMetrics.impressions > 0 ? baseMetrics.clicks / baseMetrics.impressions : 0;
21137
+ const leadsFromAds = Math.round(baseMetrics.leads * 0.7);
21138
+ const costPerLead = leadsFromAds > 0 ? baseMetrics.spend / leadsFromAds : 0;
21139
+ const leadToPurchaseRate = baseMetrics.leads > 0 ? baseMetrics.conversions / baseMetrics.leads * 100 : 0;
21140
+ const totalValueOfConvertedLeads = baseMetrics.revenue;
21141
+ const averageLeadValue = baseMetrics.conversions > 0 ? totalValueOfConvertedLeads / baseMetrics.conversions : 0;
21142
+ const openLeads = baseMetrics.leads - baseMetrics.conversions;
21143
+ const totalValueOfOpenLeads = openLeads * averageLeadValue;
21137
21144
  return {
21138
21145
  id: campaignId,
21139
21146
  name: campaignName,
@@ -21145,6 +21152,12 @@ const createCampaignMetricsFactory = (campaignId, campaignName, channel, busines
21145
21152
  measured_conversions: baseMetrics.conversions,
21146
21153
  measured_revenue: baseMetrics.revenue,
21147
21154
  new_leads: baseMetrics.leads,
21155
+ leads_from_ads: leadsFromAds,
21156
+ cost_per_lead: costPerLead,
21157
+ lead_to_purchase_rate: leadToPurchaseRate,
21158
+ total_value_of_open_leads: totalValueOfOpenLeads,
21159
+ total_value_of_converted_leads: totalValueOfConvertedLeads,
21160
+ average_lead_value: averageLeadValue,
21148
21161
  new_customers: baseMetrics.customers,
21149
21162
  customer_ltv: baseMetrics.ltv,
21150
21163
  roas,
@@ -21163,6 +21176,8 @@ const createChannelMetricsFactory = (businessId, channel, campaigns) => {
21163
21176
  impressions: acc.impressions + campaign.impressions,
21164
21177
  conversions: acc.conversions + campaign.measured_conversions,
21165
21178
  leads: acc.leads + campaign.new_leads,
21179
+ leadsFromAds: acc.leadsFromAds + (campaign.leads_from_ads || 0),
21180
+ convertedLeadValue: acc.convertedLeadValue + (campaign.total_value_of_converted_leads || 0),
21166
21181
  customers: acc.customers + campaign.new_customers,
21167
21182
  ltv: acc.ltv + campaign.customer_ltv * campaign.new_customers
21168
21183
  }),
@@ -21173,6 +21188,8 @@ const createChannelMetricsFactory = (businessId, channel, campaigns) => {
21173
21188
  impressions: 0,
21174
21189
  conversions: 0,
21175
21190
  leads: 0,
21191
+ leadsFromAds: 0,
21192
+ convertedLeadValue: 0,
21176
21193
  customers: 0,
21177
21194
  ltv: 0
21178
21195
  }
@@ -21181,6 +21198,11 @@ const createChannelMetricsFactory = (businessId, channel, campaigns) => {
21181
21198
  const roas = totals.spend > 0 ? totals.revenue / totals.spend : 0;
21182
21199
  const cpc = totals.clicks > 0 ? totals.spend / totals.clicks : 0;
21183
21200
  const ctr = totals.impressions > 0 ? totals.clicks / totals.impressions : 0;
21201
+ const costPerLead = totals.leadsFromAds > 0 ? totals.spend / totals.leadsFromAds : 0;
21202
+ const leadToPurchaseRate = totals.leads > 0 ? totals.conversions / totals.leads * 100 : 0;
21203
+ const averageLeadValue = totals.conversions > 0 ? totals.convertedLeadValue / totals.conversions : 0;
21204
+ const openLeads = totals.leads - totals.conversions;
21205
+ const totalValueOfOpenLeads = openLeads * averageLeadValue;
21184
21206
  return {
21185
21207
  business_id: businessId,
21186
21208
  ads_spend: totals.spend,
@@ -21189,6 +21211,12 @@ const createChannelMetricsFactory = (businessId, channel, campaigns) => {
21189
21211
  measured_conversions: totals.conversions,
21190
21212
  measured_revenue: totals.revenue,
21191
21213
  new_leads: totals.leads,
21214
+ leads_from_ads: totals.leadsFromAds,
21215
+ cost_per_lead: costPerLead,
21216
+ lead_to_purchase_rate: leadToPurchaseRate,
21217
+ total_value_of_open_leads: totalValueOfOpenLeads,
21218
+ total_value_of_converted_leads: totals.convertedLeadValue,
21219
+ average_lead_value: averageLeadValue,
21192
21220
  new_customers: totals.customers,
21193
21221
  customer_ltv: avgLtv,
21194
21222
  roas,
@@ -21904,6 +21932,33 @@ function scaleChannelMetrics(channelMetrics, options) {
21904
21932
  channelMetrics.new_leads = Math.round(
21905
21933
  channelMetrics.new_leads * scalingFactor
21906
21934
  );
21935
+ if (typeof channelMetrics.leads_from_ads === "number") {
21936
+ channelMetrics.leads_from_ads = Math.round(
21937
+ channelMetrics.leads_from_ads * scalingFactor
21938
+ );
21939
+ }
21940
+ if (typeof channelMetrics.total_value_of_converted_leads === "number") {
21941
+ channelMetrics.total_value_of_converted_leads = parseFloat(
21942
+ (channelMetrics.total_value_of_converted_leads * scalingFactor).toFixed(
21943
+ 2
21944
+ )
21945
+ );
21946
+ }
21947
+ if (typeof channelMetrics.leads_from_ads === "number" && channelMetrics.leads_from_ads > 0) {
21948
+ channelMetrics.cost_per_lead = parseFloat(
21949
+ (channelMetrics.ads_spend / channelMetrics.leads_from_ads).toFixed(2)
21950
+ );
21951
+ }
21952
+ if (typeof channelMetrics.total_value_of_converted_leads === "number" && typeof channelMetrics.lead_to_purchase_rate === "number" && channelMetrics.lead_to_purchase_rate > 0) {
21953
+ channelMetrics.average_lead_value = parseFloat(
21954
+ (channelMetrics.total_value_of_converted_leads / (channelMetrics.new_leads * (channelMetrics.lead_to_purchase_rate / 100))).toFixed(2)
21955
+ );
21956
+ }
21957
+ if (typeof channelMetrics.total_value_of_open_leads === "number") {
21958
+ channelMetrics.total_value_of_open_leads = parseFloat(
21959
+ (channelMetrics.total_value_of_open_leads * scalingFactor).toFixed(2)
21960
+ );
21961
+ }
21907
21962
  channelMetrics.date_range = {
21908
21963
  start: actualStartDate,
21909
21964
  end: actualEndDate
@@ -21922,6 +21977,26 @@ function scaleChannelMetrics(channelMetrics, options) {
21922
21977
  (campaign.measured_revenue * scalingFactor).toFixed(2)
21923
21978
  );
21924
21979
  campaign.new_leads = Math.round(campaign.new_leads * scalingFactor);
21980
+ if (typeof campaign.leads_from_ads === "number") {
21981
+ campaign.leads_from_ads = Math.round(
21982
+ campaign.leads_from_ads * scalingFactor
21983
+ );
21984
+ }
21985
+ if (typeof campaign.total_value_of_converted_leads === "number") {
21986
+ campaign.total_value_of_converted_leads = parseFloat(
21987
+ (campaign.total_value_of_converted_leads * scalingFactor).toFixed(2)
21988
+ );
21989
+ }
21990
+ if (typeof campaign.total_value_of_open_leads === "number") {
21991
+ campaign.total_value_of_open_leads = parseFloat(
21992
+ (campaign.total_value_of_open_leads * scalingFactor).toFixed(2)
21993
+ );
21994
+ }
21995
+ if (typeof campaign.leads_from_ads === "number" && campaign.leads_from_ads > 0) {
21996
+ campaign.cost_per_lead = parseFloat(
21997
+ (campaign.ads_spend / campaign.leads_from_ads).toFixed(2)
21998
+ );
21999
+ }
21925
22000
  campaign.date_range = {
21926
22001
  start: actualStartDate,
21927
22002
  end: actualEndDate
@@ -22481,6 +22556,148 @@ const measureAndAcquireHandlers = [
22481
22556
  }
22482
22557
  });
22483
22558
  }),
22559
+ http.get(`${HOSTNAME}/api/orders/source-info/totals-by-field`, (req) => {
22560
+ const url = new URL(req.request.url);
22561
+ const startDateString = url.searchParams.get("startDate");
22562
+ const endDateString = url.searchParams.get("endDate");
22563
+ const groupByParam = url.searchParams.get("groupBy");
22564
+ const groupBy = groupByParam || "primary_source";
22565
+ const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
22566
+ const baseSourceInfo = platformData.orderSourceInfoTotals;
22567
+ if (!baseSourceInfo || !startDateString || !endDateString) {
22568
+ return HttpResponse.json(
22569
+ {
22570
+ success: false,
22571
+ message: "Sandbox data for order source info not found or missing date params"
22572
+ },
22573
+ { status: 400 }
22574
+ );
22575
+ }
22576
+ const { requestedDays } = parseDateRange(
22577
+ startDateString,
22578
+ endDateString,
22579
+ 90
22580
+ );
22581
+ const basePeriodDays = 60;
22582
+ const scalingFactor = requestedDays / basePeriodDays;
22583
+ const scaledSourceBreakdown = baseSourceInfo.sourceBreakdown.map(
22584
+ (item) => ({
22585
+ ...item,
22586
+ count: Math.round(item.count * scalingFactor),
22587
+ amount: parseFloat((item.amount * scalingFactor).toFixed(2))
22588
+ })
22589
+ );
22590
+ const scaledTotalCount = Math.round(
22591
+ baseSourceInfo.totalCount * scalingFactor
22592
+ );
22593
+ const scaledTotalAmount = parseFloat(
22594
+ (baseSourceInfo.totalAmount * scalingFactor).toFixed(2)
22595
+ );
22596
+ const getGroupKeyAndMeta = (item) => {
22597
+ switch (groupBy) {
22598
+ case "source_type":
22599
+ return { key: item.source_type };
22600
+ case "utm_medium": {
22601
+ let medium = "(none)";
22602
+ if (item.source_type === "paid") {
22603
+ medium = "cpc";
22604
+ } else if (item.source_type === "organic_search") {
22605
+ medium = "organic";
22606
+ } else if (item.source_type === "social") {
22607
+ medium = "social";
22608
+ } else if (item.source_type === "referral") {
22609
+ medium = "referral";
22610
+ }
22611
+ return { key: medium };
22612
+ }
22613
+ case "utm_campaign": {
22614
+ let campaign = "Direct Traffic";
22615
+ if (item.source_type === "paid") {
22616
+ if (item.primary_source.toLowerCase().includes("google")) {
22617
+ campaign = "Search Campaigns";
22618
+ } else if (item.primary_source.toLowerCase().includes("facebook")) {
22619
+ campaign = "Social Campaigns";
22620
+ } else {
22621
+ campaign = "Paid Campaigns";
22622
+ }
22623
+ } else if (item.source_type === "organic_search") {
22624
+ campaign = "Organic Search";
22625
+ } else if (item.source_type === "social") {
22626
+ campaign = "Social Organic";
22627
+ } else if (item.source_type === "referral") {
22628
+ campaign = "Referral Traffic";
22629
+ } else if (item.source_type === "direct") {
22630
+ campaign = "Direct Traffic";
22631
+ } else if (item.source_type === "unknown") {
22632
+ campaign = "Unknown";
22633
+ }
22634
+ return { key: campaign };
22635
+ }
22636
+ case "referrer_domain": {
22637
+ const lower = item.primary_source.toLowerCase();
22638
+ let domain = "direct";
22639
+ if (lower.includes("google")) {
22640
+ domain = "google.com";
22641
+ } else if (lower.includes("facebook")) {
22642
+ domain = "facebook.com";
22643
+ } else if (lower.includes("bing")) {
22644
+ domain = "bing.com";
22645
+ } else if (lower.includes("yahoo")) {
22646
+ domain = "yahoo.com";
22647
+ } else if (lower.includes("yelp")) {
22648
+ domain = "yelp.com";
22649
+ } else if (lower.includes("directory")) {
22650
+ domain = "industry-directory.com";
22651
+ } else if (item.source_type === "direct") {
22652
+ domain = "(direct)";
22653
+ } else if (item.source_type === "unknown") {
22654
+ domain = "(unknown)";
22655
+ }
22656
+ return { key: domain };
22657
+ }
22658
+ case "primary_source":
22659
+ default:
22660
+ return { key: item.primary_source, source_type: item.source_type };
22661
+ }
22662
+ };
22663
+ const breakdownMap = /* @__PURE__ */ new Map();
22664
+ for (const item of scaledSourceBreakdown) {
22665
+ const { key, source_type } = getGroupKeyAndMeta(item);
22666
+ const existing = breakdownMap.get(key);
22667
+ if (existing) {
22668
+ existing.count += item.count;
22669
+ existing.amount = parseFloat(
22670
+ (existing.amount + item.amount).toFixed(2)
22671
+ );
22672
+ } else {
22673
+ breakdownMap.set(key, {
22674
+ value: key,
22675
+ source_type,
22676
+ count: item.count,
22677
+ amount: item.amount
22678
+ });
22679
+ }
22680
+ }
22681
+ const breakdown = Array.from(breakdownMap.values());
22682
+ const breakdownTotalCount = breakdown.reduce(
22683
+ (sum, item) => sum + item.count,
22684
+ 0
22685
+ );
22686
+ const breakdownTotalAmount = parseFloat(
22687
+ breakdown.reduce((sum, item) => sum + item.amount, 0).toFixed(2)
22688
+ );
22689
+ const finalTotalCount = scaledTotalCount || breakdownTotalCount;
22690
+ const finalTotalAmount = scaledTotalAmount || breakdownTotalAmount;
22691
+ return HttpResponse.json({
22692
+ success: true,
22693
+ message: `Success (Sandbox Order Source Info Totals By Field - Grouped by ${groupBy} for ${requestedDays} days)`,
22694
+ data: {
22695
+ totalCount: finalTotalCount,
22696
+ totalAmount: finalTotalAmount,
22697
+ breakdown
22698
+ }
22699
+ });
22700
+ }),
22484
22701
  http.get(`${HOSTNAME}/api/orders/source-info`, (req) => {
22485
22702
  const url = new URL(req.request.url);
22486
22703
  const startDateString = url.searchParams.get("startDate");
@@ -22729,6 +22946,8 @@ const generateReputationResponsesData = ({
22729
22946
  startDate,
22730
22947
  endDate,
22731
22948
  locationIds,
22949
+ reviewSource,
22950
+ sentiment,
22732
22951
  cursor,
22733
22952
  limit,
22734
22953
  searchTerm
@@ -22759,6 +22978,21 @@ const generateReputationResponsesData = ({
22759
22978
  (response) => response.locationId && locationIds.includes(response.locationId)
22760
22979
  );
22761
22980
  }
22981
+ if (reviewSource) {
22982
+ const sources = reviewSource.split(",").filter(Boolean);
22983
+ filteredResponses = filteredResponses.filter(
22984
+ (response) => sources.includes(response.reviewSource)
22985
+ );
22986
+ }
22987
+ if (sentiment) {
22988
+ filteredResponses = filteredResponses.filter((response) => {
22989
+ const calculatedSentiment = calculateSentiment(
22990
+ response.reviewSource,
22991
+ response.rating
22992
+ );
22993
+ return calculatedSentiment === sentiment;
22994
+ });
22995
+ }
22762
22996
  if (searchTerm && searchTerm.length >= 3) {
22763
22997
  const searchLower = searchTerm.toLowerCase();
22764
22998
  filteredResponses = filteredResponses.filter((response) => {
@@ -22792,6 +23026,18 @@ const generateReputationResponsesData = ({
22792
23026
  total: filteredResponses.length
22793
23027
  };
22794
23028
  };
23029
+ function calculateSentiment(reviewSource, rating) {
23030
+ if (reviewSource === "google_business_profile") {
23031
+ if (rating >= 4) return "positive";
23032
+ if (rating === 3) return "neutral";
23033
+ return "negative";
23034
+ } else if (reviewSource === "internal_feedback") {
23035
+ if (rating >= 9) return "positive";
23036
+ if (rating >= 7) return "neutral";
23037
+ return "negative";
23038
+ }
23039
+ return "neutral";
23040
+ }
22795
23041
  function generateResponsePool(platformConfig, platformData, startDate, endDate, totalCount) {
22796
23042
  const responses = [];
22797
23043
  const partnerLocations = platformData.reputationData?.partnerLocations || [];
@@ -24200,6 +24446,8 @@ const reputationHandlers = [
24200
24446
  const startDate = url.searchParams.get("start_date");
24201
24447
  const endDate = url.searchParams.get("end_date");
24202
24448
  const locationIds = url.searchParams.getAll("location_ids");
24449
+ const reviewSource = url.searchParams.get("review_source");
24450
+ const sentiment = url.searchParams.get("sentiment");
24203
24451
  const cursor = url.searchParams.get("cursor");
24204
24452
  const limit = parseInt(url.searchParams.get("limit") || "25", 10);
24205
24453
  const searchTerm = url.searchParams.get("search_term");
@@ -24219,11 +24467,14 @@ const reputationHandlers = [
24219
24467
  error2
24220
24468
  );
24221
24469
  }
24470
+ const reviewSourceParam = reviewSource || null;
24222
24471
  const responsesData = generateReputationResponsesData({
24223
24472
  platformData,
24224
24473
  startDate,
24225
24474
  endDate,
24226
24475
  locationIds,
24476
+ reviewSource: reviewSourceParam,
24477
+ sentiment: sentiment || null,
24227
24478
  cursor,
24228
24479
  limit,
24229
24480
  searchTerm
package/dist/index.d.ts CHANGED
@@ -51,12 +51,14 @@ declare interface CreateAutomationModalProps {
51
51
  *
52
52
  * @param args.estimatedEmailRecipients - The estimated number of email recipients
53
53
  * @param args.estimatedSmsRecipients - The estimated number of sms recipients
54
+ * @param args.scheduleSendAt - The scheduled send time as an ISO date string, or null if not set
54
55
  *
55
56
  * @returns true if the automation should be scheduled, string otherwise that will be displayed to the user in a toast notification
56
57
  */
57
58
  onBeforeSchedule?: (args: {
58
59
  estimatedEmailRecipients: number;
59
60
  estimatedSmsRecipients: number;
61
+ scheduleSendAt: string | null;
60
62
  }) => Promise<true | string>;
61
63
  /**
62
64
  * Optional text and hyperlink to display for
@@ -477,12 +479,14 @@ export declare type ViewAutomationProps = {
477
479
  *
478
480
  * @param args.estimatedEmailRecipients - The estimated number of email recipients
479
481
  * @param args.estimatedSmsRecipients - The estimated number of sms recipients
482
+ * @param args.scheduleSendAt - The scheduled send time as an ISO date string, or null if not set
480
483
  *
481
484
  * @returns true if the automation should be scheduled, string otherwise that will be displayed to the user in a toast notification
482
485
  */
483
486
  onBeforeSchedule?: (args: {
484
487
  estimatedEmailRecipients: number;
485
488
  estimatedSmsRecipients: number;
489
+ scheduleSendAt: string | null;
486
490
  }) => Promise<true | string>;
487
491
  /**
488
492
  * Optional boolean to hide features