time chart int
This commit is contained in:
@@ -175,9 +175,46 @@ export default function AnalyticsPage() {
|
||||
// 如果 localStorage 中没有匹配的数据,则从 API 获取
|
||||
const fetchShortUrlData = async () => {
|
||||
try {
|
||||
// 构建带有 URL 参数的查询字符串
|
||||
const encodedUrl = encodeURIComponent(shorturlParam);
|
||||
const apiUrl = `/api/shortlinks/byUrl?url=${encodedUrl}`;
|
||||
let apiUrl = '';
|
||||
|
||||
// Check if shorturlParam is a URL or an ID
|
||||
if (shorturlParam.startsWith('http')) {
|
||||
// Direct match by shortUrl is more reliable than URL parsing
|
||||
const exactApiUrl = `/api/shortlinks/exact?shortUrl=${encodeURIComponent(shorturlParam)}`;
|
||||
console.log('Fetching shorturl by exact match:', exactApiUrl);
|
||||
|
||||
// Try the exact endpoint first
|
||||
const response = await fetch(exactApiUrl);
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.data) {
|
||||
console.log('Found shortlink by exact shortUrl match:', result.data);
|
||||
console.log('External ID from exact API:', result.data.externalId);
|
||||
|
||||
if (result.data.externalId) {
|
||||
// Save to sessionStorage for immediate use
|
||||
sessionStorage.setItem('current_shorturl_external_id', result.data.externalId);
|
||||
console.log('Saved external ID to sessionStorage:', result.data.externalId);
|
||||
}
|
||||
|
||||
// Set in store and trigger data fetch
|
||||
setSelectedShortUrl(result.data);
|
||||
setTimeout(() => {
|
||||
setShouldFetchData(true);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to old method if exact match fails
|
||||
console.log('Exact match failed, trying byUrl endpoint');
|
||||
apiUrl = `/api/shortlinks/byUrl?url=${encodeURIComponent(shorturlParam)}`;
|
||||
} else {
|
||||
// It might be an ID or slug, try the ID endpoint directly
|
||||
apiUrl = `/api/shortlinks/${shorturlParam}`;
|
||||
}
|
||||
|
||||
console.log('Fetching shorturl data from:', apiUrl);
|
||||
|
||||
@@ -186,7 +223,7 @@ export default function AnalyticsPage() {
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch shorturl data:', response.statusText);
|
||||
// Trigger data fetching even if shortURL data fetch failed
|
||||
// Still trigger data fetching to show all data instead
|
||||
setShouldFetchData(true);
|
||||
return;
|
||||
}
|
||||
@@ -196,12 +233,28 @@ export default function AnalyticsPage() {
|
||||
// 如果找到匹配的短链接数据
|
||||
if (result.success && result.data) {
|
||||
console.log('Retrieved shortlink data:', result.data);
|
||||
// Log the external ID explicitly for debugging
|
||||
console.log('External ID from API:', result.data.externalId);
|
||||
|
||||
// 设置到 Zustand store (会自动更新到 localStorage)
|
||||
setSelectedShortUrl(result.data);
|
||||
|
||||
// 强制保证 externalId 被设置到 params
|
||||
const savedExternalId = result.data.externalId;
|
||||
if (savedExternalId) {
|
||||
// Save to sessionStorage for immediate use
|
||||
sessionStorage.setItem('current_shorturl_external_id', savedExternalId);
|
||||
console.log('Saved external ID to sessionStorage:', savedExternalId);
|
||||
}
|
||||
|
||||
// Explicitly wait for the state update to be applied
|
||||
// before triggering the data fetching
|
||||
setTimeout(() => {
|
||||
setShouldFetchData(true);
|
||||
}, 100);
|
||||
} else {
|
||||
setShouldFetchData(true);
|
||||
}
|
||||
|
||||
// Trigger data fetching after shortURL data is processed
|
||||
setShouldFetchData(true);
|
||||
} catch (error) {
|
||||
console.error('Error fetching shorturl data:', error);
|
||||
// Trigger data fetching even if there was an error
|
||||
@@ -275,10 +328,33 @@ export default function AnalyticsPage() {
|
||||
pageSize: pageSize.toString()
|
||||
});
|
||||
|
||||
// Add linkId parameter if a shorturl is selected
|
||||
if (selectedShortUrl && selectedShortUrl.id) {
|
||||
params.append('linkId', selectedShortUrl.id);
|
||||
console.log('Adding linkId to requests:', selectedShortUrl.id);
|
||||
// Verify the shortUrl data is loaded and the externalId exists
|
||||
// before adding the linkId parameter
|
||||
if (selectedShortUrl) {
|
||||
console.log('Current selectedShortUrl data:', selectedShortUrl);
|
||||
|
||||
if (selectedShortUrl.externalId) {
|
||||
params.append('linkId', selectedShortUrl.externalId);
|
||||
console.log('Adding linkId (externalId) to requests:', selectedShortUrl.externalId);
|
||||
} else {
|
||||
// Try to get externalId from sessionStorage as backup
|
||||
const savedExternalId = sessionStorage.getItem('current_shorturl_external_id');
|
||||
if (savedExternalId) {
|
||||
params.append('linkId', savedExternalId);
|
||||
console.log('Adding linkId from sessionStorage:', savedExternalId);
|
||||
} else {
|
||||
// External ID is missing - this will result in no data being returned
|
||||
console.warn('WARNING: externalId is missing in the shortUrl data - no results will be returned!', selectedShortUrl);
|
||||
}
|
||||
}
|
||||
// We now know the events table exclusively uses external_id format, so never fall back to id
|
||||
|
||||
// Add an extra log to debug the issue
|
||||
console.log('Complete shorturl data:', JSON.stringify({
|
||||
id: selectedShortUrl.id,
|
||||
externalId: selectedShortUrl.externalId,
|
||||
shortUrl: selectedShortUrl.shortUrl
|
||||
}));
|
||||
}
|
||||
|
||||
// 添加团队ID参数 - 支持多个团队
|
||||
@@ -302,15 +378,38 @@ export default function AnalyticsPage() {
|
||||
});
|
||||
}
|
||||
|
||||
// 记录构建的 URL,以确保参数正确包含
|
||||
const summaryUrl = `${baseUrl}/summary?${params.toString()}`;
|
||||
const timeSeriesUrl = `${baseUrl}/time-series?${params.toString()}`;
|
||||
const geoUrl = `${baseUrl}/geo?${params.toString()}`;
|
||||
const devicesUrl = `${baseUrl}/devices?${params.toString()}`;
|
||||
const eventsUrl = `${baseUrl}?${params.toString()}`;
|
||||
|
||||
console.log('Final API URLs being called:');
|
||||
console.log('- Summary API:', summaryUrl);
|
||||
console.log('- TimeSeries API:', timeSeriesUrl);
|
||||
console.log(`- Params contain linkId? ${params.has('linkId')}`);
|
||||
console.log(`- All params: ${params.toString()}`);
|
||||
|
||||
// 并行获取所有数据
|
||||
const [summaryRes, timeSeriesRes, geoRes, deviceRes, eventsRes] = await Promise.all([
|
||||
fetch(`${baseUrl}/summary?${params.toString()}`),
|
||||
fetch(`${baseUrl}/time-series?${params.toString()}`),
|
||||
fetch(`${baseUrl}/geo?${params.toString()}`),
|
||||
fetch(`${baseUrl}/devices?${params.toString()}`),
|
||||
fetch(`${baseUrl}?${params.toString()}`)
|
||||
fetch(summaryUrl),
|
||||
fetch(timeSeriesUrl),
|
||||
fetch(geoUrl),
|
||||
fetch(devicesUrl),
|
||||
fetch(eventsUrl)
|
||||
]);
|
||||
|
||||
// 添加额外日志,记录完整的 URL 请求
|
||||
console.log('Summary API URL:', summaryUrl);
|
||||
|
||||
if (selectedShortUrl?.externalId) {
|
||||
console.log('Verifying linkId is in params:',
|
||||
`linkId=${selectedShortUrl.externalId}`,
|
||||
`included: ${params.toString().includes(`linkId=${selectedShortUrl.externalId}`)}`
|
||||
);
|
||||
}
|
||||
|
||||
const [summaryData, timeSeriesData, geoData, deviceData, eventsData] = await Promise.all([
|
||||
summaryRes.json(),
|
||||
timeSeriesRes.json(),
|
||||
@@ -402,6 +501,68 @@ export default function AnalyticsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Debug info - remove in production */}
|
||||
{process.env.NODE_ENV !== 'production' && (
|
||||
<div className="mb-4 p-3 bg-gray-100 rounded text-xs overflow-auto max-h-80">
|
||||
<h3 className="font-bold mb-1">Debug Info:</h3>
|
||||
<div>
|
||||
<strong>Hydrated:</strong> {isHydrated ? 'Yes' : 'No'} |
|
||||
<strong> Should Fetch:</strong> {shouldFetchData ? 'Yes' : 'No'} |
|
||||
<strong> Has ShortUrl:</strong> {selectedShortUrl ? 'Yes' : 'No'}
|
||||
</div>
|
||||
{selectedShortUrl && (
|
||||
<div className="mt-1">
|
||||
<strong>ShortUrl ID:</strong> {selectedShortUrl.id} |
|
||||
<strong> ExternalId:</strong> {selectedShortUrl.externalId || 'MISSING'} |
|
||||
<strong> URL:</strong> {selectedShortUrl.shortUrl}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-1 text-xs text-red-500">
|
||||
<strong>IMPORTANT: </strong>
|
||||
The events table uses <code>external_id</code> as <code>link_id</code>, not the UUID format.
|
||||
External ID format sample: <code>cm8x34sdr0007m11yh1xe6qc2</code>
|
||||
</div>
|
||||
|
||||
{/* Full link data for debugging */}
|
||||
{selectedShortUrl && (
|
||||
<div className="mt-3 border-t pt-2">
|
||||
<details>
|
||||
<summary className="cursor-pointer font-medium text-blue-600">Show Full Link Data</summary>
|
||||
<div className="mt-2 p-2 bg-gray-800 text-green-400 rounded overflow-auto max-h-96 whitespace-pre">
|
||||
{JSON.stringify(selectedShortUrl, null, 2)}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* URL Parameters */}
|
||||
<div className="mt-3 border-t pt-2">
|
||||
<details>
|
||||
<summary className="cursor-pointer font-medium text-blue-600">API Request URLs</summary>
|
||||
<div className="mt-2">
|
||||
<div><strong>Summary API URL:</strong> {`/api/events/summary?${new URLSearchParams({
|
||||
startTime: format(dateRange.from, "yyyy-MM-dd'T'HH:mm:ss'Z'"),
|
||||
endTime: format(dateRange.to, "yyyy-MM-dd'T'HH:mm:ss'Z'"),
|
||||
...(selectedShortUrl?.externalId ? { linkId: selectedShortUrl.externalId } : {})
|
||||
}).toString()}`}</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
{/* Local Storage Data */}
|
||||
<div className="mt-3 border-t pt-2">
|
||||
<details>
|
||||
<summary className="cursor-pointer font-medium text-blue-600">LocalStorage Data</summary>
|
||||
<div className="mt-2 p-2 bg-gray-800 text-green-400 rounded overflow-auto max-h-96 whitespace-pre">
|
||||
{typeof window !== 'undefined' && localStorage.getItem('shorturl-storage') ?
|
||||
JSON.stringify(JSON.parse(localStorage.getItem('shorturl-storage') || '{}'), null, 2) :
|
||||
'No localStorage data'}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h1 className="text-2xl font-bold text-gray-900">Analytics Dashboard</h1>
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-center">
|
||||
@@ -424,7 +585,8 @@ export default function AnalyticsPage() {
|
||||
</div>
|
||||
<div className="text-xs mt-1 text-blue-700">
|
||||
<span>Analytics filtered for this short URL only</span>
|
||||
{selectedShortUrl.id && <span className="ml-2 text-blue-500">(ID: {selectedShortUrl.id})</span>}
|
||||
{selectedShortUrl.id && <span className="ml-2 text-blue-500">(ID: {selectedShortUrl.id.substring(0, 8)}...)</span>}
|
||||
{selectedShortUrl.externalId && <span className="ml-2 text-blue-500">(External ID: {selectedShortUrl.externalId})</span>}
|
||||
</div>
|
||||
{selectedShortUrl.tags && selectedShortUrl.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
@@ -727,7 +889,7 @@ export default function AnalyticsPage() {
|
||||
<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) || '-'}
|
||||
ID: {event.link_id || '-'}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-blue-600">
|
||||
|
||||
Reference in New Issue
Block a user