feat(dashboard): refactor home page to utilize TenderDashboard component

- Replaced the previous home page structure with a simplified TenderDashboard component for improved clarity and maintainability.
- Updated the .gitignore file to include IDE-specific files, ensuring a cleaner repository.
- Enhanced the useTenderListPresenter to support new parsing logic for cpv_codes in search parameters.
- Introduced a new utility function, buildQueryString, for better handling of query parameters in API requests.
This commit is contained in:
AmirReza Jamali
2026-05-18 10:59:16 +03:30
parent 76b3a4afb3
commit a238edc563
18 changed files with 2293 additions and 65 deletions
@@ -4,7 +4,12 @@ import { apiDefaultParams } from "@/constants/Api_Params";
import { useTendersQuery } from "@/hooks/queries";
import { useHybridPagination } from "@/hooks/useHybridPagination";
import { TCustomer } from "@/lib/api";
import { deleteEmptyKeys, getPaginatedRowNumber, unixToDate } from "@/utils/shared";
import {
buildQueryString,
deleteEmptyKeys,
getPaginatedRowNumber,
unixToDate,
} from "@/utils/shared";
import gsap from "gsap";
import { useIsMutating } from "@tanstack/react-query";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
@@ -16,9 +21,14 @@ const COMMA_SEPARATED_FILTER_KEYS = new Set([
"notice_types",
"form_types",
"classifications",
"cpv_codes",
]);
const parseCpvCodesFilter = (value: string): string[] =>
value
.split(",")
.map((item) => item.trim())
.filter(Boolean);
const normalizeFilterValues = (values: Record<string, any>) => {
const normalized: Record<string, any> = {};
@@ -33,6 +43,14 @@ const normalizeFilterValues = (values: Record<string, any>) => {
continue;
}
if (key === "cpv_codes") {
const codes = parseCpvCodesFilter(trimmedValue);
if (codes.length) {
normalized[key] = codes;
}
continue;
}
if (COMMA_SEPARATED_FILTER_KEYS.has(key)) {
normalized[key] = trimmedValue
.split(",")
@@ -76,8 +94,32 @@ const buildRangeFormValue = (
return undefined;
};
const formatCpvCodesForForm = (value: unknown): string => {
if (Array.isArray(value)) {
return value.map(String).filter(Boolean).join(", ");
}
return typeof value === "string" ? value : "";
};
const parseCpvCodesFromSearchParams = (
searchParams: URLSearchParams,
): string[] | undefined => {
const values = searchParams.getAll("cpv_codes").filter(Boolean);
if (!values.length) {
return undefined;
}
if (values.length === 1 && values[0].includes(",")) {
const legacy = parseCpvCodesFilter(values[0]);
return legacy.length ? legacy : undefined;
}
return values;
};
const buildTenderFilterFormValues = (params: Record<string, any>) => ({
...params,
cpv_codes: formatCpvCodesForForm(params.cpv_codes),
created_at_range: buildRangeFormValue(
params,
"created_at_from",
@@ -278,8 +320,16 @@ const useTenderListPresenter = () => {
offset: 0,
limit: 10,
};
for (const [key, value] of searchParams.entries()) {
newParams[key] = value;
for (const key of searchParams.keys()) {
if (key === "cpv_codes") {
continue;
}
newParams[key] = searchParams.get(key);
}
const cpvCodes = parseCpvCodesFromSearchParams(searchParams);
if (cpvCodes) {
newParams.cpv_codes = cpvCodes;
}
setParams(newParams);
filterFormReset(mergeTenderFilterFormState(newParams));
@@ -394,7 +444,7 @@ const useTenderListPresenter = () => {
}
const newParams = buildFirstPageParams({ ...merged, ...normalizedData });
const cleanedParams = deleteEmptyKeys(newParams);
const queryString = new URLSearchParams(cleanedParams).toString();
const queryString = buildQueryString(cleanedParams);
router.push(`${pathName}?${queryString}`);
};