Files
tm_panel/src/app/(home)/_components/dashboard/use-dashboard-data.ts
T
AmirReza Jamali 72948a9b55 feat(dashboard): add daily trend chart and statistics section to dashboard
- 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.
2026-06-07 12:39:15 +03:30

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,
};
}