← back to Open Seo
src/client/features/domain/hooks/useDomainKeywordsQuery.ts
122 lines
import { useEffect, useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
import { getDomainKeywordsPage } from "@/serverFunctions/domain";
import { debugDomain } from "@/client/features/domain/domainDebug";
import type {
DomainFilterValues,
DomainSortMode,
SortOrder,
} from "@/client/features/domain/types";
type DomainKeywordsQueryInput = {
projectId: string;
domain: string;
includeSubdomains: boolean;
locationCode: number | undefined;
page: number;
pageSize: number;
sortMode: DomainSortMode;
sortOrder: SortOrder;
appliedFilters: DomainFilterValues;
enabled: boolean;
};
function toNumberOrUndefined(value: string): number | undefined {
const trimmed = value.trim();
if (trimmed === "") return undefined;
const parsed = Number(trimmed);
return Number.isFinite(parsed) ? parsed : undefined;
}
function toFiltersPayload(
filters: DomainFilterValues,
): Record<string, unknown> {
return {
include: filters.include || undefined,
exclude: filters.exclude || undefined,
minTraffic: toNumberOrUndefined(filters.minTraffic),
maxTraffic: toNumberOrUndefined(filters.maxTraffic),
minVol: toNumberOrUndefined(filters.minVol),
maxVol: toNumberOrUndefined(filters.maxVol),
minCpc: toNumberOrUndefined(filters.minCpc),
maxCpc: toNumberOrUndefined(filters.maxCpc),
minKd: toNumberOrUndefined(filters.minKd),
maxKd: toNumberOrUndefined(filters.maxKd),
minRank: toNumberOrUndefined(filters.minRank),
maxRank: toNumberOrUndefined(filters.maxRank),
};
}
export function useDomainKeywordsQuery(input: DomainKeywordsQueryInput) {
const filtersPayload = useMemo(
() => toFiltersPayload(input.appliedFilters),
[input.appliedFilters],
);
const queryKey = useMemo(
() => [
"domain-keywords",
input.projectId,
input.domain,
input.includeSubdomains,
input.locationCode,
input.page,
input.pageSize,
input.sortMode,
input.sortOrder,
filtersPayload,
],
[
filtersPayload,
input.domain,
input.includeSubdomains,
input.locationCode,
input.page,
input.pageSize,
input.projectId,
input.sortMode,
input.sortOrder,
],
);
useEffect(() => {
debugDomain("useDomainKeywordsQuery:key", {
queryKey,
enabled: input.enabled && Boolean(input.domain),
});
}, [input.domain, input.enabled, queryKey]);
const query = useQuery({
enabled: input.enabled && Boolean(input.domain),
queryKey,
queryFn: () =>
getDomainKeywordsPage({
data: {
projectId: input.projectId,
domain: input.domain,
includeSubdomains: input.includeSubdomains,
locationCode: input.locationCode,
page: input.page,
pageSize: input.pageSize,
sortMode: input.sortMode,
sortOrder: input.sortOrder,
filters: filtersPayload,
},
}),
staleTime: 60_000,
});
useEffect(() => {
debugDomain("useDomainKeywordsQuery:state", {
status: query.status,
fetchStatus: query.fetchStatus,
isFetching: query.isFetching,
rows: query.data?.keywords.length ?? 0,
});
}, [
query.data?.keywords.length,
query.fetchStatus,
query.isFetching,
query.status,
]);
return query;
}