@embedreach/components 0.3.32 → 0.3.34

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.
@@ -18681,6 +18681,23 @@ class HttpResponse extends FetchResponse$1 {
18681
18681
  return new HttpResponse(body, normalizeResponseInit(init));
18682
18682
  }
18683
18683
  }
18684
+ function bypass(input, init) {
18685
+ const request = new Request(
18686
+ // If given a Request instance, clone it not to exhaust
18687
+ // the original request's body.
18688
+ input instanceof Request ? input.clone() : input,
18689
+ init
18690
+ );
18691
+ invariant$1(
18692
+ !request.bodyUsed,
18693
+ 'Failed to create a bypassed request to "%s %s": given request instance already has its body read. Make sure to clone the intercepted request if you wish to read its body before bypassing it.',
18694
+ request.method,
18695
+ request.url
18696
+ );
18697
+ const requestClone = request.clone();
18698
+ requestClone.headers.append("accept", "msw/passthrough");
18699
+ return requestClone;
18700
+ }
18684
18701
  checkGlobals();
18685
18702
  var e = [{ name: "Aegean Airlines", iataCode: "A3" }, { name: "Aeroflot", iataCode: "SU" }, { name: "Aerolineas Argentinas", iataCode: "AR" }, { name: "Aeromexico", iataCode: "AM" }, { name: "Air Algerie", iataCode: "AH" }, { name: "Air Arabia", iataCode: "G9" }, { name: "Air Canada", iataCode: "AC" }, { name: "Air China", iataCode: "CA" }, { name: "Air Europa", iataCode: "UX" }, { name: "Air France", iataCode: "AF" }, { name: "Air India", iataCode: "AI" }, { name: "Air Mauritius", iataCode: "MK" }, { name: "Air New Zealand", iataCode: "NZ" }, { name: "Air Niugini", iataCode: "PX" }, { name: "Air Tahiti", iataCode: "VT" }, { name: "Air Tahiti Nui", iataCode: "TN" }, { name: "Air Transat", iataCode: "TS" }, { name: "AirAsia X", iataCode: "D7" }, { name: "AirAsia", iataCode: "AK" }, { name: "Aircalin", iataCode: "SB" }, { name: "Alaska Airlines", iataCode: "AS" }, { name: "Alitalia", iataCode: "AZ" }, { name: "All Nippon Airways", iataCode: "NH" }, { name: "Allegiant Air", iataCode: "G4" }, { name: "American Airlines", iataCode: "AA" }, { name: "Asiana Airlines", iataCode: "OZ" }, { name: "Avianca", iataCode: "AV" }, { name: "Azul Linhas Aereas Brasileiras", iataCode: "AD" }, { name: "Azur Air", iataCode: "ZF" }, { name: "Beijing Capital Airlines", iataCode: "JD" }, { name: "Boliviana de Aviacion", iataCode: "OB" }, { name: "British Airways", iataCode: "BA" }, { name: "Cathay Pacific", iataCode: "CX" }, { name: "Cebu Pacific Air", iataCode: "5J" }, { name: "China Airlines", iataCode: "CI" }, { name: "China Eastern Airlines", iataCode: "MU" }, { name: "China Southern Airlines", iataCode: "CZ" }, { name: "Condor", iataCode: "DE" }, { name: "Copa Airlines", iataCode: "CM" }, { name: "Delta Air Lines", iataCode: "DL" }, { name: "Easyfly", iataCode: "VE" }, { name: "EasyJet", iataCode: "U2" }, { name: "EcoJet", iataCode: "8J" }, { name: "Egyptair", iataCode: "MS" }, { name: "El Al", iataCode: "LY" }, { name: "Emirates Airlines", iataCode: "EK" }, { name: "Ethiopian Airlines", iataCode: "ET" }, { name: "Etihad Airways", iataCode: "EY" }, { name: "EVA Air", iataCode: "BR" }, { name: "Fiji Airways", iataCode: "FJ" }, { name: "Finnair", iataCode: "AY" }, { name: "Flybondi", iataCode: "FO" }, { name: "Flydubai", iataCode: "FZ" }, { name: "FlySafair", iataCode: "FA" }, { name: "Frontier Airlines", iataCode: "F9" }, { name: "Garuda Indonesia", iataCode: "GA" }, { name: "Go First", iataCode: "G8" }, { name: "Gol Linhas Aereas Inteligentes", iataCode: "G3" }, { name: "Hainan Airlines", iataCode: "HU" }, { name: "Hawaiian Airlines", iataCode: "HA" }, { name: "IndiGo Airlines", iataCode: "6E" }, { name: "Japan Airlines", iataCode: "JL" }, { name: "Jeju Air", iataCode: "7C" }, { name: "Jet2", iataCode: "LS" }, { name: "JetBlue Airways", iataCode: "B6" }, { name: "JetSMART", iataCode: "JA" }, { name: "Juneyao Airlines", iataCode: "HO" }, { name: "Kenya Airways", iataCode: "KQ" }, { name: "KLM Royal Dutch Airlines", iataCode: "KL" }, { name: "Korean Air", iataCode: "KE" }, { name: "Kulula.com", iataCode: "MN" }, { name: "LATAM Airlines", iataCode: "LA" }, { name: "Lion Air", iataCode: "JT" }, { name: "LOT Polish Airlines", iataCode: "LO" }, { name: "Lufthansa", iataCode: "LH" }, { name: "Libyan Airlines", iataCode: "LN" }, { name: "Linea Aerea Amaszonas", iataCode: "Z8" }, { name: "Malaysia Airlines", iataCode: "MH" }, { name: "Nordwind Airlines", iataCode: "N4" }, { name: "Norwegian Air Shuttle", iataCode: "DY" }, { name: "Oman Air", iataCode: "WY" }, { name: "Pakistan International Airlines", iataCode: "PK" }, { name: "Pegasus Airlines", iataCode: "PC" }, { name: "Philippine Airlines", iataCode: "PR" }, { name: "Qantas Group", iataCode: "QF" }, { name: "Qatar Airways", iataCode: "QR" }, { name: "Republic Airways", iataCode: "YX" }, { name: "Royal Air Maroc", iataCode: "AT" }, { name: "Ryanair", iataCode: "FR" }, { name: "S7 Airlines", iataCode: "S7" }, { name: "SAS", iataCode: "SK" }, { name: "Satena", iataCode: "9R" }, { name: "Saudia", iataCode: "SV" }, { name: "Shandong Airlines", iataCode: "SC" }, { name: "Sichuan Airlines", iataCode: "3U" }, { name: "Singapore Airlines", iataCode: "SQ" }, { name: "Sky Airline", iataCode: "H2" }, { name: "SkyWest Airlines", iataCode: "OO" }, { name: "South African Airways", iataCode: "SA" }, { name: "Southwest Airlines", iataCode: "WN" }, { name: "SpiceJet", iataCode: "SG" }, { name: "Spirit Airlines", iataCode: "NK" }, { name: "Spring Airlines", iataCode: "9S" }, { name: "SriLankan Airlines", iataCode: "UL" }, { name: "Star Peru", iataCode: "2I" }, { name: "Sun Country Airlines", iataCode: "SY" }, { name: "SunExpress", iataCode: "XQ" }, { name: "TAP Air Portugal", iataCode: "TP" }, { name: "Thai AirAsia", iataCode: "FD" }, { name: "Thai Airways", iataCode: "TG" }, { name: "TUI Airways", iataCode: "BY" }, { name: "Tunisair", iataCode: "TU" }, { name: "Turkish Airlines", iataCode: "TK" }, { name: "Ukraine International", iataCode: "PS" }, { name: "United Airlines", iataCode: "UA" }, { name: "Ural Airlines", iataCode: "U6" }, { name: "VietJet Air", iataCode: "VJ" }, { name: "Vietnam Airlines", iataCode: "VN" }, { name: "Virgin Atlantic Airways", iataCode: "VS" }, { name: "Virgin Australia", iataCode: "VA" }, { name: "VivaAerobus", iataCode: "VB" }, { name: "VOEPASS Linhas Aereas", iataCode: "2Z" }, { name: "Volaris", iataCode: "Y4" }, { name: "WestJet", iataCode: "WS" }, { name: "Wingo", iataCode: "P5" }, { name: "Wizz Air", iataCode: "W6" }];
18686
18703
  var a = [{ name: "Aerospatiale/BAC Concorde", iataTypeCode: "SSC" }, { name: "Airbus A300", iataTypeCode: "AB3" }, { name: "Airbus A310", iataTypeCode: "310" }, { name: "Airbus A310-200", iataTypeCode: "312" }, { name: "Airbus A310-300", iataTypeCode: "313" }, { name: "Airbus A318", iataTypeCode: "318" }, { name: "Airbus A319", iataTypeCode: "319" }, { name: "Airbus A319neo", iataTypeCode: "31N" }, { name: "Airbus A320", iataTypeCode: "320" }, { name: "Airbus A320neo", iataTypeCode: "32N" }, { name: "Airbus A321", iataTypeCode: "321" }, { name: "Airbus A321neo", iataTypeCode: "32Q" }, { name: "Airbus A330", iataTypeCode: "330" }, { name: "Airbus A330-200", iataTypeCode: "332" }, { name: "Airbus A330-300", iataTypeCode: "333" }, { name: "Airbus A330-800neo", iataTypeCode: "338" }, { name: "Airbus A330-900neo", iataTypeCode: "339" }, { name: "Airbus A340", iataTypeCode: "340" }, { name: "Airbus A340-200", iataTypeCode: "342" }, { name: "Airbus A340-300", iataTypeCode: "343" }, { name: "Airbus A340-500", iataTypeCode: "345" }, { name: "Airbus A340-600", iataTypeCode: "346" }, { name: "Airbus A350", iataTypeCode: "350" }, { name: "Airbus A350-900", iataTypeCode: "359" }, { name: "Airbus A350-1000", iataTypeCode: "351" }, { name: "Airbus A380", iataTypeCode: "380" }, { name: "Airbus A380-800", iataTypeCode: "388" }, { name: "Antonov An-12", iataTypeCode: "ANF" }, { name: "Antonov An-24", iataTypeCode: "AN4" }, { name: "Antonov An-26", iataTypeCode: "A26" }, { name: "Antonov An-28", iataTypeCode: "A28" }, { name: "Antonov An-30", iataTypeCode: "A30" }, { name: "Antonov An-32", iataTypeCode: "A32" }, { name: "Antonov An-72", iataTypeCode: "AN7" }, { name: "Antonov An-124 Ruslan", iataTypeCode: "A4F" }, { name: "Antonov An-140", iataTypeCode: "A40" }, { name: "Antonov An-148", iataTypeCode: "A81" }, { name: "Antonov An-158", iataTypeCode: "A58" }, { name: "Antonov An-225 Mriya", iataTypeCode: "A5F" }, { name: "Boeing 707", iataTypeCode: "703" }, { name: "Boeing 717", iataTypeCode: "717" }, { name: "Boeing 720B", iataTypeCode: "B72" }, { name: "Boeing 727", iataTypeCode: "727" }, { name: "Boeing 727-100", iataTypeCode: "721" }, { name: "Boeing 727-200", iataTypeCode: "722" }, { name: "Boeing 737 MAX 7", iataTypeCode: "7M7" }, { name: "Boeing 737 MAX 8", iataTypeCode: "7M8" }, { name: "Boeing 737 MAX 9", iataTypeCode: "7M9" }, { name: "Boeing 737 MAX 10", iataTypeCode: "7MJ" }, { name: "Boeing 737", iataTypeCode: "737" }, { name: "Boeing 737-100", iataTypeCode: "731" }, { name: "Boeing 737-200", iataTypeCode: "732" }, { name: "Boeing 737-300", iataTypeCode: "733" }, { name: "Boeing 737-400", iataTypeCode: "734" }, { name: "Boeing 737-500", iataTypeCode: "735" }, { name: "Boeing 737-600", iataTypeCode: "736" }, { name: "Boeing 737-700", iataTypeCode: "73G" }, { name: "Boeing 737-800", iataTypeCode: "738" }, { name: "Boeing 737-900", iataTypeCode: "739" }, { name: "Boeing 747", iataTypeCode: "747" }, { name: "Boeing 747-100", iataTypeCode: "741" }, { name: "Boeing 747-200", iataTypeCode: "742" }, { name: "Boeing 747-300", iataTypeCode: "743" }, { name: "Boeing 747-400", iataTypeCode: "744" }, { name: "Boeing 747-400D", iataTypeCode: "74J" }, { name: "Boeing 747-8", iataTypeCode: "748" }, { name: "Boeing 747SP", iataTypeCode: "74L" }, { name: "Boeing 747SR", iataTypeCode: "74R" }, { name: "Boeing 757", iataTypeCode: "757" }, { name: "Boeing 757-200", iataTypeCode: "752" }, { name: "Boeing 757-300", iataTypeCode: "753" }, { name: "Boeing 767", iataTypeCode: "767" }, { name: "Boeing 767-200", iataTypeCode: "762" }, { name: "Boeing 767-300", iataTypeCode: "763" }, { name: "Boeing 767-400", iataTypeCode: "764" }, { name: "Boeing 777", iataTypeCode: "777" }, { name: "Boeing 777-200", iataTypeCode: "772" }, { name: "Boeing 777-200LR", iataTypeCode: "77L" }, { name: "Boeing 777-300", iataTypeCode: "773" }, { name: "Boeing 777-300ER", iataTypeCode: "77W" }, { name: "Boeing 787", iataTypeCode: "787" }, { name: "Boeing 787-8", iataTypeCode: "788" }, { name: "Boeing 787-9", iataTypeCode: "789" }, { name: "Boeing 787-10", iataTypeCode: "781" }, { name: "Canadair Challenger", iataTypeCode: "CCJ" }, { name: "Canadair CL-44", iataTypeCode: "CL4" }, { name: "Canadair Regional Jet 100", iataTypeCode: "CR1" }, { name: "Canadair Regional Jet 200", iataTypeCode: "CR2" }, { name: "Canadair Regional Jet 700", iataTypeCode: "CR7" }, { name: "Canadair Regional Jet 705", iataTypeCode: "CRA" }, { name: "Canadair Regional Jet 900", iataTypeCode: "CR9" }, { name: "Canadair Regional Jet 1000", iataTypeCode: "CRK" }, { name: "De Havilland Canada DHC-2 Beaver", iataTypeCode: "DHP" }, { name: "De Havilland Canada DHC-2 Turbo-Beaver", iataTypeCode: "DHR" }, { name: "De Havilland Canada DHC-3 Otter", iataTypeCode: "DHL" }, { name: "De Havilland Canada DHC-4 Caribou", iataTypeCode: "DHC" }, { name: "De Havilland Canada DHC-6 Twin Otter", iataTypeCode: "DHT" }, { name: "De Havilland Canada DHC-7 Dash 7", iataTypeCode: "DH7" }, { name: "De Havilland Canada DHC-8-100 Dash 8 / 8Q", iataTypeCode: "DH1" }, { name: "De Havilland Canada DHC-8-200 Dash 8 / 8Q", iataTypeCode: "DH2" }, { name: "De Havilland Canada DHC-8-300 Dash 8 / 8Q", iataTypeCode: "DH3" }, { name: "De Havilland Canada DHC-8-400 Dash 8Q", iataTypeCode: "DH4" }, { name: "De Havilland DH.104 Dove", iataTypeCode: "DHD" }, { name: "De Havilland DH.114 Heron", iataTypeCode: "DHH" }, { name: "Douglas DC-3", iataTypeCode: "D3F" }, { name: "Douglas DC-6", iataTypeCode: "D6F" }, { name: "Douglas DC-8-50", iataTypeCode: "D8T" }, { name: "Douglas DC-8-62", iataTypeCode: "D8L" }, { name: "Douglas DC-8-72", iataTypeCode: "D8Q" }, { name: "Douglas DC-9-10", iataTypeCode: "D91" }, { name: "Douglas DC-9-20", iataTypeCode: "D92" }, { name: "Douglas DC-9-30", iataTypeCode: "D93" }, { name: "Douglas DC-9-40", iataTypeCode: "D94" }, { name: "Douglas DC-9-50", iataTypeCode: "D95" }, { name: "Douglas DC-10", iataTypeCode: "D10" }, { name: "Douglas DC-10-10", iataTypeCode: "D1X" }, { name: "Douglas DC-10-30", iataTypeCode: "D1Y" }, { name: "Embraer 170", iataTypeCode: "E70" }, { name: "Embraer 175", iataTypeCode: "E75" }, { name: "Embraer 190", iataTypeCode: "E90" }, { name: "Embraer 195", iataTypeCode: "E95" }, { name: "Embraer E190-E2", iataTypeCode: "290" }, { name: "Embraer E195-E2", iataTypeCode: "295" }, { name: "Embraer EMB.110 Bandeirante", iataTypeCode: "EMB" }, { name: "Embraer EMB.120 Brasilia", iataTypeCode: "EM2" }, { name: "Embraer Legacy 600", iataTypeCode: "ER3" }, { name: "Embraer Phenom 100", iataTypeCode: "EP1" }, { name: "Embraer Phenom 300", iataTypeCode: "EP3" }, { name: "Embraer RJ135", iataTypeCode: "ER3" }, { name: "Embraer RJ140", iataTypeCode: "ERD" }, { name: "Embraer RJ145 Amazon", iataTypeCode: "ER4" }, { name: "Ilyushin IL18", iataTypeCode: "IL8" }, { name: "Ilyushin IL62", iataTypeCode: "IL6" }, { name: "Ilyushin IL76", iataTypeCode: "IL7" }, { name: "Ilyushin IL86", iataTypeCode: "ILW" }, { name: "Ilyushin IL96-300", iataTypeCode: "I93" }, { name: "Ilyushin IL114", iataTypeCode: "I14" }, { name: "Lockheed L-182 / 282 / 382 (L-100) Hercules", iataTypeCode: "LOH" }, { name: "Lockheed L-188 Electra", iataTypeCode: "LOE" }, { name: "Lockheed L-1011 Tristar", iataTypeCode: "L10" }, { name: "Lockheed L-1049 Super Constellation", iataTypeCode: "L49" }, { name: "McDonnell Douglas MD11", iataTypeCode: "M11" }, { name: "McDonnell Douglas MD80", iataTypeCode: "M80" }, { name: "McDonnell Douglas MD81", iataTypeCode: "M81" }, { name: "McDonnell Douglas MD82", iataTypeCode: "M82" }, { name: "McDonnell Douglas MD83", iataTypeCode: "M83" }, { name: "McDonnell Douglas MD87", iataTypeCode: "M87" }, { name: "McDonnell Douglas MD88", iataTypeCode: "M88" }, { name: "McDonnell Douglas MD90", iataTypeCode: "M90" }, { name: "Sukhoi Superjet 100-95", iataTypeCode: "SU9" }, { name: "Tupolev Tu-134", iataTypeCode: "TU3" }, { name: "Tupolev Tu-154", iataTypeCode: "TU5" }, { name: "Tupolev Tu-204", iataTypeCode: "T20" }, { name: "Yakovlev Yak-40", iataTypeCode: "YK4" }, { name: "Yakovlev Yak-42", iataTypeCode: "YK2" }];
@@ -20663,27 +20680,22 @@ const getPlatformConfigKey = (platformId) => {
20663
20680
  const defaultPlatformConfigs = {
20664
20681
  default: {
20665
20682
  id: "default",
20666
- name: "Sandbox",
20683
+ name: "ACME SaaS",
20667
20684
  brandColors: ["#2563eb", "#059669", "#dc2626", "#9333ea"],
20668
20685
  locationNames: ["Manhattan", "Brooklyn", "Queens", "Bronx"],
20669
20686
  locationAreas: ["New York, NY", "Brooklyn, NY", "Queens, NY", "Bronx, NY"]
20670
20687
  },
20671
20688
  goose: {
20672
20689
  id: "goose",
20673
- name: "ACME Pet Spa",
20690
+ name: "Paws Pet Hotel",
20674
20691
  brandColors: ["#f59e0b", "#dc2626", "#059669", "#7c3aed"],
20675
20692
  locationNames: [
20676
- "Pampered Paws Spa",
20677
- "Fluffy Coat Grooming",
20678
- "Tail Waggers Retreat",
20679
- "Premier Pet Palace"
20693
+ "Paws Pet Hotel Manhattan",
20694
+ "Paws Pet Hotel Brooklyn",
20695
+ "Paws Pet Hotel Queens",
20696
+ "Paws Pet Hotel Bronx"
20680
20697
  ],
20681
- locationAreas: [
20682
- "San Francisco, CA",
20683
- "Oakland, CA",
20684
- "Berkeley, CA",
20685
- "Palo Alto, CA"
20686
- ]
20698
+ locationAreas: ["Manhattan, NY", "Brooklyn, NY", "Queens, NY", "Bronx, NY"]
20687
20699
  },
20688
20700
  renterra: {
20689
20701
  id: "renterra",
@@ -20710,7 +20722,6 @@ const getPlatformConfig = (platformId) => {
20710
20722
  return defaultPlatformConfigs[configKey];
20711
20723
  };
20712
20724
  const createGbpConnectionFactory = (platformConfig, businessId, status = "connected") => {
20713
- const accountName = `${platformConfig.name} GBP Account`;
20714
20725
  const locations = Array.from(
20715
20726
  { length: Math.min(platformConfig.locationNames.length, 4) },
20716
20727
  (_2, index) => ({
@@ -20734,12 +20745,15 @@ const createGbpConnectionFactory = (platformConfig, businessId, status = "connec
20734
20745
  platform: "gbp",
20735
20746
  selectedAccount: {
20736
20747
  name: "accounts/123456789",
20737
- accountName,
20748
+ accountName: platformConfig.name,
20738
20749
  permissionLevel: "OWNER_LEVEL",
20739
20750
  verificationState: "VERIFIED",
20740
20751
  locationCount: locations.length
20741
20752
  },
20742
- selectedLocations: locations
20753
+ selectedLocations: locations.map((location2) => ({
20754
+ ...location2,
20755
+ locationState: location2.locationState
20756
+ }))
20743
20757
  },
20744
20758
  created_at: f.date.past().toISOString(),
20745
20759
  updated_at: f.date.recent().toISOString()
@@ -20760,7 +20774,7 @@ const createPartnerLocationsFactory = (platformConfig, businessId, platformId, c
20760
20774
  phone: f.phone.number(),
20761
20775
  email: f.internet.email(),
20762
20776
  website: `https://${platformConfig.name.toLowerCase().replace(/\s+/g, "-")}.com`,
20763
- googleBusinessProfilePlaceId: null,
20777
+ googleBusinessProfilePlaceId: `accounts/123456789/locations/${2e3 + index}`,
20764
20778
  businessId,
20765
20779
  platformId,
20766
20780
  createdAt: f.date.past(),
@@ -20826,17 +20840,14 @@ const createChannelDataFactory = (platformConfig) => {
20826
20840
  ];
20827
20841
  return { channelSenders, channelAccounts };
20828
20842
  };
20829
- const createReputationConfigFactory = (platformConfig, businessId, partnerLocations, gbpLocations) => {
20843
+ const createReputationConfigFactory = (platformConfig, businessId, partnerLocations) => {
20830
20844
  const locationMappings = {};
20831
- const maxMappings = Math.min(partnerLocations.length, gbpLocations.length);
20832
- for (let i2 = 0; i2 < maxMappings; i2++) {
20833
- locationMappings[partnerLocations[i2].externalId] = gbpLocations[i2].name;
20834
- }
20845
+ partnerLocations.forEach((location2) => {
20846
+ locationMappings[location2.externalId] = location2.googleBusinessProfilePlaceId || null;
20847
+ });
20835
20848
  return {
20836
20849
  id: f.string.uuid(),
20837
20850
  businessId,
20838
- locationMappings,
20839
- // Backend provides this by reading from partner locations
20840
20851
  emailChannelSenderId: null,
20841
20852
  // Will be set when user configures
20842
20853
  smsChannelSenderId: null,
@@ -20860,12 +20871,10 @@ const createSandboxReputationData = (platformId, businessId) => {
20860
20871
  );
20861
20872
  const { channelSenders, channelAccounts } = createChannelDataFactory(platformConfig);
20862
20873
  const smsApplication = createSmsApplicationFactory();
20863
- const gbpLocations = gbpConnection?.metadata?.selectedLocations || [];
20864
20874
  const reputationConfig = createReputationConfigFactory(
20865
20875
  platformConfig,
20866
20876
  businessId,
20867
- partnerLocations,
20868
- gbpLocations
20877
+ partnerLocations
20869
20878
  );
20870
20879
  return {
20871
20880
  gbpConnection,
@@ -20879,8 +20888,8 @@ const createSandboxReputationData = (platformId, businessId) => {
20879
20888
  f.seed(42);
20880
20889
  const businessConfigs = {
20881
20890
  default: {
20882
- name: "Reach Sandbox",
20883
- website: "https://sandbox.example.com",
20891
+ name: "ACME SaaS",
20892
+ website: "https://acme-saas.com",
20884
20893
  tagline: "Your sandbox environment for testing",
20885
20894
  locations: [
20886
20895
  {
@@ -20893,7 +20902,7 @@ const businessConfigs = {
20893
20902
  }
20894
20903
  ],
20895
20904
  branding: {
20896
- brandName: "Reach Sandbox",
20905
+ brandName: "ACME SaaS",
20897
20906
  primaryColor: "#2563eb",
20898
20907
  secondaryColors: ["#059669", "#dc2626", "#9333ea"],
20899
20908
  brandTagline: "Your sandbox environment for testing",
@@ -20907,7 +20916,8 @@ const businessConfigs = {
20907
20916
  accent: ["#059669", "#dc2626", "#9333ea"],
20908
20917
  background: ["#ffffff", "#f8fafc", "#f1f5f9"],
20909
20918
  text: ["#1e293b", "#475569", "#64748b"]
20910
- }
20919
+ },
20920
+ logoUrl: "https://media.licdn.com/dms/image/v2/C4D0BAQEqer-Bku3Feg/company-logo_200_200/company-logo_200_200/0/1631339163505?e=2147483647&v=beta&t=90F79drLbd8Tiz6oOGqrEDPvC7WbP4Dyp0qGTRToPIo"
20911
20921
  }
20912
20922
  },
20913
20923
  goose: {
@@ -20939,7 +20949,8 @@ const businessConfigs = {
20939
20949
  accent: ["#dc2626", "#059669", "#7c3aed"],
20940
20950
  background: ["#fef3c7", "#fef2f2", "#f0fdf4"],
20941
20951
  text: ["#92400e", "#991b1b", "#166534"]
20942
- }
20952
+ },
20953
+ logoUrl: "https://static.vecteezy.com/system/resources/previews/009/651/674/non_2x/cute-dog-logo-free-vector.jpg"
20943
20954
  }
20944
20955
  },
20945
20956
  renterra: {
@@ -20971,7 +20982,8 @@ const businessConfigs = {
20971
20982
  accent: ["#059669", "#dc2626", "#7c2d12"],
20972
20983
  background: ["#dbeafe", "#f0fdf4", "#fef2f2"],
20973
20984
  text: ["#1e3a8a", "#166534", "#991b1b"]
20974
- }
20985
+ },
20986
+ logoUrl: "https://static.vecteezy.com/system/resources/previews/043/794/103/non_2x/heavy-equipment-rental-and-service-logo-design-vector.jpg"
20975
20987
  }
20976
20988
  }
20977
20989
  };
@@ -21434,11 +21446,7 @@ const createWebPresenceContentFactory = (platformConfig, businessConfig) => {
21434
21446
  const createSandboxMeasureAndAcquireData = (platformId, businessId) => {
21435
21447
  const platformConfigKey = getPlatformConfigKey(platformId);
21436
21448
  const platformConfig = {
21437
- id: platformConfigKey,
21438
- name: platformConfigKey === "goose" ? "ACME Pet Spa" : platformConfigKey === "renterra" ? "Tomers Rentals" : "Default",
21439
- brandColors: [],
21440
- locationNames: [],
21441
- locationAreas: []
21449
+ id: platformConfigKey
21442
21450
  };
21443
21451
  const businessConfig = getBusinessConfig(platformId);
21444
21452
  const businessesMe = createBusinessDataFactory(
@@ -21705,7 +21713,7 @@ const native = {
21705
21713
  randomUUID
21706
21714
  };
21707
21715
  function v4(options, buf, offset) {
21708
- if (native.randomUUID && !buf && !options) {
21716
+ if (native.randomUUID && true && !options) {
21709
21717
  return native.randomUUID();
21710
21718
  }
21711
21719
  options = options || {};
@@ -22090,12 +22098,16 @@ const setMSWSandboxPlatformId = (platformId) => {
22090
22098
  );
22091
22099
  };
22092
22100
  const measureAndAcquireHandlers = [
22093
- http.get(`${HOSTNAME}/api/businesses/me`, () => {
22094
- const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
22101
+ http.get(`${HOSTNAME}/api/businesses/me`, async ({ request }) => {
22102
+ const response = await fetch(bypass(request));
22103
+ const originalData = await response.json();
22095
22104
  return HttpResponse.json({
22096
- success: true,
22097
- message: "Success (Sandbox)",
22098
- data: platformData.businessesMe
22105
+ ...originalData,
22106
+ data: {
22107
+ ...originalData.data,
22108
+ data_integration_completed_at: "2025-05-01T00:00:00.000Z",
22109
+ website_tracking_completed_at: "2025-05-01T00:00:00.000Z"
22110
+ }
22099
22111
  });
22100
22112
  }),
22101
22113
  http.get(`${HOSTNAME}/api/ad-accounts`, () => {
@@ -22582,9 +22594,18 @@ const generateReputationResponsesData = ({
22582
22594
  }
22583
22595
  if (searchTerm && searchTerm.length >= 3) {
22584
22596
  const searchLower = searchTerm.toLowerCase();
22585
- filteredResponses = filteredResponses.filter(
22586
- (response) => response.content.toLowerCase().includes(searchLower) || response.userName.toLowerCase().includes(searchLower) || response.userEmail.toLowerCase().includes(searchLower)
22587
- );
22597
+ filteredResponses = filteredResponses.filter((response) => {
22598
+ if (response.userName.toLowerCase().includes(searchLower)) {
22599
+ return true;
22600
+ }
22601
+ if (response.content.toLowerCase().includes(searchLower)) {
22602
+ return true;
22603
+ }
22604
+ if (response.replies && "reply_comment" in response.replies && typeof response.replies.reply_comment === "string" && response.replies.reply_comment.toLowerCase().includes(searchLower)) {
22605
+ return true;
22606
+ }
22607
+ return false;
22608
+ });
22588
22609
  }
22589
22610
  filteredResponses.sort(
22590
22611
  (a2, b2) => new Date(b2.createdAt).getTime() - new Date(a2.createdAt).getTime()
@@ -22641,41 +22662,86 @@ function createGoogleReview(platformConfig, partnerLocations, startDate, endDate
22641
22662
  ]);
22642
22663
  const reviewContent = generateReviewContent(platformConfig, rating);
22643
22664
  const location2 = partnerLocations.length > 0 ? f.helpers.arrayElement(partnerLocations) : null;
22665
+ const createdAtDate = f.date.between({ from: startDate, to: endDate });
22666
+ const hasReply = f.helpers.weightedArrayElement([
22667
+ { weight: 30, value: true },
22668
+ // 30% have business replies
22669
+ { weight: 70, value: false }
22670
+ ]);
22671
+ const replyData = hasReply ? (() => {
22672
+ const oneDayMs = 24 * 60 * 60 * 1e3;
22673
+ const replyWindowStartMs = Math.min(
22674
+ createdAtDate.getTime() + oneDayMs,
22675
+ endDate.getTime()
22676
+ );
22677
+ const reply_updated_at = replyWindowStartMs === endDate.getTime() ? new Date(replyWindowStartMs).toISOString() : f.date.between({
22678
+ from: new Date(replyWindowStartMs),
22679
+ to: endDate
22680
+ }).toISOString();
22681
+ return {
22682
+ source: "google_business_profile",
22683
+ reply_comment: generateReplyContent(platformConfig, rating),
22684
+ reply_updated_at
22685
+ };
22686
+ })() : void 0;
22687
+ const isLinked = f.helpers.weightedArrayElement([
22688
+ { weight: 30, value: true },
22689
+ // 30% are linked
22690
+ { weight: 70, value: false }
22691
+ // 70% are not linked
22692
+ ]);
22693
+ const userName = isLinked ? f.person.fullName() : "Anonymous";
22644
22694
  return {
22645
22695
  id: f.string.uuid(),
22646
22696
  source: "google",
22697
+ reviewSource: "google_business_profile",
22647
22698
  rating,
22648
- userName: f.person.fullName(),
22649
- userEmail: f.internet.email(),
22650
- // In real Google reviews, email might not be available
22699
+ userName,
22700
+ // userEmail: faker.internet.email(), // In real Google reviews, email might not be available
22651
22701
  content: reviewContent,
22652
- createdAt: f.date.between({ from: startDate, to: endDate }).toISOString(),
22653
- hasReply: f.helpers.weightedArrayElement([
22654
- { weight: 30, value: true },
22655
- // 30% have business replies
22656
- { weight: 70, value: false }
22657
- ]),
22702
+ createdAt: createdAtDate.toISOString(),
22703
+ hasReply,
22658
22704
  isResponded: false,
22659
22705
  // Not applicable for Google reviews
22660
- actionUrl: `https://business.google.com/reviews/l/${f.string.alphanumeric(21)}`,
22661
- locationId: location2?.externalId || void 0
22706
+ locationId: location2?.id || void 0,
22707
+ ...replyData ? { replies: replyData } : {},
22708
+ partnerUserId: isLinked ? f.string.uuid() : void 0,
22709
+ reviewExternalId: f.string.alphanumeric(21),
22710
+ reviewerImageUrl: f.image.url()
22662
22711
  };
22663
22712
  }
22664
22713
  function createInternalFeedback(platformConfig, partnerLocations, startDate, endDate) {
22665
22714
  const rating = f.helpers.weightedArrayElement([
22666
- { weight: 80, value: 5 },
22667
- // Thumbs up (positive feedback) - 80%
22668
- { weight: 20, value: 1 }
22669
- // Thumbs down (negative feedback) - 20%
22715
+ { weight: 15, value: 10 },
22716
+ // 15% - 10 rating (promoters)
22717
+ { weight: 25, value: 9 },
22718
+ // 25% - 9 rating (promoters)
22719
+ { weight: 15, value: 8 },
22720
+ // 15% - 8 rating (passives)
22721
+ { weight: 10, value: 7 },
22722
+ // 10% - 7 rating (passives)
22723
+ { weight: 8, value: 6 },
22724
+ // 8% - 6 rating (detractors)
22725
+ { weight: 7, value: 5 },
22726
+ // 7% - 5 rating (detractors)
22727
+ { weight: 6, value: 4 },
22728
+ // 6% - 4 rating (detractors)
22729
+ { weight: 5, value: 3 },
22730
+ // 5% - 3 rating (detractors)
22731
+ { weight: 4, value: 2 },
22732
+ // 4% - 2 rating (detractors)
22733
+ { weight: 5, value: 1 }
22734
+ // 5% - 1 rating (detractors)
22670
22735
  ]);
22671
22736
  const feedbackContent = generateFeedbackContent(platformConfig, rating);
22672
22737
  const location2 = partnerLocations.length > 0 ? f.helpers.arrayElement(partnerLocations) : null;
22673
22738
  return {
22674
22739
  id: f.string.uuid(),
22675
22740
  source: "internal",
22741
+ reviewSource: "internal_feedback",
22676
22742
  rating,
22677
22743
  userName: f.person.fullName(),
22678
- userEmail: f.internet.email(),
22744
+ // userEmail: faker.internet.email(),
22679
22745
  content: feedbackContent,
22680
22746
  createdAt: f.date.between({ from: startDate, to: endDate }).toISOString(),
22681
22747
  hasReply: false,
@@ -22685,9 +22751,8 @@ function createInternalFeedback(platformConfig, partnerLocations, startDate, end
22685
22751
  // 40% have customer responses
22686
22752
  { weight: 60, value: false }
22687
22753
  ]),
22688
- actionUrl: void 0,
22689
- // Internal feedback doesn't have external action URLs
22690
- locationId: location2?.externalId || void 0
22754
+ locationId: location2?.id || void 0,
22755
+ reviewerImageUrl: f.image.url()
22691
22756
  };
22692
22757
  }
22693
22758
  function generateReviewContent(platformConfig, rating) {
@@ -22759,42 +22824,78 @@ function getReviewTemplates(platformId, rating) {
22759
22824
  if (rating <= 2) return commonNegative;
22760
22825
  return commonNeutral;
22761
22826
  }
22827
+ function generateReplyContent(platformConfig, rating) {
22828
+ const platformName = platformConfig.name || "Business";
22829
+ const positiveReplies = [
22830
+ `Thank you so much for your kind words! We're thrilled to hear you had a great experience with ${platformName}. We look forward to serving you again soon!`,
22831
+ `We really appreciate your positive feedback! It's wonderful to know that our team at ${platformName} was able to exceed your expectations. Thank you for choosing us!`,
22832
+ `Thank you for taking the time to leave such a wonderful review! We're so glad you enjoyed your experience at ${platformName}. See you again soon!`,
22833
+ `Your review made our day! We're delighted that you had such a positive experience with ${platformName}. Thank you for your support!`
22834
+ ];
22835
+ const neutralReplies = [
22836
+ `Thank you for your feedback. We appreciate you taking the time to share your experience with ${platformName}. We're always working to improve our services and would love to hear more about how we can serve you better.`,
22837
+ `We appreciate your honest feedback. At ${platformName}, we're committed to continuous improvement. Please feel free to reach out to us directly if there's anything specific we can address.`,
22838
+ `Thank you for your review. We value your input and are always looking for ways to enhance our customers' experience at ${platformName}. We hope to have the opportunity to serve you better in the future.`
22839
+ ];
22840
+ const negativeReplies = [
22841
+ `We sincerely apologize that your experience at ${platformName} did not meet your expectations. Your feedback is very important to us, and we would like the opportunity to make this right. Please contact us directly so we can address your concerns.`,
22842
+ `Thank you for bringing this to our attention. We're sorry to hear about your experience at ${platformName}. We take all feedback seriously and would appreciate the chance to discuss this with you further. Please reach out to us at your convenience.`,
22843
+ `We're truly sorry about your disappointing experience with ${platformName}. This is not the standard of service we strive for. We'd like to learn more about what happened and work towards a resolution. Please contact our team directly.`,
22844
+ `Your feedback is concerning to us, and we apologize for falling short of your expectations at ${platformName}. We'd very much like to understand what went wrong and how we can improve. Please get in touch with us so we can make this right.`
22845
+ ];
22846
+ if (rating >= 4) return f.helpers.arrayElement(positiveReplies);
22847
+ if (rating === 3) return f.helpers.arrayElement(neutralReplies);
22848
+ return f.helpers.arrayElement(negativeReplies);
22849
+ }
22762
22850
  function getFeedbackTemplates(platformId, rating) {
22763
- const commonPositive = [
22764
- "I wanted to share my positive experience with {business}. The entire process was smooth and professional. The staff was knowledgeable and took the time to explain everything clearly. I'm very satisfied with the service and would definitely return.",
22765
- "Excellent service from {business}! From the initial consultation to the final delivery, everything exceeded my expectations. The team was responsive to my questions and made sure I was completely satisfied with the outcome.",
22766
- "Outstanding experience with {business}. The quality of work was exceptional and the customer service was top-notch. I appreciated the attention to detail and the follow-up communication. Highly recommend!"
22851
+ const commonPromoters = [
22852
+ "I wanted to share my exceptional experience with {business}. The entire process was smooth and professional. The staff was knowledgeable and took the time to explain everything clearly. I'm extremely satisfied with the service and would definitely return.",
22853
+ "Outstanding service from {business}! From the initial consultation to the final delivery, everything exceeded my expectations. The team was responsive to my questions and made sure I was completely satisfied with the outcome.",
22854
+ "Excellent experience with {business}. The quality of work was exceptional and the customer service was top-notch. I appreciated the attention to detail and the follow-up communication. Highly recommend!",
22855
+ "Absolutely fantastic service! {business} went above and beyond my expectations. Professional, efficient, and genuinely cared about delivering the best possible outcome."
22767
22856
  ];
22768
- const commonNegative = [
22857
+ const commonPassives = [
22858
+ "My experience with {business} was good overall. The service was completed as requested, with only minor issues along the way. The staff was generally helpful, though response times could be improved. Met my expectations.",
22859
+ "Solid experience with {business}. Some aspects of the service were very good, while others were just okay. The pricing was reasonable, and the outcome was satisfactory. Room for improvement but generally positive.",
22860
+ "Decent service from {business}. The work was completed professionally, though not exceptional. Staff was courteous and the process was straightforward. Would consider using them again."
22861
+ ];
22862
+ const commonDetractors = [
22769
22863
  "I had several issues with my recent experience at {business}. The initial promise was not met, and when I tried to address my concerns, the customer service was lacking. The quality of service was below what I expected based on the pricing.",
22770
22864
  "Unfortunately, my experience with {business} was disappointing. There were delays in service delivery, and the communication was poor throughout the process. I hope they can improve their operations for future customers.",
22771
- "Not satisfied with the service from {business}. The staff seemed unprepared and the final result didn't match what was discussed initially. For the price paid, I expected much better quality and professionalism."
22772
- ];
22773
- const commonNeutral = [
22774
- "My experience with {business} was average. The service was completed as requested, but there were some minor issues along the way. The staff was generally helpful, though response times could be improved. Overall, it met basic expectations.",
22775
- "Mixed feelings about {business}. Some aspects of the service were good, while others fell short. The pricing was fair, but the execution could have been better. They have potential but need some improvements."
22865
+ "Not satisfied with the service from {business}. The staff seemed unprepared and the final result didn't match what was discussed initially. For the price paid, I expected much better quality and professionalism.",
22866
+ "Poor experience overall. {business} needs significant improvements in customer service and service delivery. Multiple issues that were not resolved satisfactorily."
22776
22867
  ];
22777
22868
  if (platformId === "goose") {
22778
- const petSpaDetailed = rating >= 4 ? [
22779
- "My experience at {business} was wonderful. The staff clearly has extensive training in animal care and grooming techniques. My pet was comfortable throughout the entire process, and the grooming results were exactly what I requested. The facility is clean and well-organized, and I appreciate the care they take with each animal."
22780
- ] : [
22869
+ const petSpaPromoters = [
22870
+ "My experience at {business} was absolutely wonderful. The staff clearly has extensive training in animal care and grooming techniques. My pet was comfortable throughout the entire process, and the grooming results exceeded what I requested. The facility is immaculate and well-organized, and I truly appreciate the exceptional care they take with each animal."
22871
+ ];
22872
+ const petSpaPassives = [
22873
+ "Good experience at {business}. The staff was knowledgeable and my pet was handled well. The grooming results were as requested, though the wait time was a bit longer than expected. Overall satisfied with the service."
22874
+ ];
22875
+ const petSpaDetractors = [
22781
22876
  "I had concerns about my pet's experience at {business}. While the grooming was completed, my animal seemed stressed afterward. The staff could benefit from additional training on handling anxious pets. The facility was clean, but the process could be more gentle and patient."
22782
22877
  ];
22783
- if (rating >= 4) return [...commonPositive, ...petSpaDetailed];
22784
- if (rating <= 2) return [...commonNegative, ...petSpaDetailed];
22878
+ if (rating >= 9) return [...commonPromoters, ...petSpaPromoters];
22879
+ if (rating >= 7) return [...commonPassives, ...petSpaPassives];
22880
+ return [...commonDetractors, ...petSpaDetractors];
22785
22881
  }
22786
22882
  if (platformId === "renterra") {
22787
- const rentalDetailed = rating >= 4 ? [
22788
- "Excellent equipment rental experience with {business}. The reservation process was straightforward, and the equipment was delivered on time in perfect working condition. The staff provided clear instructions for operation and maintenance. When we had a minor question during the rental period, they were quick to respond and helpful."
22789
- ] : [
22883
+ const rentalPromoters = [
22884
+ "Exceptional equipment rental experience with {business}! The reservation process was seamless, and the equipment was delivered ahead of schedule in perfect working condition. The staff provided comprehensive instructions and was incredibly responsive to our questions throughout the rental period."
22885
+ ];
22886
+ const rentalPassives = [
22887
+ "Solid rental experience with {business}. The equipment worked as expected and delivery was on time. Staff was helpful when contacted. Met our needs for the project, though nothing particularly stood out as exceptional."
22888
+ ];
22889
+ const rentalDetractors = [
22790
22890
  "Had some challenges with my rental from {business}. The equipment arrived later than scheduled, which impacted our project timeline. Additionally, the equipment showed signs of wear that weren't disclosed upfront. Customer service was slow to respond when we reported issues."
22791
22891
  ];
22792
- if (rating >= 4) return [...commonPositive, ...rentalDetailed];
22793
- if (rating <= 2) return [...commonNegative, ...rentalDetailed];
22892
+ if (rating >= 9) return [...commonPromoters, ...rentalPromoters];
22893
+ if (rating >= 7) return [...commonPassives, ...rentalPassives];
22894
+ return [...commonDetractors, ...rentalDetractors];
22794
22895
  }
22795
- if (rating >= 4) return commonPositive;
22796
- if (rating <= 2) return commonNegative;
22797
- return commonNeutral;
22896
+ if (rating >= 9) return commonPromoters;
22897
+ if (rating >= 7) return commonPassives;
22898
+ return commonDetractors;
22798
22899
  }
22799
22900
  const generateReviewAnalyticsData = ({
22800
22901
  platformData,
@@ -22820,36 +22921,55 @@ const generateReviewAnalyticsData = ({
22820
22921
  false,
22821
22922
  isAllTimeQuery || false
22822
22923
  );
22823
- const includeComparison = Boolean(queryParams.include_comparison);
22824
- if (includeComparison) {
22825
- const previousPeriodData = generateReviewPeriodData(
22826
- platformConfig,
22827
- scalingFactor,
22828
- true,
22829
- isAllTimeQuery || false
22830
- );
22831
- const comparison = calculateReviewComparison(
22832
- currentPeriodData,
22833
- previousPeriodData
22834
- );
22835
- const responseWithComparison = {
22836
- reviewSource: "google_business_profile",
22837
- current: currentPeriodData,
22838
- previous: previousPeriodData,
22839
- comparison
22840
- };
22841
- return responseWithComparison;
22842
- }
22843
- const basicResponse = {
22924
+ const previousPeriodData = generateReviewPeriodData(
22925
+ platformConfig,
22926
+ scalingFactor,
22927
+ true,
22928
+ // isPrevious = true
22929
+ isAllTimeQuery || false
22930
+ );
22931
+ const totalData = generateReviewPeriodData(
22932
+ platformConfig,
22933
+ 1,
22934
+ // No scaling for all-time
22935
+ false,
22936
+ true
22937
+ // isAllTime = true
22938
+ );
22939
+ const totalCountChange = currentPeriodData.totalCount - previousPeriodData.totalCount;
22940
+ const totalCountChangePercent = previousPeriodData.totalCount > 0 ? parseFloat(
22941
+ (totalCountChange / previousPeriodData.totalCount * 100).toFixed(1)
22942
+ ) : null;
22943
+ const averageRatingChange = parseFloat(
22944
+ (currentPeriodData.averageRating - previousPeriodData.averageRating).toFixed(1)
22945
+ );
22946
+ const averageRatingChangePercent = previousPeriodData.averageRating > 0 ? parseFloat(
22947
+ (averageRatingChange / previousPeriodData.averageRating * 100).toFixed(1)
22948
+ ) : 0;
22949
+ const fullResponse = {
22844
22950
  reviewSource: "google_business_profile",
22845
- totalCount: currentPeriodData.totalCount,
22846
- averageRating: currentPeriodData.averageRating,
22847
- ratingDistribution: currentPeriodData.ratingDistribution
22951
+ current: {
22952
+ totalCount: currentPeriodData.totalCount,
22953
+ averageRating: currentPeriodData.averageRating,
22954
+ ratingDistribution: currentPeriodData.ratingDistribution
22955
+ },
22956
+ previous: {
22957
+ totalCount: previousPeriodData.totalCount,
22958
+ averageRating: previousPeriodData.averageRating,
22959
+ ratingDistribution: previousPeriodData.ratingDistribution
22960
+ },
22961
+ total: totalData,
22962
+ comparison: {
22963
+ totalCountChange,
22964
+ totalCountChangePercent,
22965
+ averageRatingChange,
22966
+ averageRatingChangePercent
22967
+ }
22848
22968
  };
22849
- return basicResponse;
22969
+ return fullResponse;
22850
22970
  };
22851
22971
  function generateReviewPeriodData(platformConfig, scalingFactor, isPrevious = false, isAllTime = false) {
22852
- const baseMetrics = isAllTime ? getAllTimePlatformReviewMetrics(platformConfig) : getBasePlatformReviewMetrics$1(platformConfig);
22972
+ const baseMetrics = isAllTime ? getAllTimePlatformReviewMetrics(platformConfig) : getBasePlatformReviewMetrics(platformConfig);
22853
22973
  const scaledTotalCount = isAllTime ? baseMetrics.totalCount : Math.max(1, Math.round(baseMetrics.totalCount * scalingFactor));
22854
22974
  const performanceMultiplier = isPrevious ? 0.9 : 1;
22855
22975
  const adjustedTotalCount = Math.round(
@@ -22857,7 +22977,7 @@ function generateReviewPeriodData(platformConfig, scalingFactor, isPrevious = fa
22857
22977
  );
22858
22978
  const ratingDelta = isPrevious ? -0.2 : 0;
22859
22979
  const adjustedAverageRating = baseMetrics.averageRating + ratingDelta;
22860
- const ratingDistribution = generateRatingDistribution(
22980
+ const ratingDistribution = generateRatingDistribution$1(
22861
22981
  adjustedTotalCount,
22862
22982
  adjustedAverageRating
22863
22983
  );
@@ -22867,22 +22987,24 @@ function generateReviewPeriodData(platformConfig, scalingFactor, isPrevious = fa
22867
22987
  ratingDistribution
22868
22988
  };
22869
22989
  }
22870
- function getBasePlatformReviewMetrics$1(platformConfig) {
22990
+ function getBasePlatformReviewMetrics(platformConfig) {
22871
22991
  const industryMultipliers = {
22872
22992
  // Pet spa industry (Goose) - high customer satisfaction
22873
- "ACME Pet Spa": {
22874
- totalCount: 8,
22875
- // Much lower - reviews are rare compared to emails
22876
- averageRating: 4.6
22993
+ "Paws Pet Hotel": {
22994
+ totalCount: 12,
22995
+ // ~12 reviews per 30 days
22996
+ averageRating: 4.5
22877
22997
  },
22878
22998
  // Equipment rental (Renterra) - moderate review volume
22879
22999
  "Tomer's Rentals": {
22880
- totalCount: 5,
23000
+ totalCount: 8,
23001
+ // ~8 reviews per 30 days
22881
23002
  averageRating: 4.2
22882
23003
  },
22883
23004
  // Default platform
22884
23005
  default: {
22885
- totalCount: 6,
23006
+ totalCount: 10,
23007
+ // ~10 reviews per 30 days
22886
23008
  averageRating: 4.3
22887
23009
  }
22888
23010
  };
@@ -22892,27 +23014,28 @@ function getBasePlatformReviewMetrics$1(platformConfig) {
22892
23014
  function getAllTimePlatformReviewMetrics(platformConfig) {
22893
23015
  const industryMultipliers = {
22894
23016
  // Pet spa industry (Goose) - high customer satisfaction
22895
- "ACME Pet Spa": {
22896
- totalCount: 124,
22897
- // Much lower - reviews are rare compared to emails
22898
- averageRating: 4.3
22899
- // Slightly lower all-time average
23017
+ "Paws Pet Hotel": {
23018
+ totalCount: 40,
23019
+ // ~100 days of reviews (12 per 30 days)
23020
+ averageRating: 4.4
22900
23021
  },
22901
23022
  // Equipment rental (Renterra) - moderate review volume
22902
23023
  "Tomer's Rentals": {
22903
- totalCount: 78,
23024
+ totalCount: 27,
23025
+ // ~100 days of reviews (8 per 30 days)
22904
23026
  averageRating: 4.1
22905
23027
  },
22906
23028
  // Default platform
22907
23029
  default: {
22908
- totalCount: 95,
23030
+ totalCount: 33,
23031
+ // ~100 days of reviews (10 per 30 days)
22909
23032
  averageRating: 4.2
22910
23033
  }
22911
23034
  };
22912
23035
  const baseKey = platformConfig.name in industryMultipliers ? platformConfig.name : "default";
22913
23036
  return industryMultipliers[baseKey];
22914
23037
  }
22915
- function generateRatingDistribution(totalCount, targetAverage) {
23038
+ function generateRatingDistribution$1(totalCount, targetAverage) {
22916
23039
  const baseDistribution = {
22917
23040
  5: 0.6,
22918
23041
  // 60% 5-star
@@ -22953,28 +23076,6 @@ function adjustDistributionForAverage(baseDistribution, targetAverage) {
22953
23076
  }
22954
23077
  return adjusted;
22955
23078
  }
22956
- function calculateReviewComparison(currentPeriod, previousPeriod) {
22957
- const totalCountChange = currentPeriod.totalCount - previousPeriod.totalCount;
22958
- const totalCountChangePercent = previousPeriod.totalCount > 0 ? parseFloat(
22959
- (totalCountChange / previousPeriod.totalCount * 100).toFixed(1)
22960
- ) : null;
22961
- const averageRatingChange = currentPeriod.averageRating && previousPeriod.averageRating ? parseFloat(
22962
- (currentPeriod.averageRating - previousPeriod.averageRating).toFixed(
22963
- 2
22964
- )
22965
- ) : null;
22966
- const averageRatingChangePercent = averageRatingChange && previousPeriod.averageRating ? parseFloat(
22967
- (averageRatingChange / previousPeriod.averageRating * 100).toFixed(
22968
- 1
22969
- )
22970
- ) : null;
22971
- return {
22972
- totalCountChange,
22973
- totalCountChangePercent,
22974
- averageRatingChange,
22975
- averageRatingChangePercent
22976
- };
22977
- }
22978
23079
  const generateFeedbackAnalyticsData = ({
22979
23080
  platformData,
22980
23081
  queryParams
@@ -22999,38 +23100,86 @@ const generateFeedbackAnalyticsData = ({
22999
23100
  false,
23000
23101
  isAllTimeQuery
23001
23102
  );
23002
- if (queryParams.include_comparison) {
23003
- const previousPeriodData = generateFeedbackPeriodData(
23004
- platformConfig,
23005
- scalingFactor,
23006
- true,
23007
- isAllTimeQuery
23008
- );
23009
- const comparison = calculateFeedbackComparison(
23010
- currentPeriodData,
23011
- previousPeriodData
23012
- );
23013
- const responseWithComparison = {
23014
- current: currentPeriodData,
23015
- previous: previousPeriodData,
23016
- comparison
23017
- };
23018
- return responseWithComparison;
23019
- }
23020
- const basicResponse = {
23021
- totalSent: currentPeriodData.totalSent,
23022
- totalResponded: currentPeriodData.totalResponded,
23023
- averageRating: currentPeriodData.averageRating,
23024
- responseRate: currentPeriodData.responseRate,
23025
- positiveResponses: currentPeriodData.positiveResponses,
23026
- negativeResponses: currentPeriodData.negativeResponses,
23027
- passiveResponses: currentPeriodData.passiveResponses,
23028
- npsScore: currentPeriodData.npsScore
23103
+ const previousPeriodData = generateFeedbackPeriodData(
23104
+ platformConfig,
23105
+ scalingFactor,
23106
+ true,
23107
+ // isPrevious = true
23108
+ isAllTimeQuery
23109
+ );
23110
+ const totalData = generateFeedbackPeriodData(
23111
+ platformConfig,
23112
+ 1,
23113
+ // No scaling for all-time
23114
+ false,
23115
+ true
23116
+ // isAllTime = true
23117
+ );
23118
+ const totalSentChange = currentPeriodData.totalSent - previousPeriodData.totalSent;
23119
+ const totalSentChangePercent = previousPeriodData.totalSent > 0 ? parseFloat(
23120
+ (totalSentChange / previousPeriodData.totalSent * 100).toFixed(1)
23121
+ ) : null;
23122
+ const npsScoreChange = currentPeriodData.npsScore - previousPeriodData.npsScore;
23123
+ const npsScoreChangePercent = previousPeriodData.npsScore !== 0 ? parseFloat(
23124
+ (npsScoreChange / Math.abs(previousPeriodData.npsScore) * 100).toFixed(1)
23125
+ ) : null;
23126
+ const responseRateChange = parseFloat(
23127
+ (currentPeriodData.responseRate - previousPeriodData.responseRate).toFixed(
23128
+ 1
23129
+ )
23130
+ );
23131
+ const responseRateChangePercent = previousPeriodData.responseRate > 0 ? parseFloat(
23132
+ (responseRateChange / previousPeriodData.responseRate * 100).toFixed(1)
23133
+ ) : 0;
23134
+ const totalRespondedChange = currentPeriodData.totalResponded - previousPeriodData.totalResponded;
23135
+ const totalRespondedChangePercent = previousPeriodData.totalResponded > 0 ? parseFloat(
23136
+ (totalRespondedChange / previousPeriodData.totalResponded * 100).toFixed(1)
23137
+ ) : null;
23138
+ const averageRatingChange = parseFloat(
23139
+ (currentPeriodData.averageRating - previousPeriodData.averageRating).toFixed(1)
23140
+ );
23141
+ const averageRatingChangePercent = previousPeriodData.averageRating > 0 ? parseFloat(
23142
+ (averageRatingChange / previousPeriodData.averageRating * 100).toFixed(1)
23143
+ ) : 0;
23144
+ const fullResponse = {
23145
+ current: {
23146
+ totalSent: currentPeriodData.totalSent,
23147
+ totalResponded: currentPeriodData.totalResponded,
23148
+ averageRating: currentPeriodData.averageRating,
23149
+ responseRate: currentPeriodData.responseRate,
23150
+ positiveResponses: currentPeriodData.positiveResponses,
23151
+ negativeResponses: currentPeriodData.negativeResponses,
23152
+ passiveResponses: currentPeriodData.passiveResponses,
23153
+ npsScore: currentPeriodData.npsScore
23154
+ },
23155
+ previous: {
23156
+ totalSent: previousPeriodData.totalSent,
23157
+ totalResponded: previousPeriodData.totalResponded,
23158
+ averageRating: previousPeriodData.averageRating,
23159
+ responseRate: previousPeriodData.responseRate,
23160
+ positiveResponses: previousPeriodData.positiveResponses,
23161
+ negativeResponses: previousPeriodData.negativeResponses,
23162
+ passiveResponses: previousPeriodData.passiveResponses,
23163
+ npsScore: previousPeriodData.npsScore
23164
+ },
23165
+ total: totalData,
23166
+ comparison: {
23167
+ totalSentChange,
23168
+ totalSentChangePercent,
23169
+ npsScoreChange,
23170
+ npsScoreChangePercent,
23171
+ responseRateChange,
23172
+ responseRateChangePercent,
23173
+ totalRespondedChange,
23174
+ totalRespondedChangePercent,
23175
+ averageRatingChange,
23176
+ averageRatingChangePercent
23177
+ }
23029
23178
  };
23030
- return basicResponse;
23179
+ return fullResponse;
23031
23180
  };
23032
23181
  function generateFeedbackPeriodData(platformConfig, scalingFactor, isPrevious = false, isAllTime = false) {
23033
- const baseMetrics = isAllTime ? getAllTimePlatformFeedbackMetrics(platformConfig) : getBasePlatformFeedbackMetrics$1(platformConfig);
23182
+ const baseMetrics = isAllTime ? getAllTimePlatformFeedbackMetrics(platformConfig) : getBasePlatformFeedbackMetrics(platformConfig);
23034
23183
  const scaledTotalSent = isAllTime ? baseMetrics.totalSent : Math.max(1, Math.round(baseMetrics.totalSent * scalingFactor));
23035
23184
  const performanceMultiplier = isPrevious ? 0.82 : 1;
23036
23185
  const adjustedTotalSent = Math.round(scaledTotalSent * performanceMultiplier);
@@ -23068,10 +23217,10 @@ function generateFeedbackPeriodData(platformConfig, scalingFactor, isPrevious =
23068
23217
  // real NPS score
23069
23218
  };
23070
23219
  }
23071
- function getBasePlatformFeedbackMetrics$1(platformConfig) {
23220
+ function getBasePlatformFeedbackMetrics(platformConfig) {
23072
23221
  const industryMultipliers = {
23073
23222
  // Pet spa industry (Goose) - high engagement, positive feedback
23074
- "ACME Pet Spa": {
23223
+ "Paws Pet Hotel": {
23075
23224
  totalSent: 1250,
23076
23225
  // Much higher - businesses send way more emails than get reviews
23077
23226
  responseRate: 42,
@@ -23114,7 +23263,7 @@ function getBasePlatformFeedbackMetrics$1(platformConfig) {
23114
23263
  function getAllTimePlatformFeedbackMetrics(platformConfig) {
23115
23264
  const industryMultipliers = {
23116
23265
  // Pet spa industry (Goose) - high engagement, positive feedback
23117
- "ACME Pet Spa": {
23266
+ "Paws Pet Hotel": {
23118
23267
  totalSent: 18500,
23119
23268
  // WAY higher - businesses send tons of emails over time
23120
23269
  responseRate: 39,
@@ -23163,48 +23312,6 @@ function calculateAverageFromNPS(promoters, passives, detractors) {
23163
23312
  const weightedSum = promoters * promoterAverage + passives * passiveAverage + detractors * detractorAverage;
23164
23313
  return weightedSum / totalResponses;
23165
23314
  }
23166
- function calculateFeedbackComparison(currentPeriod, previousPeriod) {
23167
- const totalSentChange = currentPeriod.totalSent - previousPeriod.totalSent;
23168
- const totalSentChangePercent = previousPeriod.totalSent > 0 ? parseFloat(
23169
- (totalSentChange / previousPeriod.totalSent * 100).toFixed(1)
23170
- ) : null;
23171
- const totalRespondedChange = currentPeriod.totalResponded - previousPeriod.totalResponded;
23172
- const totalRespondedChangePercent = previousPeriod.totalResponded > 0 ? parseFloat(
23173
- (totalRespondedChange / previousPeriod.totalResponded * 100).toFixed(1)
23174
- ) : null;
23175
- const averageRatingChange = currentPeriod.averageRating && previousPeriod.averageRating ? parseFloat(
23176
- (currentPeriod.averageRating - previousPeriod.averageRating).toFixed(
23177
- 2
23178
- )
23179
- ) : null;
23180
- const averageRatingChangePercent = averageRatingChange && previousPeriod.averageRating ? parseFloat(
23181
- (averageRatingChange / previousPeriod.averageRating * 100).toFixed(
23182
- 1
23183
- )
23184
- ) : null;
23185
- const responseRateChange = currentPeriod.responseRate - previousPeriod.responseRate;
23186
- const responseRateChangePercent = previousPeriod.responseRate > 0 ? parseFloat(
23187
- (responseRateChange / previousPeriod.responseRate * 100).toFixed(1)
23188
- ) : null;
23189
- const npsScoreChange = currentPeriod.npsScore - previousPeriod.npsScore;
23190
- const npsScoreChangePercent = previousPeriod.npsScore !== 0 ? parseFloat(
23191
- (npsScoreChange / Math.abs(previousPeriod.npsScore) * 100).toFixed(
23192
- 1
23193
- )
23194
- ) : null;
23195
- return {
23196
- totalSentChange,
23197
- totalSentChangePercent,
23198
- totalRespondedChange,
23199
- totalRespondedChangePercent,
23200
- averageRatingChange,
23201
- averageRatingChangePercent,
23202
- responseRateChange: parseFloat(responseRateChange.toFixed(1)),
23203
- responseRateChangePercent,
23204
- npsScoreChange,
23205
- npsScoreChangePercent
23206
- };
23207
- }
23208
23315
  const generateAuditLogData = ({
23209
23316
  startDate,
23210
23317
  endDate,
@@ -23212,7 +23319,8 @@ const generateAuditLogData = ({
23212
23319
  cursor,
23213
23320
  limit,
23214
23321
  searchTerm,
23215
- orderBy
23322
+ orderBy,
23323
+ directFeedbackEnabled = true
23216
23324
  }) => {
23217
23325
  const queryStartDate = startDate ? new Date(startDate) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
23218
23326
  const queryEndDate = endDate ? new Date(endDate) : /* @__PURE__ */ new Date();
@@ -23227,7 +23335,8 @@ const generateAuditLogData = ({
23227
23335
  const allLogs = generateAuditLogPool(
23228
23336
  queryStartDate,
23229
23337
  queryEndDate,
23230
- scaledLogCount
23338
+ scaledLogCount,
23339
+ directFeedbackEnabled
23231
23340
  );
23232
23341
  let filteredLogs = allLogs;
23233
23342
  if (startDate || endDate) {
@@ -23266,28 +23375,33 @@ const generateAuditLogData = ({
23266
23375
  nextCursor
23267
23376
  };
23268
23377
  };
23269
- function generateAuditLogPool(startDate, endDate, count) {
23378
+ function generateAuditLogPool(startDate, endDate, count, directFeedbackEnabled) {
23270
23379
  const logs = [];
23271
23380
  const timeSpan = endDate.getTime() - startDate.getTime();
23272
- const eventSequences = [
23273
- // Survey flow
23381
+ let eventSequences = [
23382
+ // Private Feedback flow
23274
23383
  {
23275
23384
  type: "SURVEY_REQUEST",
23276
23385
  channel: "EMAIL",
23277
23386
  statuses: ["SENT", "DELIVERED", "OPENED", "COMPLETED"],
23278
23387
  messages: [
23279
- "Survey request sent to customer",
23280
- "Survey request delivered to customer",
23281
- "Customer opened survey request",
23282
- "Survey completed by customer"
23283
- ]
23388
+ "Private feedback request sent to customer",
23389
+ "Private feedback request delivered to customer",
23390
+ "Customer opened private feedback request",
23391
+ "Private feedback completed by customer"
23392
+ ],
23393
+ isDirectFeedback: true
23284
23394
  },
23285
- // Review flow
23395
+ // Google Review flow
23286
23396
  {
23287
23397
  type: "REVIEW_REQUEST",
23288
23398
  channel: "SMS",
23289
23399
  statuses: ["SENT", "DELIVERED"],
23290
- messages: ["Review request sent to", "Review request delivered to"]
23400
+ messages: [
23401
+ "Google review request sent to",
23402
+ "Google review request delivered to"
23403
+ ],
23404
+ isDirectFeedback: false
23291
23405
  },
23292
23406
  // Google review flow
23293
23407
  {
@@ -23297,22 +23411,34 @@ function generateAuditLogPool(startDate, endDate, count) {
23297
23411
  messages: [
23298
23412
  "New Google review received from",
23299
23413
  "Response sent to Google review from"
23300
- ]
23414
+ ],
23415
+ isDirectFeedback: false
23301
23416
  },
23302
23417
  // Reminder flows
23303
23418
  {
23304
23419
  type: "SURVEY_REMINDER",
23305
23420
  channel: "EMAIL",
23306
23421
  statuses: ["SENT", "DELIVERED"],
23307
- messages: ["Survey reminder sent to", "Survey reminder delivered to"]
23422
+ messages: [
23423
+ "Private feedback reminder sent to",
23424
+ "Private feedback reminder delivered to"
23425
+ ],
23426
+ isDirectFeedback: true
23308
23427
  },
23309
23428
  {
23310
23429
  type: "REVIEW_REMINDER",
23311
23430
  channel: "SMS",
23312
23431
  statuses: ["SENT", "DELIVERED"],
23313
- messages: ["Review reminder sent to", "Review reminder delivered to"]
23432
+ messages: [
23433
+ "Google review reminder sent to",
23434
+ "Google review reminder delivered to"
23435
+ ],
23436
+ isDirectFeedback: false
23314
23437
  }
23315
23438
  ];
23439
+ if (!directFeedbackEnabled) {
23440
+ eventSequences = eventSequences.filter((seq) => !seq.isDirectFeedback);
23441
+ }
23316
23442
  for (let i2 = 0; i2 < count; i2++) {
23317
23443
  const sequence = f.helpers.arrayElement(eventSequences);
23318
23444
  const statusIndex = f.number.int({
@@ -23352,125 +23478,112 @@ const generateReviewTimeSeriesData = ({
23352
23478
  platformData,
23353
23479
  queryParams
23354
23480
  }) => {
23355
- const platformConfig = getPlatformConfig(
23356
- platformData.businessesMe?.platform_id
23357
- );
23481
+ const analyticsData = generateReviewAnalyticsData({
23482
+ platformData,
23483
+ queryParams
23484
+ });
23358
23485
  const { requestedDays, startDate, endDate } = parseDateRange(
23359
23486
  queryParams.start_date,
23360
23487
  queryParams.end_date,
23361
23488
  30
23362
23489
  );
23363
- const baseMetrics = getBasePlatformReviewMetrics(platformConfig);
23364
- const basePeriodDays = 30;
23365
- const scalingFactor = requestedDays / basePeriodDays;
23366
- const totalScaledReviews = Math.max(
23367
- 1,
23368
- Math.round(baseMetrics.totalReviews * scalingFactor)
23369
- );
23490
+ const isAllTimeQuery = !queryParams.start_date || !queryParams.end_date || queryParams.start_date.startsWith("2020-");
23370
23491
  let intervalDays = 7;
23371
23492
  if (requestedDays <= 30)
23372
23493
  intervalDays = 1;
23373
23494
  else if (requestedDays <= 90) intervalDays = 3;
23374
23495
  const numDataPoints = Math.max(1, Math.ceil(requestedDays / intervalDays));
23375
23496
  const dataPoints = generateReviewTimeSeriesPoints({
23376
- totalScaledReviews,
23497
+ analyticsData,
23377
23498
  startDate,
23378
23499
  endDate,
23379
23500
  numDataPoints,
23380
- intervalDays
23501
+ intervalDays,
23502
+ isAllTimeQuery
23381
23503
  });
23382
23504
  return {
23383
23505
  data: dataPoints
23384
23506
  };
23385
23507
  };
23386
23508
  function generateReviewTimeSeriesPoints({
23387
- totalScaledReviews,
23509
+ analyticsData,
23388
23510
  startDate,
23389
23511
  endDate,
23390
23512
  numDataPoints,
23391
- intervalDays
23513
+ intervalDays,
23514
+ isAllTimeQuery
23392
23515
  }) {
23516
+ const reviewCounts = distributeReviewsAcrossPoints(
23517
+ analyticsData.current.totalCount,
23518
+ numDataPoints
23519
+ );
23520
+ const targetRating = analyticsData.total.averageRating ?? 4.2;
23521
+ const startingRating = targetRating - 0.3;
23522
+ const endingRating = targetRating + 0.15;
23393
23523
  const dataPoints = [];
23394
23524
  let currentDate = new Date(startDate);
23395
- let cumulativeReviews = 0;
23396
- const basePointReviews = totalScaledReviews / numDataPoints;
23397
- const targetRating = 4.2;
23398
- const startingRating = 4;
23399
- const endingRating = 4.2;
23525
+ let runningReviewCount = 0;
23526
+ let runningWeightedSum = 0;
23527
+ const targetEndCumulative = targetRating;
23528
+ const ratingChange = analyticsData.comparison.averageRatingChange ?? 0;
23529
+ const targetStartCumulative = targetRating - ratingChange;
23400
23530
  for (let i2 = 0; i2 < numDataPoints; i2++) {
23401
23531
  const pointDateStr = currentDate.toISOString().split("T")[0];
23402
- let currentPointReviews;
23403
- let currentPointRating;
23404
- if (i2 < numDataPoints - 1) {
23405
- currentPointReviews = Math.round(
23406
- basePointReviews * (1 + (Math.random() - 0.5) * 0.5)
23407
- );
23408
- const progress = i2 / (numDataPoints - 1);
23409
- currentPointRating = startingRating + (endingRating - startingRating) * Math.pow(progress, 1.3);
23410
- currentPointRating *= 1 + (Math.random() - 0.5) * 0.05;
23532
+ const currentPointReviews = reviewCounts[i2];
23533
+ const progress = numDataPoints > 1 ? i2 / (numDataPoints - 1) : 1;
23534
+ let currentPointRating = startingRating + (endingRating - startingRating) * Math.pow(progress, 1.3);
23535
+ currentPointRating += (Math.random() - 0.5) * 0.08;
23536
+ currentPointRating = Math.max(1, Math.min(5, currentPointRating));
23537
+ let cumulativeAverageRating;
23538
+ if (isAllTimeQuery) {
23539
+ runningReviewCount += currentPointReviews;
23540
+ runningWeightedSum += currentPointReviews * currentPointRating;
23541
+ cumulativeAverageRating = runningReviewCount > 0 ? parseFloat((runningWeightedSum / runningReviewCount).toFixed(2)) : null;
23411
23542
  } else {
23412
- currentPointReviews = Math.max(0, totalScaledReviews - cumulativeReviews);
23413
- currentPointRating = targetRating;
23543
+ const cumulativeProgress = numDataPoints > 1 ? i2 / (numDataPoints - 1) : 1;
23544
+ cumulativeAverageRating = parseFloat(
23545
+ (targetStartCumulative + (targetEndCumulative - targetStartCumulative) * cumulativeProgress).toFixed(2)
23546
+ );
23414
23547
  }
23415
- currentPointReviews = Math.max(0, currentPointReviews);
23416
- currentPointRating = Math.max(1, Math.min(5, currentPointRating));
23417
- cumulativeReviews += currentPointReviews;
23418
23548
  const { promoterCount, detractorCount } = calculatePromoterDetractorSplit(
23419
23549
  currentPointReviews,
23420
23550
  currentPointRating
23421
23551
  );
23552
+ const ratingDistribution = generateRatingDistribution(
23553
+ currentPointReviews,
23554
+ currentPointRating
23555
+ );
23422
23556
  dataPoints.push({
23423
23557
  date: pointDateStr,
23424
23558
  totalReviews: currentPointReviews,
23425
23559
  averageRating: currentPointReviews > 0 ? parseFloat(currentPointRating.toFixed(1)) : null,
23560
+ cumulativeAverageRating,
23426
23561
  promoterCount,
23427
- detractorCount
23562
+ detractorCount,
23563
+ ratingDistribution
23428
23564
  });
23429
23565
  currentDate.setDate(currentDate.getDate() + intervalDays);
23430
23566
  if (currentDate > endDate && i2 < numDataPoints - 1) {
23431
23567
  currentDate = new Date(endDate);
23432
- if (dataPoints.length > 0 && dataPoints[dataPoints.length - 1].date === currentDate.toISOString().split("T")[0]) {
23433
- break;
23434
- }
23435
23568
  }
23436
23569
  }
23437
23570
  return dataPoints;
23438
23571
  }
23439
- function getBasePlatformReviewMetrics(platformConfig) {
23440
- const industryMetrics = {
23441
- // Pet spa industry (Goose) - high customer satisfaction, more reviews
23442
- "ACME Pet Spa": {
23443
- totalReviews: 52,
23444
- // Slightly higher than analytics factory
23445
- averageRating: 4.5,
23446
- promoterRate: 0.65,
23447
- // 65% 5-star
23448
- detractorRate: 0.15
23449
- // 15% 1-3 star
23450
- },
23451
- // Equipment rental (Renterra) - moderate review volume, good satisfaction
23452
- "Tomer's Rentals": {
23453
- totalReviews: 31,
23454
- averageRating: 4.2,
23455
- promoterRate: 0.55,
23456
- // 55% 5-star
23457
- detractorRate: 0.2
23458
- // 20% 1-3 star
23459
- },
23460
- // Default platform
23461
- default: {
23462
- totalReviews: 95,
23463
- // Match all-time analytics exactly
23464
- averageRating: 4.2,
23465
- // Match all-time analytics exactly
23466
- promoterRate: 0.6,
23467
- // 60% 5-star
23468
- detractorRate: 0.18
23469
- // 18% 1-3 star
23470
- }
23471
- };
23472
- const baseKey = platformConfig.name in industryMetrics ? platformConfig.name : "default";
23473
- return industryMetrics[baseKey];
23572
+ function distributeReviewsAcrossPoints(totalReviews, numDataPoints) {
23573
+ if (numDataPoints === 0) return [];
23574
+ if (numDataPoints === 1) return [totalReviews];
23575
+ const counts = [];
23576
+ let remainingReviews = totalReviews;
23577
+ const basePerPoint = totalReviews / numDataPoints;
23578
+ for (let i2 = 0; i2 < numDataPoints - 1; i2++) {
23579
+ const variation = 0.7 + Math.random() * 0.6;
23580
+ const count = Math.round(basePerPoint * variation);
23581
+ const actualCount = Math.max(0, Math.min(count, remainingReviews));
23582
+ counts.push(actualCount);
23583
+ remainingReviews -= actualCount;
23584
+ }
23585
+ counts.push(Math.max(0, remainingReviews));
23586
+ return counts;
23474
23587
  }
23475
23588
  function calculatePromoterDetractorSplit(totalReviews, averageRating) {
23476
23589
  if (totalReviews === 0) {
@@ -23497,33 +23610,80 @@ function calculatePromoterDetractorSplit(totalReviews, averageRating) {
23497
23610
  const detractorCount = Math.round(totalReviews * detractorRate);
23498
23611
  return { promoterCount, detractorCount };
23499
23612
  }
23613
+ function generateRatingDistribution(totalReviews, averageRating) {
23614
+ if (totalReviews === 0) {
23615
+ return { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
23616
+ }
23617
+ let distribution = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
23618
+ if (averageRating >= 4.5) {
23619
+ distribution[5] = Math.round(totalReviews * 0.6);
23620
+ distribution[4] = Math.round(totalReviews * 0.3);
23621
+ distribution[3] = Math.round(totalReviews * 0.08);
23622
+ distribution[2] = Math.round(totalReviews * 0.015);
23623
+ distribution[1] = Math.round(totalReviews * 5e-3);
23624
+ } else if (averageRating >= 4) {
23625
+ distribution[5] = Math.round(totalReviews * 0.45);
23626
+ distribution[4] = Math.round(totalReviews * 0.35);
23627
+ distribution[3] = Math.round(totalReviews * 0.15);
23628
+ distribution[2] = Math.round(totalReviews * 0.04);
23629
+ distribution[1] = Math.round(totalReviews * 0.01);
23630
+ } else if (averageRating >= 3.5) {
23631
+ distribution[5] = Math.round(totalReviews * 0.3);
23632
+ distribution[4] = Math.round(totalReviews * 0.35);
23633
+ distribution[3] = Math.round(totalReviews * 0.25);
23634
+ distribution[2] = Math.round(totalReviews * 0.07);
23635
+ distribution[1] = Math.round(totalReviews * 0.03);
23636
+ } else {
23637
+ distribution[5] = Math.round(totalReviews * 0.15);
23638
+ distribution[4] = Math.round(totalReviews * 0.25);
23639
+ distribution[3] = Math.round(totalReviews * 0.35);
23640
+ distribution[2] = Math.round(totalReviews * 0.15);
23641
+ distribution[1] = Math.round(totalReviews * 0.1);
23642
+ }
23643
+ Object.keys(distribution).forEach((rating) => {
23644
+ const key = parseInt(rating);
23645
+ const variation = Math.round(
23646
+ distribution[key] * (Math.random() - 0.5) * 0.2
23647
+ );
23648
+ distribution[key] = Math.max(0, distribution[key] + variation);
23649
+ });
23650
+ const currentTotal = Object.values(distribution).reduce(
23651
+ (sum, count) => sum + count,
23652
+ 0
23653
+ );
23654
+ const difference = totalReviews - currentTotal;
23655
+ if (difference !== 0) {
23656
+ const mostCommonRating = Object.entries(distribution).reduce(
23657
+ (max, [rating, count]) => count > distribution[max] ? rating : max,
23658
+ "5"
23659
+ );
23660
+ distribution[mostCommonRating] = Math.max(
23661
+ 0,
23662
+ distribution[mostCommonRating] + difference
23663
+ );
23664
+ }
23665
+ return distribution;
23666
+ }
23500
23667
  const generateFeedbackTimeSeriesData = ({
23501
23668
  platformData,
23502
23669
  queryParams
23503
23670
  }) => {
23504
- const platformConfig = getPlatformConfig(
23505
- platformData.businessesMe?.platform_id
23506
- );
23671
+ const analyticsData = generateFeedbackAnalyticsData({
23672
+ platformData,
23673
+ queryParams
23674
+ });
23507
23675
  const { requestedDays, startDate, endDate } = parseDateRange(
23508
23676
  queryParams.start_date,
23509
23677
  queryParams.end_date,
23510
23678
  30
23511
23679
  );
23512
- const baseMetrics = getBasePlatformFeedbackMetrics(platformConfig);
23513
- const basePeriodDays = 30;
23514
- const scalingFactor = requestedDays / basePeriodDays;
23515
- const totalScaledSent = Math.max(
23516
- 1,
23517
- Math.round(baseMetrics.totalSent * scalingFactor)
23518
- );
23519
23680
  let intervalDays = 7;
23520
23681
  if (requestedDays <= 30)
23521
23682
  intervalDays = 1;
23522
23683
  else if (requestedDays <= 90) intervalDays = 3;
23523
23684
  const numDataPoints = Math.max(1, Math.ceil(requestedDays / intervalDays));
23524
23685
  const dataPoints = generateFeedbackTimeSeriesPoints({
23525
- totalScaledSent,
23526
- baseMetrics,
23686
+ analyticsData,
23527
23687
  startDate,
23528
23688
  endDate,
23529
23689
  numDataPoints,
@@ -23533,52 +23693,54 @@ const generateFeedbackTimeSeriesData = ({
23533
23693
  data: dataPoints
23534
23694
  };
23535
23695
  };
23696
+ function distributeFeedbackAcrossPoints(total, numDataPoints) {
23697
+ if (numDataPoints === 0) return [];
23698
+ if (numDataPoints === 1) return [total];
23699
+ const counts = [];
23700
+ let remaining = total;
23701
+ const basePerPoint = total / numDataPoints;
23702
+ for (let i2 = 0; i2 < numDataPoints - 1; i2++) {
23703
+ const variation = 0.7 + Math.random() * 0.6;
23704
+ const count = Math.round(basePerPoint * variation);
23705
+ const actualCount = Math.max(0, Math.min(count, remaining));
23706
+ counts.push(actualCount);
23707
+ remaining -= actualCount;
23708
+ }
23709
+ counts.push(Math.max(0, remaining));
23710
+ return counts;
23711
+ }
23536
23712
  function generateFeedbackTimeSeriesPoints({
23537
- totalScaledSent,
23538
- baseMetrics,
23713
+ analyticsData,
23539
23714
  startDate,
23540
23715
  endDate,
23541
23716
  numDataPoints,
23542
23717
  intervalDays
23543
23718
  }) {
23719
+ const sentCounts = distributeFeedbackAcrossPoints(
23720
+ analyticsData.current.totalSent,
23721
+ numDataPoints
23722
+ );
23723
+ const respondedCounts = distributeFeedbackAcrossPoints(
23724
+ analyticsData.current.totalResponded,
23725
+ numDataPoints
23726
+ );
23727
+ const targetEndNPS = analyticsData.total.npsScore;
23728
+ const npsChange = analyticsData.comparison.npsScoreChange ?? 0;
23729
+ const targetStartNPS = targetEndNPS - npsChange;
23544
23730
  const dataPoints = [];
23545
23731
  let currentDate = new Date(startDate);
23546
- let cumulativeSent = 0;
23547
- const basePointSent = totalScaledSent / numDataPoints;
23548
- const startingResponseRate = baseMetrics.responseRate * 0.75;
23549
- const endingResponseRate = baseMetrics.responseRate * 1.15;
23550
- const startingNPS = 30;
23551
- const endingNPS = 42;
23552
23732
  for (let i2 = 0; i2 < numDataPoints; i2++) {
23553
23733
  const pointDateStr = currentDate.toISOString().split("T")[0];
23554
- let currentPointSent;
23555
- let currentPointResponseRate;
23556
- let currentPointNPS;
23557
- if (i2 < numDataPoints - 1) {
23558
- currentPointSent = Math.round(
23559
- basePointSent * (1 + (Math.random() - 0.5) * 0.5)
23560
- );
23561
- const progress = i2 / (numDataPoints - 1);
23562
- currentPointResponseRate = startingResponseRate + (endingResponseRate - startingResponseRate) * Math.pow(progress, 1.3);
23563
- currentPointNPS = startingNPS + (endingNPS - startingNPS) * Math.pow(progress, 1.2);
23564
- currentPointResponseRate *= 1 + (Math.random() - 0.5) * 0.1;
23565
- currentPointNPS *= 1 + (Math.random() - 0.5) * 0.1;
23566
- } else {
23567
- currentPointSent = Math.max(0, totalScaledSent - cumulativeSent);
23568
- currentPointResponseRate = baseMetrics.responseRate;
23569
- currentPointNPS = 42;
23570
- }
23571
- currentPointSent = Math.max(0, currentPointSent);
23572
- currentPointResponseRate = Math.max(
23573
- 5,
23574
- Math.min(80, currentPointResponseRate)
23575
- );
23734
+ const currentPointSent = sentCounts[i2];
23735
+ const currentPointResponded = respondedCounts[i2];
23736
+ const progress = numDataPoints > 1 ? i2 / (numDataPoints - 1) : 1;
23737
+ let currentPointNPS = targetStartNPS + (targetEndNPS - targetStartNPS) * Math.pow(progress, 1.2);
23738
+ currentPointNPS += (Math.random() - 0.5) * 8;
23576
23739
  currentPointNPS = Math.max(-100, Math.min(100, currentPointNPS));
23577
- cumulativeSent += currentPointSent;
23578
- const currentPointResponded = Math.round(
23579
- currentPointSent * (currentPointResponseRate / 100)
23580
- );
23581
23740
  const { promoterCount, detractorCount, passiveCount } = calculateNPSBreakdown(currentPointResponded, currentPointNPS);
23741
+ const cumulativeNPS = parseFloat(
23742
+ (targetStartNPS + (targetEndNPS - targetStartNPS) * progress).toFixed(1)
23743
+ );
23582
23744
  dataPoints.push({
23583
23745
  date: pointDateStr,
23584
23746
  totalSent: currentPointSent,
@@ -23586,56 +23748,16 @@ function generateFeedbackTimeSeriesPoints({
23586
23748
  promoterCount,
23587
23749
  detractorCount,
23588
23750
  passiveCount,
23589
- npsScore: parseFloat(currentPointNPS.toFixed(1))
23590
- // Use the target NPS (30 42)
23751
+ npsScore: cumulativeNPS
23752
+ // Use cumulative NPS for the chart line
23591
23753
  });
23592
23754
  currentDate.setDate(currentDate.getDate() + intervalDays);
23593
23755
  if (currentDate > endDate && i2 < numDataPoints - 1) {
23594
23756
  currentDate = new Date(endDate);
23595
- if (dataPoints.length > 0 && dataPoints[dataPoints.length - 1].date === currentDate.toISOString().split("T")[0]) {
23596
- break;
23597
- }
23598
23757
  }
23599
23758
  }
23600
23759
  return dataPoints;
23601
23760
  }
23602
- function getBasePlatformFeedbackMetrics(platformConfig) {
23603
- const industryMetrics = {
23604
- // Pet spa industry (Goose) - high engagement, positive feedback
23605
- "ACME Pet Spa": {
23606
- totalSent: 1200,
23607
- // Much higher - realistic email volume
23608
- responseRate: 44,
23609
- // 44% response rate
23610
- targetNPS: 42,
23611
- // Match the all-time NPS score (42)
23612
- promoterRate: 0.78
23613
- // 78% promoters
23614
- },
23615
- // Equipment rental (Renterra) - moderate engagement
23616
- "Tomer's Rentals": {
23617
- totalSent: 950,
23618
- responseRate: 37,
23619
- // 37% response rate
23620
- targetNPS: 55,
23621
- // Good NPS for B2B
23622
- promoterRate: 0.72
23623
- // 72% promoters
23624
- },
23625
- // Default platform
23626
- default: {
23627
- totalSent: 1050,
23628
- responseRate: 40,
23629
- // 40% response rate
23630
- targetNPS: 60,
23631
- // Good overall NPS
23632
- promoterRate: 0.75
23633
- // 75% promoters
23634
- }
23635
- };
23636
- const baseKey = platformConfig.name in industryMetrics ? platformConfig.name : "default";
23637
- return industryMetrics[baseKey];
23638
- }
23639
23761
  function calculateNPSBreakdown(totalResponses, targetNPS) {
23640
23762
  if (totalResponses === 0) {
23641
23763
  return { promoterCount: 0, detractorCount: 0, passiveCount: 0 };
@@ -23666,7 +23788,8 @@ function calculateNPSBreakdown(totalResponses, targetNPS) {
23666
23788
  };
23667
23789
  }
23668
23790
  const mockStore = {
23669
- reputationConfig: null
23791
+ reputationConfig: null,
23792
+ partnerLocations: null
23670
23793
  };
23671
23794
  const reputationHandlers = [
23672
23795
  // External OAuth - GBP Connection
@@ -23681,14 +23804,51 @@ const reputationHandlers = [
23681
23804
  // Partner Locations
23682
23805
  http.get(`${HOSTNAME}/api/partner-locations`, () => {
23683
23806
  const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
23807
+ const locations = mockStore.partnerLocations || platformData.reputationData?.partnerLocations || [];
23684
23808
  return HttpResponse.json({
23685
23809
  success: true,
23686
23810
  message: "Success (Sandbox)",
23687
23811
  data: {
23688
- locations: platformData.reputationData?.partnerLocations || []
23812
+ locations
23689
23813
  }
23690
23814
  });
23691
23815
  }),
23816
+ // Update Partner Location (for Google Business Profile mapping)
23817
+ http.put(
23818
+ `${HOSTNAME}/api/partner-locations/:id`,
23819
+ async ({ request, params }) => {
23820
+ const locationId = params.id;
23821
+ const body = await request.json();
23822
+ const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
23823
+ if (!mockStore.partnerLocations) {
23824
+ mockStore.partnerLocations = [
23825
+ ...platformData.reputationData?.partnerLocations || []
23826
+ ];
23827
+ }
23828
+ const locationIndex = mockStore.partnerLocations.findIndex(
23829
+ (loc) => loc.externalId === locationId
23830
+ );
23831
+ if (locationIndex === -1) {
23832
+ return HttpResponse.json(
23833
+ {
23834
+ success: false,
23835
+ message: `Partner location with ID ${locationId} not found (Sandbox)`
23836
+ },
23837
+ { status: 404 }
23838
+ );
23839
+ }
23840
+ mockStore.partnerLocations[locationIndex] = {
23841
+ ...mockStore.partnerLocations[locationIndex],
23842
+ googleBusinessProfilePlaceId: body.googleBusinessProfilePlaceId || null,
23843
+ updatedAt: /* @__PURE__ */ new Date()
23844
+ };
23845
+ return HttpResponse.json({
23846
+ success: true,
23847
+ message: "Partner location updated successfully (Sandbox)",
23848
+ data: mockStore.partnerLocations[locationIndex]
23849
+ });
23850
+ }
23851
+ ),
23692
23852
  // Channel Senders
23693
23853
  http.get(`${HOSTNAME}/api/channel/senders`, () => {
23694
23854
  const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
@@ -23714,10 +23874,19 @@ const reputationHandlers = [
23714
23874
  // Reputation Config
23715
23875
  http.get(`${HOSTNAME}/api/reputation/config`, () => {
23716
23876
  if (mockStore.reputationConfig) {
23877
+ const currentLocations = mockStore.partnerLocations || getSandboxDataForPlatform(currentSandboxPlatformId).reputationData?.partnerLocations || [];
23878
+ const locationMappings = {};
23879
+ currentLocations.forEach((location2) => {
23880
+ locationMappings[location2.externalId] = location2.googleBusinessProfilePlaceId || null;
23881
+ });
23717
23882
  const response2 = {
23718
23883
  success: true,
23719
23884
  message: "Success (Sandbox)",
23720
- data: mockStore.reputationConfig
23885
+ data: {
23886
+ ...mockStore.reputationConfig,
23887
+ locationMappings
23888
+ // Always return current mappings from partner locations
23889
+ }
23721
23890
  };
23722
23891
  return HttpResponse.json(response2);
23723
23892
  }
@@ -23737,7 +23906,7 @@ const reputationHandlers = [
23737
23906
  if (currentConfig) {
23738
23907
  const updatedConfig = {
23739
23908
  ...currentConfig,
23740
- locationMappings: body.locationMappings || currentConfig.locationMappings,
23909
+ // Note: locationMappings are now stored in partner_locations table, not here
23741
23910
  emailChannelSenderId: body.emailChannelSenderId,
23742
23911
  smsChannelSenderId: body.smsChannelSenderId,
23743
23912
  // Preserve existing completedOnboardingAt if not provided in update
@@ -23746,10 +23915,19 @@ const reputationHandlers = [
23746
23915
  };
23747
23916
  mockStore.reputationConfig = updatedConfig;
23748
23917
  }
23918
+ const currentLocations = mockStore.partnerLocations || getSandboxDataForPlatform(currentSandboxPlatformId).reputationData?.partnerLocations || [];
23919
+ const locationMappings = {};
23920
+ currentLocations.forEach((location2) => {
23921
+ locationMappings[location2.externalId] = location2.googleBusinessProfilePlaceId || null;
23922
+ });
23749
23923
  const response = {
23750
23924
  success: true,
23751
23925
  message: "Configuration updated successfully (Sandbox)",
23752
- data: mockStore.reputationConfig
23926
+ data: {
23927
+ ...mockStore.reputationConfig,
23928
+ locationMappings
23929
+ // Include current mappings from partner locations
23930
+ }
23753
23931
  };
23754
23932
  return HttpResponse.json(response);
23755
23933
  }),
@@ -23849,27 +24027,8 @@ const reputationHandlers = [
23849
24027
  data: platformData.reputationData?.gbpConnection || null
23850
24028
  });
23851
24029
  }),
23852
- // Business Branding Update (used in preview step)
23853
- http.put(`${HOSTNAME}/api/businesses/me`, async ({ request }) => {
23854
- const body = await request.json();
23855
- const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
23856
- const existingBusiness = platformData.businessesMe;
23857
- const updatedBranding = {
23858
- ...existingBusiness?.branding,
23859
- ...body?.branding
23860
- };
23861
- return HttpResponse.json({
23862
- success: true,
23863
- message: "Branding updated successfully (Sandbox)",
23864
- data: {
23865
- ...existingBusiness,
23866
- name: body?.name || existingBusiness?.name || "Updated Brand",
23867
- branding: updatedBranding
23868
- }
23869
- });
23870
- }),
23871
24030
  // Reputation Responses (unified reviews and internal feedback)
23872
- http.get(`${HOSTNAME}/api/reputation/responses`, ({ request }) => {
24031
+ http.get(`${HOSTNAME}/api/reputation/responses`, async ({ request }) => {
23873
24032
  const url = new URL(request.url);
23874
24033
  const startDate = url.searchParams.get("start_date");
23875
24034
  const endDate = url.searchParams.get("end_date");
@@ -23878,6 +24037,21 @@ const reputationHandlers = [
23878
24037
  const limit = parseInt(url.searchParams.get("limit") || "25", 10);
23879
24038
  const searchTerm = url.searchParams.get("search_term");
23880
24039
  const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
24040
+ let directFeedbackEnabled = false;
24041
+ try {
24042
+ const partnerResponse = await fetch(`${HOSTNAME}/api/partners/me`, {
24043
+ headers: request.headers
24044
+ });
24045
+ if (partnerResponse.ok) {
24046
+ const partnerData = await partnerResponse.json();
24047
+ directFeedbackEnabled = partnerData?.data?.feature_customizations?.reputation?.directFeedbackEnabled ?? false;
24048
+ }
24049
+ } catch (error2) {
24050
+ console.warn(
24051
+ "Failed to fetch partner data in MSW handler, using default directFeedbackEnabled=true",
24052
+ error2
24053
+ );
24054
+ }
23881
24055
  const responsesData = generateReputationResponsesData({
23882
24056
  platformData,
23883
24057
  startDate,
@@ -23887,6 +24061,12 @@ const reputationHandlers = [
23887
24061
  limit,
23888
24062
  searchTerm
23889
24063
  });
24064
+ if (!directFeedbackEnabled) {
24065
+ responsesData.data = responsesData.data.filter(
24066
+ (response) => response.source !== "internal"
24067
+ );
24068
+ responsesData.total = responsesData.data.length;
24069
+ }
23890
24070
  return HttpResponse.json({
23891
24071
  success: true,
23892
24072
  message: "Success (Sandbox)",
@@ -23899,14 +24079,11 @@ const reputationHandlers = [
23899
24079
  const startDate = url.searchParams.get("start_date");
23900
24080
  const endDate = url.searchParams.get("end_date");
23901
24081
  const locationIds = url.searchParams.get("location_ids");
23902
- const includeComparison = url.searchParams.get("include_comparison") === "true";
23903
24082
  const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
23904
24083
  const queryParams = {
23905
- platform: "google_business_profile",
23906
24084
  start_date: startDate || void 0,
23907
24085
  end_date: endDate || void 0,
23908
- location_ids: locationIds ? locationIds.split(",").filter(Boolean) : void 0,
23909
- include_comparison: includeComparison
24086
+ location_ids: locationIds ? locationIds.split(",").filter(Boolean) : void 0
23910
24087
  };
23911
24088
  const analyticsData = generateReviewAnalyticsData({
23912
24089
  platformData,
@@ -23954,11 +24131,8 @@ const reputationHandlers = [
23954
24131
  const url = new URL(request.url);
23955
24132
  const startDate = url.searchParams.get("start_date");
23956
24133
  const endDate = url.searchParams.get("end_date");
23957
- const includeComparison = url.searchParams.get("include_comparison") === "true";
23958
24134
  const platformData = getSandboxDataForPlatform(currentSandboxPlatformId);
23959
- const queryParams = {
23960
- include_comparison: includeComparison
23961
- };
24135
+ const queryParams = {};
23962
24136
  if (startDate) {
23963
24137
  queryParams.start_date = startDate;
23964
24138
  }
@@ -24005,7 +24179,7 @@ const reputationHandlers = [
24005
24179
  });
24006
24180
  }),
24007
24181
  // Reputation Audit Logs
24008
- http.get(`${HOSTNAME}/api/reputation/audit-logs`, ({ request }) => {
24182
+ http.get(`${HOSTNAME}/api/reputation/audit-logs`, async ({ request }) => {
24009
24183
  const url = new URL(request.url);
24010
24184
  const startDate = url.searchParams.get("start_date");
24011
24185
  const endDate = url.searchParams.get("end_date");
@@ -24014,6 +24188,21 @@ const reputationHandlers = [
24014
24188
  const limit = parseInt(url.searchParams.get("limit") || "25", 10);
24015
24189
  const searchTerm = url.searchParams.get("search_term");
24016
24190
  const orderBy = url.searchParams.get("order_by") || "timestamp";
24191
+ let directFeedbackEnabled = false;
24192
+ try {
24193
+ const partnerResponse = await fetch(`${HOSTNAME}/api/partners/me`, {
24194
+ headers: request.headers
24195
+ });
24196
+ if (partnerResponse.ok) {
24197
+ const partnerData = await partnerResponse.json();
24198
+ directFeedbackEnabled = partnerData?.data?.feature_customizations?.reputation?.directFeedbackEnabled ?? false;
24199
+ }
24200
+ } catch (error2) {
24201
+ console.warn(
24202
+ "Failed to fetch partner data in MSW handler, using default directFeedbackEnabled=true",
24203
+ error2
24204
+ );
24205
+ }
24017
24206
  const auditLogResponse = generateAuditLogData({
24018
24207
  startDate,
24019
24208
  endDate,
@@ -24021,14 +24210,122 @@ const reputationHandlers = [
24021
24210
  cursor,
24022
24211
  limit,
24023
24212
  searchTerm,
24024
- orderBy
24213
+ orderBy,
24214
+ directFeedbackEnabled
24025
24215
  });
24026
24216
  return HttpResponse.json({
24027
24217
  success: true,
24028
24218
  message: "Success (Sandbox)",
24029
24219
  data: auditLogResponse
24030
24220
  });
24031
- })
24221
+ }),
24222
+ // Reviews Data Availability
24223
+ http.get(`${HOSTNAME}/api/reviews/data-availability`, () => {
24224
+ const now = /* @__PURE__ */ new Date();
24225
+ const earliestDate = new Date(now);
24226
+ earliestDate.setDate(earliestDate.getDate() - 100);
24227
+ return HttpResponse.json({
24228
+ success: true,
24229
+ message: "Success (Sandbox)",
24230
+ data: {
24231
+ dataAvailableFrom: earliestDate.toISOString()
24232
+ }
24233
+ });
24234
+ }),
24235
+ // Feedback Data Availability
24236
+ http.get(`${HOSTNAME}/api/feedback/data-availability`, () => {
24237
+ const now = /* @__PURE__ */ new Date();
24238
+ const earliestDate = new Date(now);
24239
+ earliestDate.setDate(earliestDate.getDate() - 100);
24240
+ return HttpResponse.json({
24241
+ success: true,
24242
+ message: "Success (Sandbox)",
24243
+ data: {
24244
+ dataAvailableFrom: earliestDate.toISOString()
24245
+ }
24246
+ });
24247
+ }),
24248
+ // Reputation Data Availability
24249
+ http.get(`${HOSTNAME}/api/reputation/data-availability`, () => {
24250
+ const now = /* @__PURE__ */ new Date();
24251
+ const earliestDate = new Date(now);
24252
+ earliestDate.setDate(earliestDate.getDate() - 100);
24253
+ return HttpResponse.json({
24254
+ success: true,
24255
+ message: "Success (Sandbox)",
24256
+ data: {
24257
+ dataAvailableFrom: earliestDate.toISOString()
24258
+ }
24259
+ });
24260
+ }),
24261
+ // Activate Automations
24262
+ http.post(`${HOSTNAME}/api/reputation/activate-automations`, () => {
24263
+ return HttpResponse.json({
24264
+ success: true,
24265
+ message: "Success (Sandbox)",
24266
+ data: {}
24267
+ });
24268
+ }),
24269
+ // Generate Review Response
24270
+ http.post(`${HOSTNAME}/api/reputation/reviews/generate-response`, () => {
24271
+ return HttpResponse.json({
24272
+ success: true,
24273
+ message: "Success (Sandbox)",
24274
+ data: {
24275
+ response: "This is a test response"
24276
+ }
24277
+ });
24278
+ }),
24279
+ // Post Review Reply - prevent actual replies in sandbox
24280
+ http.post(`${HOSTNAME}/api/reputation/reviews/reply`, () => {
24281
+ return HttpResponse.json({
24282
+ success: true,
24283
+ message: "Reply intercepted (Sandbox)",
24284
+ data: { success: true }
24285
+ });
24286
+ }),
24287
+ // Sync Google Reviews
24288
+ http.post(`${HOSTNAME}/api/reputation/sync-google-reviews`, () => {
24289
+ return HttpResponse.json({
24290
+ success: true,
24291
+ message: "Success (Sandbox)",
24292
+ data: {}
24293
+ });
24294
+ }),
24295
+ // finalize onboarding
24296
+ http.post(
24297
+ `${HOSTNAME}/api/reputation/finalize-onboarding`,
24298
+ async ({ request }) => {
24299
+ const body = await request.json();
24300
+ const currentConfig = mockStore.reputationConfig || getSandboxDataForPlatform(currentSandboxPlatformId).reputationData?.reputationConfig;
24301
+ const updatedConfig = {
24302
+ ...currentConfig || {},
24303
+ id: currentConfig?.id || "mock-reputation-config-id",
24304
+ businessId: currentConfig?.businessId || "mock-business-id",
24305
+ emailChannelSenderId: body.emailChannelSenderId || currentConfig?.emailChannelSenderId || null,
24306
+ smsChannelSenderId: body.smsChannelSenderId || currentConfig?.smsChannelSenderId || null,
24307
+ completedOnboardingAt: (/* @__PURE__ */ new Date()).toISOString(),
24308
+ createdAt: currentConfig?.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
24309
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
24310
+ };
24311
+ mockStore.reputationConfig = updatedConfig;
24312
+ const currentLocations = mockStore.partnerLocations || getSandboxDataForPlatform(currentSandboxPlatformId).reputationData?.partnerLocations || [];
24313
+ const locationMappings = {};
24314
+ currentLocations.forEach((location2) => {
24315
+ locationMappings[location2.externalId] = location2.googleBusinessProfilePlaceId || null;
24316
+ });
24317
+ const response = {
24318
+ success: true,
24319
+ message: "Configuration updated successfully (Sandbox)",
24320
+ data: {
24321
+ ...mockStore.reputationConfig,
24322
+ locationMappings
24323
+ // Include current mappings from partner locations
24324
+ }
24325
+ };
24326
+ return HttpResponse.json(response);
24327
+ }
24328
+ )
24032
24329
  ];
24033
24330
  const getHandlersByFeatures = (features) => {
24034
24331
  const allHandlers = [];