fix statistics; i need to make unit tests

This commit is contained in:
Wavering Ana 2025-01-29 21:57:59 -05:00
parent 67e94b4483
commit c2d5f5a644
4 changed files with 47 additions and 41 deletions

View file

@ -63,15 +63,26 @@ export const deleteLink = async (id: number) => {
}; };
export const getLinkClickStats = async (id: number) => { export const getLinkClickStats = async (id: number) => {
try {
const response = await api.get<ClickStats[]>(`/links/${id}/clicks`); const response = await api.get<ClickStats[]>(`/links/${id}/clicks`);
return response.data; return response.data;
} catch (error) {
console.error('Error fetching click stats:', error);
throw error;
}
}; };
export const getLinkSourceStats = async (id: number) => { export const getLinkSourceStats = async (id: number) => {
try {
const response = await api.get<SourceStats[]>(`/links/${id}/sources`); const response = await api.get<SourceStats[]>(`/links/${id}/sources`);
return response.data; return response.data;
} catch (error) {
console.error('Error fetching source stats:', error);
throw error;
}
}; };
export const checkFirstUser = async () => { export const checkFirstUser = async () => {
const response = await api.get<{ isFirstUser: boolean }>('/auth/check-first-user'); const response = await api.get<{ isFirstUser: boolean }>('/auth/check-first-user');
return response.data.isFirstUser; return response.data.isFirstUser;

View file

@ -9,6 +9,7 @@ import {
ResponsiveContainer, ResponsiveContainer,
} from "recharts"; } from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { toast } from "@/hooks/use-toast"
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { getLinkClickStats, getLinkSourceStats } from '../api/client'; import { getLinkClickStats, getLinkSourceStats } from '../api/client';
@ -36,8 +37,13 @@ export function StatisticsModal({ isOpen, onClose, linkId }: StatisticsModalProp
]); ]);
setClicksOverTime(clicksData); setClicksOverTime(clicksData);
setSourcesData(sourcesData); setSourcesData(sourcesData);
} catch (error) { } catch (error: any) {
console.error("Failed to fetch statistics:", error); console.error("Failed to fetch statistics:", error);
toast({
variant: "destructive",
title: "Error",
description: error.response?.data || "Failed to load statistics",
});
} finally { } finally {
setLoading(false); setLoading(false);
} }

View file

@ -537,31 +537,21 @@ pub async fn get_link_clicks(
) -> Result<impl Responder, AppError> { ) -> Result<impl Responder, AppError> {
let link_id = path.into_inner(); let link_id = path.into_inner();
// Verify the link belongs to the user // First verify the link belongs to the user
let link = match &state.db { let link = match &state.db {
DatabasePool::Postgres(pool) => { DatabasePool::Postgres(pool) => {
let mut tx = pool.begin().await?; sqlx::query_as::<_, (i32,)>("SELECT id FROM links WHERE id = $1 AND user_id = $2")
let link = sqlx::query_as::<Postgres, (i32,)>(
"SELECT id FROM links WHERE id = $1 AND user_id = $2",
)
.bind(link_id) .bind(link_id)
.bind(user.user_id) .bind(user.user_id)
.fetch_optional(&mut *tx) .fetch_optional(pool)
.await?; .await?
tx.commit().await?;
link
} }
DatabasePool::Sqlite(pool) => { DatabasePool::Sqlite(pool) => {
let mut tx = pool.begin().await?; sqlx::query_as::<_, (i32,)>("SELECT id FROM links WHERE id = ? AND user_id = ?")
let link = sqlx::query_as::<Sqlite, (i32,)>(
"SELECT id FROM links WHERE id = ? AND user_id = ?",
)
.bind(link_id) .bind(link_id)
.bind(user.user_id) .bind(user.user_id)
.fetch_optional(&mut *tx) .fetch_optional(pool)
.await?; .await?
tx.commit().await?;
link
} }
}; };
@ -571,11 +561,11 @@ pub async fn get_link_clicks(
let clicks = match &state.db { let clicks = match &state.db {
DatabasePool::Postgres(pool) => { DatabasePool::Postgres(pool) => {
sqlx::query_as::<Postgres, ClickStats>( sqlx::query_as::<_, ClickStats>(
r#" r#"
SELECT SELECT
DATE(created_at)::date as "date!", DATE(created_at) as date,
COUNT(*)::bigint as "clicks!" COUNT(*) as clicks
FROM clicks FROM clicks
WHERE link_id = $1 WHERE link_id = $1
GROUP BY DATE(created_at) GROUP BY DATE(created_at)
@ -588,11 +578,11 @@ pub async fn get_link_clicks(
.await? .await?
} }
DatabasePool::Sqlite(pool) => { DatabasePool::Sqlite(pool) => {
sqlx::query_as::<Sqlite, ClickStats>( sqlx::query_as::<_, ClickStats>(
r#" r#"
SELECT SELECT
DATE(created_at) as "date!", DATE(created_at) as date,
COUNT(*) as "clicks!" COUNT(*) as clicks
FROM clicks FROM clicks
WHERE link_id = ? WHERE link_id = ?
GROUP BY DATE(created_at) GROUP BY DATE(created_at)
@ -650,11 +640,11 @@ pub async fn get_link_sources(
let sources = match &state.db { let sources = match &state.db {
DatabasePool::Postgres(pool) => { DatabasePool::Postgres(pool) => {
sqlx::query_as::<Postgres, SourceStats>( sqlx::query_as::<_, SourceStats>(
r#" r#"
SELECT SELECT
query_source as "source!", query_source as source, // Remove the ! mark
COUNT(*)::bigint as "count!" COUNT(*)::bigint as count // Remove the ! mark
FROM clicks FROM clicks
WHERE link_id = $1 WHERE link_id = $1
AND query_source IS NOT NULL AND query_source IS NOT NULL
@ -669,11 +659,11 @@ pub async fn get_link_sources(
.await? .await?
} }
DatabasePool::Sqlite(pool) => { DatabasePool::Sqlite(pool) => {
sqlx::query_as::<Sqlite, SourceStats>( sqlx::query_as::<_, SourceStats>(
r#" r#"
SELECT SELECT
query_source as "source!", query_source as source,
COUNT(*) as "count!" COUNT(*) as count
FROM clicks FROM clicks
WHERE link_id = ? WHERE link_id = ?
AND query_source IS NOT NULL AND query_source IS NOT NULL

View file

@ -1,5 +1,4 @@
use anyhow::Result; use anyhow::Result;
use chrono::NaiveDate;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::postgres::PgRow; use sqlx::postgres::PgRow;
@ -145,7 +144,7 @@ pub struct User {
#[derive(sqlx::FromRow, Serialize)] #[derive(sqlx::FromRow, Serialize)]
pub struct ClickStats { pub struct ClickStats {
pub date: NaiveDate, pub date: String,
pub clicks: i64, pub clicks: i64,
} }