Add browser fingerprint support and analytics metadata display
- Transfer user's browser fingerprint (user-agent, viewport, timezone, language, geolocation) to Chrome for more authentic scraping - Display review topics from Google Maps in analytics dashboard - Show business category badge in analytics header - Fix date_text null handling in analytics (handle undefined/timestamp fields) - Add review_topics and business_category to JobStatus interface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,9 @@ export function calculateReviewStats(reviews: Review[]): ReviewStats {
|
||||
// Populate minDate/maxDate/centerDate on reviews for display
|
||||
reviews.forEach(r => {
|
||||
if (!r.minDate || !r.maxDate || !r.centerDate) {
|
||||
const range = parseDateTextToRange(r.date_text);
|
||||
// Handle both date_text and timestamp field names
|
||||
const dateText = r.date_text || (r as any).timestamp || '';
|
||||
const range = parseDateTextToRange(dateText);
|
||||
r.minDate = range.minDate;
|
||||
r.maxDate = range.maxDate;
|
||||
// Calculate centerDate as midpoint
|
||||
@@ -96,8 +98,8 @@ export function calculateReviewStats(reviews: Review[]): ReviewStats {
|
||||
|
||||
// Recent reviews (last 30 days - simplified check)
|
||||
const recentReviews = reviews.filter(r => {
|
||||
const text = r.date_text.toLowerCase();
|
||||
return text.includes('day') || text.includes('week') || text.includes('hour');
|
||||
const text = (r.date_text || (r as any).timestamp || '').toLowerCase();
|
||||
return text.includes('day') || text.includes('week') || text.includes('hour') || text.includes('minute') || text.includes('second');
|
||||
}).length;
|
||||
|
||||
// Rating distribution
|
||||
@@ -278,6 +280,14 @@ function extractNumber(text: string): number {
|
||||
*/
|
||||
export function parseDateTextToRange(dateText: string): { minDate: Date; maxDate: Date } {
|
||||
const now = new Date();
|
||||
|
||||
// Handle undefined/null dateText
|
||||
if (!dateText) {
|
||||
// Return a default range (assume recent - within last month)
|
||||
const daysAgo = (days: number) => new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
|
||||
return { minDate: daysAgo(30), maxDate: now };
|
||||
}
|
||||
|
||||
const text = dateText.toLowerCase();
|
||||
|
||||
// Remove "Edited " prefix if present
|
||||
@@ -396,7 +406,8 @@ export function filterReviewsByDateRange(reviews: Review[], range: DateRange): R
|
||||
// Filter range: [filterStart, filterEnd]
|
||||
// Overlap occurs when: minDate <= filterEnd AND maxDate >= filterStart
|
||||
return reviews.filter(r => {
|
||||
const { minDate, maxDate } = parseDateTextToRange(r.date_text);
|
||||
const dateText = r.date_text || (r as any).timestamp || '';
|
||||
const { minDate, maxDate } = parseDateTextToRange(dateText);
|
||||
return minDate <= filterEnd && maxDate >= filterStart;
|
||||
});
|
||||
}
|
||||
@@ -405,7 +416,8 @@ export function filterReviewsByCustomDateRange(reviews: Review[], fromDate: Date
|
||||
if (!fromDate && !toDate) return reviews;
|
||||
|
||||
return reviews.filter(r => {
|
||||
const reviewDate = parseDateText(r.date_text);
|
||||
const dateText = r.date_text || (r as any).timestamp || '';
|
||||
const reviewDate = parseDateText(dateText);
|
||||
|
||||
// If only fromDate is set, filter reviews >= fromDate
|
||||
if (fromDate && !toDate) {
|
||||
@@ -429,7 +441,7 @@ export function filterReviewsByCustomDateRange(reviews: Review[], fromDate: Date
|
||||
export function calculateTimelineData(reviews: Review[]): TimelineDataPoint[] {
|
||||
// Sort reviews by date (newest first)
|
||||
const sortedReviews = [...reviews]
|
||||
.map(r => ({ ...r, parsedDate: parseDateText(r.date_text) }))
|
||||
.map(r => ({ ...r, parsedDate: parseDateText(r.date_text || (r as any).timestamp || '') }))
|
||||
.sort((a, b) => b.parsedDate.getTime() - a.parsedDate.getTime());
|
||||
|
||||
// Group by month
|
||||
|
||||
Reference in New Issue
Block a user