@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.
- package/dist/chunks/index.js +5246 -3322
- package/dist/chunks/sandbox-loading-screen.js +734 -437
- package/dist/index.d.ts +14 -5
- package/dist/index.umd.js +11 -11
- package/dist/styles.css +2 -2
- package/package.json +1 -1
|
@@ -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: "
|
|
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: "
|
|
20690
|
+
name: "Paws Pet Hotel",
|
|
20674
20691
|
brandColors: ["#f59e0b", "#dc2626", "#059669", "#7c3aed"],
|
|
20675
20692
|
locationNames: [
|
|
20676
|
-
"
|
|
20677
|
-
"
|
|
20678
|
-
"
|
|
20679
|
-
"
|
|
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:
|
|
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
|
|
20843
|
+
const createReputationConfigFactory = (platformConfig, businessId, partnerLocations) => {
|
|
20830
20844
|
const locationMappings = {};
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
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: "
|
|
20883
|
-
website: "https://
|
|
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: "
|
|
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 &&
|
|
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
|
|
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
|
-
|
|
22097
|
-
|
|
22098
|
-
|
|
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
|
-
|
|
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
|
|
22649
|
-
userEmail:
|
|
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:
|
|
22653
|
-
hasReply
|
|
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
|
-
|
|
22661
|
-
|
|
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:
|
|
22667
|
-
//
|
|
22668
|
-
{ weight:
|
|
22669
|
-
//
|
|
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:
|
|
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
|
-
|
|
22689
|
-
|
|
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
|
|
22764
|
-
"I wanted to share my
|
|
22765
|
-
"
|
|
22766
|
-
"
|
|
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
|
|
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
|
|
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
|
|
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 >=
|
|
22784
|
-
if (rating
|
|
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
|
|
22788
|
-
"
|
|
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 >=
|
|
22793
|
-
if (rating
|
|
22892
|
+
if (rating >= 9) return [...commonPromoters, ...rentalPromoters];
|
|
22893
|
+
if (rating >= 7) return [...commonPassives, ...rentalPassives];
|
|
22894
|
+
return [...commonDetractors, ...rentalDetractors];
|
|
22794
22895
|
}
|
|
22795
|
-
if (rating >=
|
|
22796
|
-
if (rating
|
|
22797
|
-
return
|
|
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
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
|
|
22831
|
-
|
|
22832
|
-
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
|
|
22838
|
-
|
|
22839
|
-
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
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
|
-
|
|
22846
|
-
|
|
22847
|
-
|
|
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
|
|
22969
|
+
return fullResponse;
|
|
22850
22970
|
};
|
|
22851
22971
|
function generateReviewPeriodData(platformConfig, scalingFactor, isPrevious = false, isAllTime = false) {
|
|
22852
|
-
const baseMetrics = isAllTime ? getAllTimePlatformReviewMetrics(platformConfig) : getBasePlatformReviewMetrics
|
|
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
|
|
22990
|
+
function getBasePlatformReviewMetrics(platformConfig) {
|
|
22871
22991
|
const industryMultipliers = {
|
|
22872
22992
|
// Pet spa industry (Goose) - high customer satisfaction
|
|
22873
|
-
"
|
|
22874
|
-
totalCount:
|
|
22875
|
-
//
|
|
22876
|
-
averageRating: 4.
|
|
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:
|
|
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:
|
|
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
|
-
"
|
|
22896
|
-
totalCount:
|
|
22897
|
-
//
|
|
22898
|
-
averageRating: 4.
|
|
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:
|
|
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:
|
|
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
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23016
|
-
|
|
23017
|
-
|
|
23018
|
-
|
|
23019
|
-
|
|
23020
|
-
|
|
23021
|
-
|
|
23022
|
-
|
|
23023
|
-
|
|
23024
|
-
|
|
23025
|
-
|
|
23026
|
-
|
|
23027
|
-
|
|
23028
|
-
|
|
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
|
|
23179
|
+
return fullResponse;
|
|
23031
23180
|
};
|
|
23032
23181
|
function generateFeedbackPeriodData(platformConfig, scalingFactor, isPrevious = false, isAllTime = false) {
|
|
23033
|
-
const baseMetrics = isAllTime ? getAllTimePlatformFeedbackMetrics(platformConfig) : getBasePlatformFeedbackMetrics
|
|
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
|
|
23220
|
+
function getBasePlatformFeedbackMetrics(platformConfig) {
|
|
23072
23221
|
const industryMultipliers = {
|
|
23073
23222
|
// Pet spa industry (Goose) - high engagement, positive feedback
|
|
23074
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
23273
|
-
//
|
|
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
|
-
"
|
|
23280
|
-
"
|
|
23281
|
-
"Customer opened
|
|
23282
|
-
"
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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
|
|
23356
|
-
platformData
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
23396
|
-
|
|
23397
|
-
const
|
|
23398
|
-
const
|
|
23399
|
-
const
|
|
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
|
-
|
|
23403
|
-
|
|
23404
|
-
|
|
23405
|
-
|
|
23406
|
-
|
|
23407
|
-
|
|
23408
|
-
|
|
23409
|
-
|
|
23410
|
-
|
|
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
|
-
|
|
23413
|
-
|
|
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
|
|
23440
|
-
|
|
23441
|
-
|
|
23442
|
-
|
|
23443
|
-
|
|
23444
|
-
|
|
23445
|
-
|
|
23446
|
-
|
|
23447
|
-
|
|
23448
|
-
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
|
|
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
|
|
23505
|
-
platformData
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
|
|
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:
|
|
23590
|
-
// Use
|
|
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
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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 = [];
|