move events pos

This commit is contained in:
2025-04-01 23:04:15 +08:00
parent 9fa61ccf8d
commit bdae5c164c
3 changed files with 138 additions and 102 deletions

View File

@@ -35,6 +35,7 @@ interface Event {
project_name?: string; project_name?: string;
link_id?: string; link_id?: string;
link_slug?: string; link_slug?: string;
link_tags?: string;
} }
// 格式化日期函数 // 格式化日期函数
@@ -77,6 +78,19 @@ const extractEventInfo = (event: Event) => {
// 解析用户属性 // 解析用户属性
const userAttrs = parseJsonSafely(event.user_attributes || '{}'); const userAttrs = parseJsonSafely(event.user_attributes || '{}');
// 解析标签信息
let tags: string[] = [];
try {
if (event.link_tags) {
const parsedTags = JSON.parse(event.link_tags);
if (Array.isArray(parsedTags)) {
tags = parsedTags;
}
}
} catch {
// 解析失败则保持空数组
}
return { return {
eventTime: event.created_at || event.event_time, eventTime: event.created_at || event.event_time,
linkName: event.link_label || linkAttrs?.name || eventAttrs?.link_name || event.link_slug || '-', linkName: event.link_label || linkAttrs?.name || eventAttrs?.link_name || event.link_slug || '-',
@@ -90,7 +104,8 @@ const extractEventInfo = (event: Event) => {
os: event.os || '-', os: event.os || '-',
userInfo: getUserDisplayName(userAttrs), userInfo: getUserDisplayName(userAttrs),
teamName: event.team_name || '-', teamName: event.team_name || '-',
projectName: event.project_name || '-' projectName: event.project_name || '-',
tags: tags
}; };
}; };
@@ -330,7 +345,126 @@ export default function DashboardPage() {
</div> </div>
)} )}
{/* 仪表板内容 */} {/* 事件列表部分 - 现在放在最上面 */}
<div className="bg-white rounded-lg shadow overflow-hidden mb-8">
<div className="p-6 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Recent Events</h2>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Time
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Link Name
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Original URL
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Event Type
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tags
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
User
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Team/Project
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Device Info
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{events.map((event, index) => {
const info = extractEventInfo(event);
return (
<tr key={event.event_id || index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{formatDate(info.eventTime)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span className="font-medium">{info.linkName}</span>
<div className="text-xs text-gray-500 mt-1 truncate max-w-xs">
ID: {event.link_id?.substring(0, 8) || '-'}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-blue-600">
<a href={info.originalUrl} className="hover:underline truncate max-w-xs block" target="_blank" rel="noopener noreferrer">
{info.originalUrl}
</a>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
info.eventType === 'click'
? 'bg-green-100 text-green-800'
: 'bg-blue-100 text-blue-800'
}`}>
{info.eventType}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="flex flex-wrap gap-1">
{info.tags && info.tags.length > 0 ? (
info.tags.map((tag, idx) => (
<span
key={idx}
className="bg-gray-100 text-gray-800 text-xs px-2 py-0.5 rounded"
>
{tag}
</span>
))
) : (
<span className="text-gray-400">-</span>
)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="font-medium">{info.userInfo}</div>
<div className="text-xs text-gray-400 mt-1">{info.visitorId}...</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="font-medium">{info.teamName}</div>
<div className="text-xs text-gray-400 mt-1">{info.projectName}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="flex flex-col">
<span className="text-xs inline-flex items-center mb-1">
<span className="font-medium">Device:</span>
<span className="ml-1">{info.device}</span>
</span>
<span className="text-xs inline-flex items-center mb-1">
<span className="font-medium">Browser:</span>
<span className="ml-1">{info.browser}</span>
</span>
<span className="text-xs inline-flex items-center">
<span className="font-medium">OS:</span>
<span className="ml-1">{info.os}</span>
</span>
</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
{/* 表格为空状态 */}
{!loading && events.length === 0 && (
<div className="flex justify-center items-center p-8 text-gray-500">
No events found
</div>
)}
</div>
{/* 仪表板内容 - 现在放在事件列表之后 */}
<> <>
{summary && ( {summary && (
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8"> <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
@@ -377,106 +511,6 @@ export default function DashboardPage() {
<h2 className="text-lg font-semibold text-gray-900 mb-4">Geographic Distribution</h2> <h2 className="text-lg font-semibold text-gray-900 mb-4">Geographic Distribution</h2>
<GeoAnalytics data={geoData} /> <GeoAnalytics data={geoData} />
</div> </div>
{/* 事件列表部分 */}
<div className="bg-white rounded-lg shadow overflow-hidden mb-8">
<div className="p-6 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Recent Events</h2>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Time
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Link Name
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Original URL
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Event Type
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
User
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Team/Project
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Device Info
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{events.map((event, index) => {
const info = extractEventInfo(event);
return (
<tr key={event.event_id || index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{formatDate(info.eventTime)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span className="font-medium">{info.linkName}</span>
<div className="text-xs text-gray-500 mt-1 truncate max-w-xs">
ID: {event.link_id?.substring(0, 8) || '-'}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-blue-600">
<a href={info.originalUrl} className="hover:underline truncate max-w-xs block" target="_blank" rel="noopener noreferrer">
{info.originalUrl}
</a>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
info.eventType === 'click'
? 'bg-green-100 text-green-800'
: 'bg-blue-100 text-blue-800'
}`}>
{info.eventType}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="font-medium">{info.userInfo}</div>
<div className="text-xs text-gray-400 mt-1">{info.visitorId}...</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="font-medium">{info.teamName}</div>
<div className="text-xs text-gray-400 mt-1">{info.projectName}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="flex flex-col">
<span className="text-xs inline-flex items-center mb-1">
<span className="font-medium">Device:</span>
<span className="ml-1">{info.device}</span>
</span>
<span className="text-xs inline-flex items-center mb-1">
<span className="font-medium">Browser:</span>
<span className="ml-1">{info.browser}</span>
</span>
<span className="text-xs inline-flex items-center">
<span className="font-medium">OS:</span>
<span className="ml-1">{info.os}</span>
</span>
</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
{/* 表格为空状态 */}
{!loading && events.length === 0 && (
<div className="flex justify-center items-center p-8 text-gray-500">
No events found
</div>
)}
</div>
</> </>
</div> </div>
); );

View File

@@ -0,0 +1 @@

1
types/react-simple-maps.d.ts vendored Normal file
View File

@@ -0,0 +1 @@