Migrate TED dashboard chart to summary API
continuous-integration/drone/push Build is passing

This commit is contained in:
AmirReza Jamali
2026-07-04 12:51:28 +03:30
parent 6b6dd7d2d1
commit fc0d45e909
6 changed files with 38 additions and 16 deletions
+15 -8
View File
@@ -27,7 +27,9 @@ const stubDashboardRequests = () => {
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
cy.intercept("GET", "**/api/proxy/dashboard/summary**", (req) => { cy.intercept("GET", "**/api/proxy/dashboard/summary**", (req) => {
expect(req.query.closing_window).to.be.undefined; const days = Number(req.query.days);
expect(req.query.closing_window).to.eq("168");
req.alias = days === 30 ? "summary30Days" : "summary14Days";
req.reply({ req.reply({
statusCode: 200, statusCode: 200,
body: apiResponse({ body: apiResponse({
@@ -39,10 +41,15 @@ const stubDashboardRequests = () => {
closing_soon: 12, closing_soon: 12,
total_estimated_value: 2500000, total_estimated_value: 2500000,
value_currency: "EUR", value_currency: "EUR",
days,
scraped_ted: [
{ date: "2026-06-08", count: days === 30 ? 12 : 5 },
{ date: "2026-06-09", count: days === 30 ? 18 : 8 },
],
generated_at: now, generated_at: now,
}), }),
}); });
}).as("dashboardSummary"); });
cy.intercept("GET", "**/api/proxy/dashboard/trend**", (req) => { cy.intercept("GET", "**/api/proxy/dashboard/trend**", (req) => {
expect(req.query).to.include({ days: "14", metric: "created" }); expect(req.query).to.include({ days: "14", metric: "created" });
@@ -139,10 +146,6 @@ const stubDashboardRequests = () => {
days, days,
generated_at: now, generated_at: now,
daily: { daily: {
scraped_ted: [
{ date: "2026-06-08", count: days === 30 ? 12 : 5 },
{ date: "2026-06-09", count: days === 30 ? 18 : 8 },
],
scraped_documents: [ scraped_documents: [
{ date: "2026-06-08", count: days === 30 ? 40 : 20 }, { date: "2026-06-08", count: days === 30 ? 40 : 20 },
{ date: "2026-06-09", count: days === 30 ? 60 : 30 }, { date: "2026-06-09", count: days === 30 ? 60 : 30 },
@@ -155,6 +158,7 @@ const stubDashboardRequests = () => {
totals: { totals: {
scraped_documents: days === 30 ? 9876 : 4321, scraped_documents: days === 30 ? 9876 : 4321,
translated_tenders: days === 30 ? 654 : 321, translated_tenders: days === 30 ? 654 : 321,
scraped_ted_notices: days === 30 ? 2468 : 1234,
}, },
}), }),
}); });
@@ -171,7 +175,7 @@ describe("tender dashboard", () => {
visitDashboard(); visitDashboard();
cy.wait([ cy.wait([
"@dashboardSummary", "@summary14Days",
"@dashboardTrend", "@dashboardTrend",
"@dashboardCountries", "@dashboardCountries",
"@dashboardNoticeTypes", "@dashboardNoticeTypes",
@@ -213,10 +217,13 @@ describe("tender dashboard", () => {
it("reloads scraping and translation statistics for a selected range", () => { it("reloads scraping and translation statistics for a selected range", () => {
visitDashboard(); visitDashboard();
cy.wait("@statistics14Days"); cy.wait(["@summary14Days", "@statistics14Days"]);
cy.contains("button", "14d").should("have.class", "bg-primary"); cy.contains("button", "14d").should("have.class", "bg-primary");
cy.contains("button", "30d").click(); cy.contains("button", "30d").click();
cy.wait("@summary30Days")
.its("request.query.days")
.should("eq", "30");
cy.wait("@statistics30Days") cy.wait("@statistics30Days")
.its("request.query.days") .its("request.query.days")
.should("eq", "30"); .should("eq", "30");
@@ -15,7 +15,10 @@ import { toLabeledPoints } from "./to-labeled-points";
export type DashboardMetrics = ReturnType<typeof useDashboardData>; export type DashboardMetrics = ReturnType<typeof useDashboardData>;
export function useDashboardData() { export function useDashboardData() {
const summaryQuery = useDashboardSummaryQuery(); const summaryQuery = useDashboardSummaryQuery({
closing_window: 168,
days: 14,
});
const trendQuery = useDashboardTrendQuery({ days: 14, metric: "created" }); const trendQuery = useDashboardTrendQuery({ days: 14, metric: "created" });
const countriesQuery = useDashboardCountriesQuery({ limit: 6 }); const countriesQuery = useDashboardCountriesQuery({ limit: 6 });
const noticeTypesQuery = useDashboardNoticeTypesQuery(); const noticeTypesQuery = useDashboardNoticeTypesQuery();
@@ -1,6 +1,9 @@
"use client"; "use client";
import { useDashboardStatisticsQuery } from "@/hooks/queries"; import {
useDashboardStatisticsQuery,
useDashboardSummaryQuery,
} from "@/hooks/queries";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { toLabeledPoints } from "./to-labeled-points"; import { toLabeledPoints } from "./to-labeled-points";
@@ -11,14 +14,16 @@ export type StatisticsDays = (typeof STATISTICS_DAY_OPTIONS)[number];
export function useStatistics() { export function useStatistics() {
const [days, setDays] = useState<StatisticsDays>(14); const [days, setDays] = useState<StatisticsDays>(14);
const summaryQuery = useDashboardSummaryQuery({ closing_window: 168, days });
const query = useDashboardStatisticsQuery({ days }); const query = useDashboardStatisticsQuery({ days });
const summary = summaryQuery.data?.data;
const stats = query.data?.data; const stats = query.data?.data;
const daily = stats?.daily; const daily = stats?.daily;
const scrapedTed = useMemo( const scrapedTed = useMemo(
() => toLabeledPoints(daily?.scraped_ted ?? []), () => toLabeledPoints(summary?.scraped_ted ?? []),
[daily], [summary],
); );
const scrapedDocuments = useMemo( const scrapedDocuments = useMemo(
() => toLabeledPoints(daily?.scraped_documents ?? []), () => toLabeledPoints(daily?.scraped_documents ?? []),
@@ -32,9 +37,12 @@ export function useStatistics() {
return { return {
days, days,
setDays, setDays,
isLoading: query.isPending, isLoading: query.isPending || summaryQuery.isPending,
isError: query.isError, isError: query.isError || summaryQuery.isError,
refetch: query.refetch, refetch: () => {
summaryQuery.refetch();
query.refetch();
},
scrapedTed, scrapedTed,
scrapedDocuments, scrapedDocuments,
translatedNotices, translatedNotices,
+1
View File
@@ -4,6 +4,7 @@ import { useMemo } from "react";
export const useDashboardSummaryQuery = (params?: { export const useDashboardSummaryQuery = (params?: {
closing_window?: number; closing_window?: number;
days?: number;
}) => { }) => {
const queryKey = useMemo( const queryKey = useMemo(
() => [API_ENDPOINTS.DASHBOARD.SUMMARY, params], () => [API_ENDPOINTS.DASHBOARD.SUMMARY, params],
@@ -13,6 +13,7 @@ import {
export const dashboardService = { export const dashboardService = {
summary: async (params?: { summary: async (params?: {
closing_window?: number; closing_window?: number;
days?: number;
}): Promise<ApiResponse<TDashboardSummary>> => { }): Promise<ApiResponse<TDashboardSummary>> => {
try { try {
return (await api.get(API_ENDPOINTS.DASHBOARD.SUMMARY, { params })).data; return (await api.get(API_ENDPOINTS.DASHBOARD.SUMMARY, { params })).data;
+3 -1
View File
@@ -7,6 +7,8 @@ export type TDashboardSummary = {
closing_soon: number; closing_soon: number;
total_estimated_value: number; total_estimated_value: number;
value_currency: string; value_currency: string;
days: number;
scraped_ted: TDashboardTrendPoint[];
generated_at: number; generated_at: number;
}; };
@@ -22,7 +24,6 @@ export type TDashboardTrend = {
}; };
export type TDashboardStatisticsDaily = { export type TDashboardStatisticsDaily = {
scraped_ted: TDashboardTrendPoint[];
scraped_documents: TDashboardTrendPoint[]; scraped_documents: TDashboardTrendPoint[];
translated_notices: TDashboardTrendPoint[]; translated_notices: TDashboardTrendPoint[];
}; };
@@ -30,6 +31,7 @@ export type TDashboardStatisticsDaily = {
export type TDashboardStatisticsTotals = { export type TDashboardStatisticsTotals = {
scraped_documents: number; scraped_documents: number;
translated_tenders: number; translated_tenders: number;
scraped_ted_notices: number;
}; };
export type TDashboardStatistics = { export type TDashboardStatistics = {