events date range 7days
This commit is contained in:
@@ -7,8 +7,8 @@ import { Event, EventType } from '@/lib/types';
|
|||||||
|
|
||||||
export default function EventsPage() {
|
export default function EventsPage() {
|
||||||
const [dateRange, setDateRange] = useState({
|
const [dateRange, setDateRange] = useState({
|
||||||
from: new Date('2024-02-01'),
|
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // 7天前
|
||||||
to: new Date('2025-03-05')
|
to: new Date() // 今天
|
||||||
});
|
});
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
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"
|
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>
|
||||||
|
<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>
|
<div>
|
||||||
<select
|
<select
|
||||||
value={filters.sortBy}
|
value={filters.sortBy}
|
||||||
@@ -250,33 +268,6 @@ export default function EventsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div className="mt-4 flex justify-end">
|
||||||
<button
|
<button
|
||||||
onClick={resetFilters}
|
onClick={resetFilters}
|
||||||
|
|||||||
@@ -186,66 +186,6 @@ export default function LinkDetailsPage({
|
|||||||
}
|
}
|
||||||
}, [dateRange, timeGranularity]);
|
}, [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 formatTime = (seconds: number) => {
|
||||||
const minutes = Math.floor(seconds / 60);
|
const minutes = Math.floor(seconds / 60);
|
||||||
@@ -253,6 +193,59 @@ export default function LinkDetailsPage({
|
|||||||
return `${minutes}m ${remainingSeconds}s`;
|
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) => {
|
const updateTimeGranularity = (granularity: TimeGranularity) => {
|
||||||
setTimeGranularity(granularity);
|
setTimeGranularity(granularity);
|
||||||
|
|||||||
Reference in New Issue
Block a user