events date range 7days
This commit is contained in:
@@ -7,8 +7,8 @@ import { Event, EventType } from '@/lib/types';
|
||||
|
||||
export default function EventsPage() {
|
||||
const [dateRange, setDateRange] = useState({
|
||||
from: new Date('2024-02-01'),
|
||||
to: new Date('2025-03-05')
|
||||
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // 7天前
|
||||
to: new Date() // 今天
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -227,6 +227,24 @@ export default function EventsPage() {
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<select
|
||||
value={filters.tags.length > 0 ? filters.tags[0] : ''}
|
||||
onChange={(e) => {
|
||||
const selectedTag = e.target.value;
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
tags: selectedTag ? [selectedTag] : []
|
||||
}));
|
||||
}}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="">所有标签</option>
|
||||
{tags.map(tag => (
|
||||
<option key={tag} value={tag}>{tag}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<select
|
||||
value={filters.sortBy}
|
||||
@@ -250,33 +268,6 @@ export default function EventsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 标签选择 */}
|
||||
<div className="mt-4">
|
||||
<h3 className="text-sm font-medium mb-2">标签</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{tags.map(tag => (
|
||||
<button
|
||||
key={tag}
|
||||
onClick={() => {
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
tags: prev.tags.includes(tag)
|
||||
? prev.tags.filter(t => t !== tag)
|
||||
: [...prev.tags, tag]
|
||||
}));
|
||||
}}
|
||||
className={`px-2 py-1 rounded-full text-sm font-medium ${
|
||||
filters.tags.includes(tag)
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex justify-end">
|
||||
<button
|
||||
onClick={resetFilters}
|
||||
|
||||
@@ -186,66 +186,6 @@ export default function LinkDetailsPage({
|
||||
}
|
||||
}, [dateRange, timeGranularity]);
|
||||
|
||||
// 获取并设置linkId
|
||||
useEffect(() => {
|
||||
const loadParams = async () => {
|
||||
const resolvedParams = await params;
|
||||
setLinkId(resolvedParams.id);
|
||||
};
|
||||
|
||||
loadParams();
|
||||
}, [params]);
|
||||
|
||||
// 获取链接详情数据
|
||||
useEffect(() => {
|
||||
if (!linkId) return;
|
||||
|
||||
const fetchLinkDetails = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 获取链接详情
|
||||
const response = await fetch(`/api/links/${linkId}/details`);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message || "Failed to fetch link details");
|
||||
}
|
||||
|
||||
const details = await response.json();
|
||||
console.log("Link details:", details); // 添加日志以确认 API 响应
|
||||
|
||||
// 将 API 返回的数据映射到组件需要的格式
|
||||
setLinkDetails({
|
||||
id: details.link_id || linkId,
|
||||
name: details.title || "Untitled Link",
|
||||
shortUrl: details.short_url || window.location.hostname + "/" + details.link_id,
|
||||
originalUrl: details.original_url || "",
|
||||
creator: details.created_by || "Unknown",
|
||||
createdAt: details.created_at || new Date().toISOString(),
|
||||
visits: details.visits || 0,
|
||||
visitChange: details.visit_change || 0,
|
||||
uniqueVisitors: details.unique_visitors || 0,
|
||||
uniqueVisitorsChange: details.unique_visitors_change || 0,
|
||||
avgTime: formatTime(details.avg_time_spent || 0),
|
||||
avgTimeChange: details.avg_time_change || 0,
|
||||
conversionRate: details.conversion_rate || 0,
|
||||
conversionChange: details.conversion_change || 0,
|
||||
status: details.is_active ? "active" : "inactive",
|
||||
tags: details.tags || [],
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
|
||||
// 获取分析数据
|
||||
fetchAnalyticsData(linkId);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch link details:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchLinkDetails();
|
||||
}, [linkId, fetchAnalyticsData]);
|
||||
|
||||
// 格式化时间(秒转为分钟和秒)
|
||||
const formatTime = (seconds: number) => {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
@@ -253,6 +193,59 @@ export default function LinkDetailsPage({
|
||||
return `${minutes}m ${remainingSeconds}s`;
|
||||
};
|
||||
|
||||
// 将 fetchLinkDetails 移到 useEffect 外面
|
||||
const fetchLinkDetails = useCallback(async (id: string) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 获取链接详情
|
||||
const response = await fetch(`/api/links/${id}/details`);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message || "Failed to fetch link details");
|
||||
}
|
||||
|
||||
const details = await response.json();
|
||||
console.log("Link details:", details); // 添加日志以确认 API 响应
|
||||
|
||||
// 将 API 返回的数据映射到组件需要的格式
|
||||
setLinkDetails({
|
||||
id: details.link_id || id,
|
||||
name: details.title || "Untitled Link",
|
||||
shortUrl: details.short_url || window.location.hostname + "/" + details.link_id,
|
||||
originalUrl: details.original_url || "",
|
||||
creator: details.created_by || "Unknown",
|
||||
createdAt: details.created_at || new Date().toISOString(),
|
||||
visits: details.visits || 0,
|
||||
visitChange: details.visit_change || 0,
|
||||
uniqueVisitors: details.unique_visitors || 0,
|
||||
uniqueVisitorsChange: details.unique_visitors_change || 0,
|
||||
avgTime: formatTime(details.avg_time_spent || 0),
|
||||
avgTimeChange: details.avg_time_change || 0,
|
||||
conversionRate: details.conversion_rate || 0,
|
||||
conversionChange: details.conversion_change || 0,
|
||||
status: details.is_active ? "active" : "inactive",
|
||||
tags: details.tags || [],
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch link details:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 获取并设置linkId - 移除异步获取参数的逻辑,因为params已经可用
|
||||
useEffect(() => {
|
||||
// 直接使用params.id
|
||||
if (params.id) {
|
||||
setLinkId(params.id);
|
||||
fetchLinkDetails(params.id);
|
||||
fetchAnalyticsData(params.id);
|
||||
}
|
||||
}, [params.id, fetchLinkDetails, fetchAnalyticsData]);
|
||||
|
||||
// 获取链接详情数据 - 这个 useEffect 可以删除,已合并到上面的 useEffect
|
||||
|
||||
// 更新时间粒度并重新获取趋势数据
|
||||
const updateTimeGranularity = (granularity: TimeGranularity) => {
|
||||
setTimeGranularity(granularity);
|
||||
|
||||
Reference in New Issue
Block a user