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:
@@ -22,14 +22,21 @@ interface ReviewWithNew extends Review {
|
||||
photo_urls?: string[] | null;
|
||||
}
|
||||
|
||||
interface ReviewTopic {
|
||||
topic: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
interface ReviewAnalyticsProps {
|
||||
reviews: ReviewWithNew[];
|
||||
businessName?: string;
|
||||
businessUrl?: string;
|
||||
newCount?: number;
|
||||
businessCategory?: string;
|
||||
reviewTopics?: ReviewTopic[];
|
||||
}
|
||||
|
||||
export default function ReviewAnalytics({ reviews, businessName, businessUrl, newCount }: ReviewAnalyticsProps) {
|
||||
export default function ReviewAnalytics({ reviews, businessName, businessUrl, newCount, businessCategory, reviewTopics }: ReviewAnalyticsProps) {
|
||||
const [sorting, setSorting] = useState<SortingState>([{ id: 'date', desc: true }]); // Default: newest first
|
||||
const [columnFilters, setColumnFiltersState] = useState<ColumnFiltersState>([]);
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
@@ -476,9 +483,16 @@ export default function ReviewAnalytics({ reviews, businessName, businessUrl, ne
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-gray-900">
|
||||
{businessName || 'Review Analytics'}
|
||||
</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
<h2 className="text-3xl font-bold text-gray-900">
|
||||
{businessName || 'Review Analytics'}
|
||||
</h2>
|
||||
{businessCategory && (
|
||||
<span className="px-3 py-1 bg-purple-100 text-purple-800 text-sm font-medium rounded-full border border-purple-300">
|
||||
{businessCategory}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{businessUrl && (
|
||||
<a
|
||||
href={businessUrl}
|
||||
@@ -821,6 +835,33 @@ export default function ReviewAnalytics({ reviews, businessName, businessUrl, ne
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Review Topics - from Google Maps */}
|
||||
{reviewTopics && reviewTopics.length > 0 && (
|
||||
<div className="bg-white border-2 border-gray-300 rounded-xl p-5 shadow-md">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<MessageSquare className="w-6 h-6 text-indigo-600" />
|
||||
<h3 className="text-lg font-bold text-gray-900">What People Talk About</h3>
|
||||
<span className="text-sm text-gray-500">({reviewTopics.length} topics from Google)</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{reviewTopics.slice(0, 15).map((topic, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="px-3 py-1.5 bg-gradient-to-r from-indigo-50 to-purple-50 border border-indigo-200 rounded-full flex items-center gap-2"
|
||||
>
|
||||
<span className="text-sm font-medium text-indigo-800">{topic.topic}</span>
|
||||
<span className="text-xs bg-indigo-200 text-indigo-900 px-1.5 py-0.5 rounded-full font-bold">
|
||||
{topic.count}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{reviewTopics.length > 15 && (
|
||||
<p className="text-sm text-gray-500 mt-3">+{reviewTopics.length - 15} more topics</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rating & Volume Timeline */}
|
||||
{timelineData.length > 0 && (
|
||||
<div className={`bg-white rounded-xl p-6 shadow-md transition-all ${
|
||||
|
||||
Reference in New Issue
Block a user