This commit is contained in:
2025-03-25 21:12:03 +08:00
parent ecf21a812f
commit e0ac87fb25
2 changed files with 105 additions and 34 deletions

View File

@@ -1,29 +1,87 @@
// Event Types
export interface Event {
id: string;
time: string;
type: string;
linkInfo: {
// 核心事件信息
event_id: string;
event_time: string;
event_type: string;
event_attributes: string;
// 链接信息
link_id: string;
link_slug: string;
link_label: string;
link_title: string;
link_original_url: string;
link_attributes: string;
link_created_at: string;
link_expires_at: string | null;
link_tags: string;
// 用户信息
user_id: string;
user_name: string;
user_email: string;
user_attributes: string;
// 团队信息
team_id: string;
team_name: string;
team_attributes: string;
// 项目信息
project_id: string;
project_name: string;
project_attributes: string;
// 二维码信息
qr_code_id: string;
qr_code_name: string;
qr_code_attributes: string;
// 访问者信息
visitor_id: string;
session_id: string;
ip_address: string;
country: string;
city: string;
device_type: string;
browser: string;
os: string;
user_agent: string;
// 来源信息
referrer: string;
utm_source: string;
utm_medium: string;
utm_campaign: string;
// 交互信息
time_spent_sec: number;
is_bounce: boolean;
is_qr_scan: boolean;
conversion_type: string;
conversion_value: number;
// 旧接口兼容字段
id?: string;
time?: string;
type?: string;
linkInfo?: {
id: string;
shortUrl: string;
originalUrl: string;
};
visitor: {
visitor?: {
id: string;
browser: string;
os: string;
device: string;
};
location: {
location?: {
country: string;
region: string;
city: string;
};
referrer: string;
conversion?: {
type: string;
value: number;
};
}
// Analytics Types

View File

@@ -1,14 +1,14 @@
"use client";
import { useState, useEffect, useRef } from 'react';
import { addDays, format } from 'date-fns';
import { useState, useEffect } from 'react';
import { format } from 'date-fns';
import { DateRangePicker } from '../components/ui/DateRangePicker';
import { Event, EventFilters } from '../api/types';
import { Event } from '../api/types';
export default function EventsPage() {
const [dateRange, setDateRange] = useState({
from: addDays(new Date(), -7),
to: new Date()
from: new Date('2024-02-01'),
to: new Date('2025-03-05')
});
const [loading, setLoading] = useState(true);
@@ -22,7 +22,7 @@ export default function EventsPage() {
linkSlug: ''
});
const [filters, setFilters] = useState<EventFilters>({
const [filters, setFilters] = useState({
startTime: format(new Date('2024-02-01'), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
endTime: format(new Date('2025-03-05'), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
page: 1,
@@ -30,8 +30,6 @@ export default function EventsPage() {
});
const [summary, setSummary] = useState<any>(null);
const observerRef = useRef<IntersectionObserver | null>(null);
const lastEventRef = useRef<HTMLDivElement | null>(null);
const fetchEvents = async (pageNum: number) => {
try {
@@ -56,15 +54,19 @@ export default function EventsPage() {
throw new Error(data.error || 'Failed to fetch events');
}
const eventsData = data.data || data.events || [];
if (pageNum === 1) {
setEvents(data.events);
setEvents(eventsData);
} else {
setEvents(prev => [...prev, ...data.events]);
setEvents(prev => [...prev, ...eventsData]);
}
setHasMore(data.events.length === 50);
setHasMore(Array.isArray(eventsData) && eventsData.length === 50);
} catch (err) {
console.error("Error fetching events:", err);
setError(err instanceof Error ? err.message : 'An error occurred while fetching events');
setEvents([]);
} finally {
setLoading(false);
}
@@ -174,42 +176,53 @@ export default function EventsPage() {
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Referrer
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Conversion
</th>
</tr>
</thead>
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
{events.map((event, index) => (
<tr key={event.id} className={index % 2 === 0 ? 'bg-white dark:bg-gray-800' : 'bg-gray-50 dark:bg-gray-900'}>
{Array.isArray(events) && events.map((event, index) => (
<tr key={event.event_id || index} className={index % 2 === 0 ? 'bg-white dark:bg-gray-800' : 'bg-gray-50 dark:bg-gray-900'}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
{formatDate(event.time)}
{event.event_time && formatDate(event.event_time)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
event.type === 'conversion' ? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' : 'bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100'
event.event_type === 'conversion' ? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' : 'bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100'
}`}>
{event.type}
{event.event_type || 'unknown'}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
<div>
<div className="font-medium">{event.linkInfo.shortUrl}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">{event.linkInfo.originalUrl}</div>
<div className="font-medium">{event.link_slug || '-'}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">{event.link_original_url || '-'}</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
<div>
<div>{event.visitor.browser}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">{event.visitor.os} / {event.visitor.device}</div>
<div>{event.browser || '-'}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">{event.os || '-'} / {event.device_type || '-'}</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
<div>
<div>{event.location.city}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">{event.location.region}, {event.location.country}</div>
<div>{event.city || '-'}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">{event.country || '-'}</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{event.referrer || '-'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
<div>
<div>{event.conversion_type || '-'}</div>
{event.conversion_value > 0 && (
<div className="text-gray-500 dark:text-gray-400 text-xs">Value: {event.conversion_value}</div>
)}
</div>
</td>
</tr>
))}
</tbody>
@@ -233,7 +246,7 @@ export default function EventsPage() {
</div>
)}
{!loading && events.length === 0 && (
{!loading && Array.isArray(events) && events.length === 0 && (
<div className="flex justify-center p-8 text-gray-500 dark:text-gray-400">
No events found
</div>