fix statistics; i need to make unit tests
This commit is contained in:
parent
67e94b4483
commit
c2d5f5a644
4 changed files with 47 additions and 41 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue