This commit is contained in:
2025-04-02 20:50:10 +08:00
parent 0f8419778c
commit 95f230b996
3 changed files with 34 additions and 27 deletions

View File

@@ -27,7 +27,7 @@ export default function GeoAnalytics({ data }: GeoAnalyticsProps) {
<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">
Location
IP Address
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Visits
@@ -41,30 +41,38 @@ export default function GeoAnalytics({ data }: GeoAnalyticsProps) {
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{sortedData.map((item, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{item.location || 'Unknown'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatNumber(item.visits)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatNumber(item.visitors)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div className="flex items-center">
<div className="w-24 bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${item.percentage || 0}%` }}
/>
{sortedData.length > 0 ? (
sortedData.map((item, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{item.location || 'Unknown'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatNumber(item.visits)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatNumber(item.visitors)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div className="flex items-center">
<div className="w-24 bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${item.percentage || 0}%` }}
/>
</div>
<span className="ml-2">{formatPercent(item.percentage)}%</span>
</div>
<span className="ml-2">{formatPercent(item.percentage)}%</span>
</div>
</td>
</tr>
))
) : (
<tr>
<td colSpan={4} className="px-6 py-4 text-center text-sm text-gray-500">
No IP data available
</td>
</tr>
))}
)}
</tbody>
</table>
</div>

View File

@@ -763,7 +763,7 @@ export default function HomePage() {
<div className="bg-white rounded-lg shadow p-6 mb-8">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Geographic Distribution</h2>
<GeoAnalytics data={geoData} />
</div>
</div>
</>
</div>
);

View File

@@ -215,23 +215,22 @@ export async function getGeoAnalytics(params: {
startTime?: string;
endTime?: string;
linkId?: string;
groupBy?: 'country' | 'city';
teamIds?: string[];
projectIds?: string[];
tagIds?: string[];
}): Promise<GeoData[]> {
const filter = buildFilter(params);
const groupByField = params.groupBy === 'city' ? 'city' : 'country';
// Use IP address as the grouping field
const query = `
SELECT
${groupByField} as location,
ip_address as location,
count() as visits,
uniq(ip_address) as visitors,
count() * 100.0 / sum(count()) OVER () as percentage
FROM events
${filter}
GROUP BY ${groupByField}
GROUP BY location
HAVING location != ''
ORDER BY visits DESC
LIMIT 20