events team

This commit is contained in:
2025-04-01 22:34:07 +08:00
parent 57e16144a9
commit a4ef2c3147

View File

@@ -17,16 +17,26 @@ interface Event {
event_type: string; event_type: string;
visitor_id: string; visitor_id: string;
created_at: string; created_at: string;
event_time?: string;
referrer?: string; referrer?: string;
browser?: string; browser?: string;
os?: string; os?: string;
device_type?: string; device_type?: string;
country?: string; country?: string;
city?: string; city?: string;
event_attributes?: string;
link_attributes?: string;
user_attributes?: string;
link_label?: string;
link_original_url?: string;
team_name?: string;
project_name?: string;
link_id?: string;
link_slug?: string;
} }
// 格式化日期函数 // 格式化日期函数
const formatDate = (dateString: string) => { const formatDate = (dateString: string | undefined) => {
if (!dateString) return ''; if (!dateString) return '';
try { try {
return format(new Date(dateString), 'yyyy-MM-dd HH:mm:ss'); return format(new Date(dateString), 'yyyy-MM-dd HH:mm:ss');
@@ -35,6 +45,53 @@ const formatDate = (dateString: string) => {
} }
}; };
// 解析JSON字符串
const parseJsonSafely = (jsonString: string) => {
if (!jsonString) return null;
try {
return JSON.parse(jsonString);
} catch {
return null;
}
};
// 获取用户可读名称
const getUserDisplayName = (user: Record<string, unknown> | null) => {
if (!user) return '-';
if (typeof user.full_name === 'string') return user.full_name;
if (typeof user.name === 'string') return user.name;
if (typeof user.email === 'string') return user.email;
return '-';
};
// 提取链接和事件的重要信息
const extractEventInfo = (event: Event) => {
// 解析事件属性
const eventAttrs = parseJsonSafely(event.event_attributes || '{}');
// 解析链接属性
const linkAttrs = parseJsonSafely(event.link_attributes || '{}');
// 解析用户属性
const userAttrs = parseJsonSafely(event.user_attributes || '{}');
return {
eventTime: event.created_at || event.event_time,
linkName: event.link_label || linkAttrs?.name || eventAttrs?.link_name || event.link_slug || '-',
originalUrl: event.link_original_url || eventAttrs?.origin_url || '-',
eventType: event.event_type || '-',
visitorId: event.visitor_id?.substring(0, 8) || '-',
referrer: eventAttrs?.referrer || '-',
location: event.country ? (event.city ? `${event.city}, ${event.country}` : event.country) : '-',
device: event.device_type || '-',
browser: event.browser || '-',
os: event.os || '-',
userInfo: getUserDisplayName(userAttrs),
teamName: event.team_name || '-',
projectName: event.project_name || '-'
};
};
export default function DashboardPage() { export default function DashboardPage() {
// 默认日期范围为最近7天 // 默认日期范围为最近7天
const today = new Date(); const today = new Date();
@@ -255,59 +312,80 @@ export default function DashboardPage() {
Time Time
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
URL ID Link Name
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
URL Original URL
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Event Type Event Type
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Visitor ID User
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Referrer Team/Project
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Location Device Info
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{events.map((event, index) => ( {events.map((event, index) => {
<tr key={event.event_id || index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}> const info = extractEventInfo(event);
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> return (
{formatDate(event.created_at)} <tr key={event.event_id || index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
</td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> {formatDate(info.eventTime)}
{event.url_id} </td>
</td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <span className="font-medium">{info.linkName}</span>
<a href={event.url} className="text-blue-600 hover:underline" target="_blank" rel="noopener noreferrer"> <div className="text-xs text-gray-500 mt-1 truncate max-w-xs">
{event.url} ID: {event.link_id?.substring(0, 8) || '-'}
</a> </div>
</td> </td>
<td className="px-6 py-4 whitespace-nowrap text-sm"> <td className="px-6 py-4 whitespace-nowrap text-sm text-blue-600">
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${ <a href={info.originalUrl} className="hover:underline truncate max-w-xs block" target="_blank" rel="noopener noreferrer">
event.event_type === 'click' {info.originalUrl}
? 'bg-green-100 text-green-800' </a>
: 'bg-blue-100 text-blue-800' </td>
}`}> <td className="px-6 py-4 whitespace-nowrap text-sm">
{event.event_type} <span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
</span> info.eventType === 'click'
</td> ? 'bg-green-100 text-green-800'
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> : 'bg-blue-100 text-blue-800'
{event.visitor_id.substring(0, 8)}... }`}>
</td> {info.eventType}
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> </span>
{event.referrer || '-'} </td>
</td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <div className="font-medium">{info.userInfo}</div>
{event.country && event.city ? `${event.city}, ${event.country}` : (event.country || '-')} <div className="text-xs text-gray-400 mt-1">{info.visitorId}...</div>
</td> </td>
</tr> <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> </tbody>
</table> </table>
</div> </div>