72948a9b55
- Introduced DailyTrendChart component for visualizing daily scraped TED notices, documents, and translated notices. - Implemented StatisticsSection to display key metrics with animated counters and a days selector for data range. - Created utility function to convert API data into labeled points for chart rendering. - Updated dashboard index to include the new StatisticsSection, enhancing overall dashboard functionality and user experience. - Refactored existing trends chart to utilize the new DailyPoint type for consistency in data handling.
109 lines
2.8 KiB
TypeScript
109 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
useDashboardClosingSoonQuery,
|
|
useDashboardCountriesQuery,
|
|
useDashboardNoticeTypesQuery,
|
|
useDashboardSummaryQuery,
|
|
useDashboardTrendQuery,
|
|
useTendersQuery,
|
|
} from "@/hooks/queries";
|
|
import { useMemo } from "react";
|
|
import { toLabeledPoints } from "./to-labeled-points";
|
|
|
|
export type DashboardMetrics = ReturnType<typeof useDashboardData>;
|
|
|
|
export function useDashboardData() {
|
|
const summaryQuery = useDashboardSummaryQuery();
|
|
const trendQuery = useDashboardTrendQuery({ days: 14, metric: "created" });
|
|
const countriesQuery = useDashboardCountriesQuery({ limit: 6 });
|
|
const noticeTypesQuery = useDashboardNoticeTypesQuery();
|
|
const closingSoonQuery = useDashboardClosingSoonQuery({ limit: 5 });
|
|
|
|
const {
|
|
data: recentData,
|
|
isPending: recentPending,
|
|
isError: recentError,
|
|
refetch: refetchRecent,
|
|
} = useTendersQuery({
|
|
sort_by: "created_at",
|
|
sort_order: "desc",
|
|
offset: 0,
|
|
limit: 5,
|
|
});
|
|
|
|
const summary = summaryQuery.data?.data;
|
|
const trendResponse = trendQuery.data?.data;
|
|
const countriesResponse = countriesQuery.data?.data;
|
|
const noticeTypesResponse = noticeTypesQuery.data?.data;
|
|
const closingSoonList = closingSoonQuery.data?.data?.items ?? [];
|
|
|
|
const isPending =
|
|
summaryQuery.isPending ||
|
|
trendQuery.isPending ||
|
|
countriesQuery.isPending ||
|
|
noticeTypesQuery.isPending ||
|
|
closingSoonQuery.isPending;
|
|
|
|
const isError =
|
|
summaryQuery.isError ||
|
|
trendQuery.isError ||
|
|
countriesQuery.isError ||
|
|
noticeTypesQuery.isError ||
|
|
closingSoonQuery.isError ||
|
|
recentError;
|
|
|
|
const refetch = () => {
|
|
summaryQuery.refetch();
|
|
trendQuery.refetch();
|
|
countriesQuery.refetch();
|
|
noticeTypesQuery.refetch();
|
|
closingSoonQuery.refetch();
|
|
refetchRecent();
|
|
};
|
|
|
|
const trend = useMemo(
|
|
() => toLabeledPoints(trendResponse?.series ?? []),
|
|
[trendResponse],
|
|
);
|
|
|
|
const countries = useMemo(
|
|
() =>
|
|
(countriesResponse?.items ?? []).map((item) => ({
|
|
code: item.country_code,
|
|
count: item.count,
|
|
})),
|
|
[countriesResponse],
|
|
);
|
|
|
|
const noticeTypes = useMemo(
|
|
() =>
|
|
(noticeTypesResponse?.items ?? []).map((item) => ({
|
|
type: item.type,
|
|
count: item.count,
|
|
})),
|
|
[noticeTypesResponse],
|
|
);
|
|
|
|
const recent = recentData?.data?.tenders ?? [];
|
|
const recentIsLoading = recentPending && recent.length === 0;
|
|
|
|
return {
|
|
isPending,
|
|
isError,
|
|
refetch,
|
|
total: summary?.total_tenders ?? 0,
|
|
active: summary?.active_tenders ?? 0,
|
|
awarded: summary?.awarded_tenders ?? 0,
|
|
closingSoon: summary?.closing_soon ?? 0,
|
|
totalValue: summary?.total_estimated_value ?? 0,
|
|
valueCurrency: summary?.value_currency ?? "EUR",
|
|
countries,
|
|
noticeTypes,
|
|
trend,
|
|
recent,
|
|
recentIsLoading,
|
|
closingSoonList,
|
|
};
|
|
}
|