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

View File

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

View File

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