Add gorgias component and services

This commit is contained in:
2024-12-27 16:51:19 -05:00
parent 6b7eae3473
commit 9e0a6a9b6a
24 changed files with 4287 additions and 1 deletions

View File

@@ -0,0 +1,292 @@
// src/services/googleAnalyticsService.js
class GoogleAnalyticsService {
constructor() {
this.baseUrl = "/api/analytics"; // This matches your NGINX config
}
async getBasicMetrics({ startDate = "7daysAgo" } = {}) {
try {
const response = await fetch(
`${this.baseUrl}/metrics?startDate=${startDate}`,
{
credentials: "include",
}
);
if (!response.ok) {
throw new Error("Failed to fetch metrics");
}
const result = await response.json();
if (!result?.data) {
throw new Error("No data received");
}
return this.processMetricsData(result.data);
} catch (error) {
console.error("Failed to fetch basic metrics:", error);
throw error;
}
}
async getRealTimeBasicData() {
try {
const response = await fetch(`${this.baseUrl}/realtime/basic`, {
credentials: "include",
});
if (!response.ok) {
throw new Error("Failed to fetch basic realtime data");
}
const result = await response.json();
if (!result?.data) {
throw new Error("No data received");
}
const processed = this.processRealTimeBasicData(result.data);
return {
...processed,
lastUpdated: result.lastUpdated || new Date().toISOString(),
};
} catch (error) {
console.error("Failed to fetch basic realtime data:", error);
throw error;
}
}
async getRealTimeDetailedData() {
try {
const response = await fetch(`${this.baseUrl}/realtime/detailed`, {
credentials: "include",
});
if (!response.ok) {
throw new Error("Failed to fetch detailed realtime data");
}
const result = await response.json();
if (!result?.data) {
throw new Error("No data received");
}
const processed = this.processRealTimeDetailedData(result.data);
return {
...processed,
lastUpdated: result.lastUpdated || new Date().toISOString(),
};
} catch (error) {
console.error("Failed to fetch detailed realtime data:", error);
throw error;
}
}
async getUserBehavior(timeRange = "30") {
try {
const response = await fetch(
`${this.baseUrl}/user-behavior?timeRange=${timeRange}`,
{
credentials: "include",
}
);
if (!response.ok) {
throw new Error("Failed to fetch user behavior");
}
const result = await response.json();
console.log("Raw user behavior response:", result);
if (!result?.success) {
throw new Error("Invalid response structure");
}
// Handle both data structures
const rawData = result.data?.data || result.data;
// Try to access the data differently based on the structure
const pageResponse = rawData?.pageResponse || rawData?.reports?.[0];
const deviceResponse = rawData?.deviceResponse || rawData?.reports?.[1];
const sourceResponse = rawData?.sourceResponse || rawData?.reports?.[2];
console.log("Extracted responses:", {
pageResponse,
deviceResponse,
sourceResponse,
});
const processed = {
success: true,
data: {
pageData: {
pageData: this.processPageData(pageResponse),
deviceData: this.processDeviceData(deviceResponse),
},
sourceData: this.processSourceData(sourceResponse),
},
};
console.log("Final processed data:", processed);
return processed;
} catch (error) {
console.error("Failed to fetch user behavior:", error);
throw error;
}
}
processMetricsData(data) {
if (!data?.rows) {
console.log("No rows found in data");
return [];
}
return data.rows.map((row) => ({
date: row.dimensionValues[0].value,
activeUsers: parseInt(row.metricValues[0].value),
newUsers: parseInt(row.metricValues[1].value),
avgSessionDuration: parseFloat(row.metricValues[2].value),
pageViews: parseInt(row.metricValues[3].value),
bounceRate: parseFloat(row.metricValues[4].value) * 100,
conversions: parseInt(row.metricValues[5].value),
}));
}
processRealTimeBasicData(data) {
const last30MinUsers = parseInt(
data.userResponse?.rows?.[0]?.metricValues?.[0]?.value || 0
);
const last5MinUsers = parseInt(
data.fiveMinResponse?.rows?.[0]?.metricValues?.[0]?.value || 0
);
const byMinute = Array.from({ length: 30 }, (_, i) => {
const matchingRow = data.timeSeriesResponse?.rows?.find(
(row) => parseInt(row.dimensionValues[0].value) === i
);
const users = matchingRow
? parseInt(matchingRow.metricValues[0].value)
: 0;
const timestamp = new Date(Date.now() - i * 60000);
return {
minute: -i,
users,
timestamp: timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
}),
};
}).reverse();
const tokenQuota = data.quotaInfo
? {
projectHourly: data.quotaInfo.projectHourly || {},
daily: data.quotaInfo.daily || {},
serverErrors: data.quotaInfo.serverErrors || {},
thresholdedRequests: data.quotaInfo.thresholdedRequests || {},
}
: null;
return {
last30MinUsers,
last5MinUsers,
byMinute,
tokenQuota,
};
}
processRealTimeDetailedData(data) {
return {
currentPages:
data.pageResponse?.rows?.map((row) => ({
page: row.dimensionValues[0].value,
views: parseInt(row.metricValues[0].value),
})) || [],
sources:
data.sourceResponse?.rows?.map((row) => ({
device: row.dimensionValues[0].value,
users: parseInt(row.metricValues[0].value),
})) || [],
recentEvents:
data.eventResponse?.rows
?.filter(
(row) =>
!["session_start", "(other)"].includes(
row.dimensionValues[0].value
)
)
.map((row) => ({
event: row.dimensionValues[0].value,
count: parseInt(row.metricValues[0].value),
})) || [],
};
}
processPageData(data) {
console.log("Processing page data input:", data);
if (!data?.rows) {
console.log("No rows in page data");
return [];
}
const processed = data.rows.map((row) => ({
path: row.dimensionValues[0].value || "Unknown",
pageViews: parseInt(row.metricValues[0].value || 0),
avgSessionDuration: parseFloat(row.metricValues[1].value || 0),
bounceRate: parseFloat(row.metricValues[2].value || 0) * 100,
engagedSessions: parseInt(row.metricValues[3].value || 0),
}));
console.log("Processed page data:", processed);
return processed;
}
processDeviceData(data) {
console.log("Processing device data input:", data);
if (!data?.rows) {
console.log("No rows in device data");
return [];
}
const processed = data.rows
.filter((row) => {
const device = (row.dimensionValues[0].value || "").toLowerCase();
return ["desktop", "mobile", "tablet"].includes(device);
})
.map((row) => {
const device = row.dimensionValues[0].value || "Unknown";
return {
device:
device.charAt(0).toUpperCase() + device.slice(1).toLowerCase(),
pageViews: parseInt(row.metricValues[0].value || 0),
sessions: parseInt(row.metricValues[1].value || 0),
};
})
.sort((a, b) => b.pageViews - a.pageViews);
console.log("Processed device data:", processed);
return processed;
}
processSourceData(data) {
console.log("Processing source data input:", data);
if (!data?.rows) {
console.log("No rows in source data");
return [];
}
const processed = data.rows.map((row) => ({
source: row.dimensionValues[0].value || "Unknown",
sessions: parseInt(row.metricValues[0].value || 0),
conversions: parseInt(row.metricValues[1].value || 0),
}));
console.log("Processed source data:", processed);
return processed;
}
}
// Create a single instance
const service = new GoogleAnalyticsService();
// Export both the instance and the class
export { service as googleAnalyticsService, GoogleAnalyticsService };