chore: response page optimization (#6843)

Co-authored-by: igor-srdoc <igor@srdoc.si>
This commit is contained in:
Dhruwang Jariwala
2025-11-18 22:20:48 +05:30
committed by GitHub
parent 6999abba3b
commit 00a61f7abe
2 changed files with 31 additions and 14 deletions
@@ -26,6 +26,7 @@ interface ResponsePageProps {
isReadOnly: boolean; isReadOnly: boolean;
isQuotasAllowed: boolean; isQuotasAllowed: boolean;
quotas: TSurveyQuota[]; quotas: TSurveyQuota[];
initialResponses?: TResponseWithQuotas[];
} }
export const ResponsePage = ({ export const ResponsePage = ({
@@ -39,11 +40,12 @@ export const ResponsePage = ({
isReadOnly, isReadOnly,
isQuotasAllowed, isQuotasAllowed,
quotas, quotas,
initialResponses = [],
}: ResponsePageProps) => { }: ResponsePageProps) => {
const [responses, setResponses] = useState<TResponseWithQuotas[]>([]); const [responses, setResponses] = useState<TResponseWithQuotas[]>(initialResponses);
const [page, setPage] = useState<number>(1); const [page, setPage] = useState<number | null>(null);
const [hasMore, setHasMore] = useState<boolean>(true); const [hasMore, setHasMore] = useState<boolean>(initialResponses.length >= responsesPerPage);
const [isFetchingFirstPage, setFetchingFirstPage] = useState<boolean>(true); const [isFetchingFirstPage, setIsFetchingFirstPage] = useState<boolean>(false);
const { selectedFilter, dateRange, resetState } = useResponseFilter(); const { selectedFilter, dateRange, resetState } = useResponseFilter();
const filters = useMemo( const filters = useMemo(
@@ -56,6 +58,7 @@ export const ResponsePage = ({
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const fetchNextPage = useCallback(async () => { const fetchNextPage = useCallback(async () => {
if (page === null) return;
const newPage = page + 1; const newPage = page + 1;
let newResponses: TResponseWithQuotas[] = []; let newResponses: TResponseWithQuotas[] = [];
@@ -94,9 +97,14 @@ export const ResponsePage = ({
}, [searchParams, resetState]); }, [searchParams, resetState]);
useEffect(() => { useEffect(() => {
const fetchInitialResponses = async () => { const fetchFilteredResponses = async () => {
try { try {
setFetchingFirstPage(true); // skip call for initial mount
if (page === null) {
setPage(1);
return;
}
setIsFetchingFirstPage(true);
let responses: TResponseWithQuotas[] = []; let responses: TResponseWithQuotas[] = [];
const getResponsesActionResponse = await getResponsesAction({ const getResponsesActionResponse = await getResponsesAction({
@@ -110,19 +118,24 @@ export const ResponsePage = ({
if (responses.length < responsesPerPage) { if (responses.length < responsesPerPage) {
setHasMore(false); setHasMore(false);
} else {
setHasMore(true);
} }
setResponses(responses); setResponses(responses);
} finally { } finally {
setFetchingFirstPage(false); setIsFetchingFirstPage(false);
} }
}; };
fetchInitialResponses();
}, [surveyId, filters, responsesPerPage]);
useEffect(() => { // Only fetch if filters are applied (not on initial mount with no filters)
setPage(1); const hasFilters =
setHasMore(true); (selectedFilter && Object.keys(selectedFilter).length > 0) ||
}, [filters]); (dateRange && (dateRange.from || dateRange.to));
if (hasFilters) {
fetchFilteredResponses();
}
}, [filters, responsesPerPage, selectedFilter, dateRange, surveyId]);
return ( return (
<> <>
@@ -3,7 +3,7 @@ import { ResponsePage } from "@/app/(app)/environments/[environmentId]/surveys/[
import { SurveyAnalysisCTA } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA"; import { SurveyAnalysisCTA } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA";
import { IS_FORMBRICKS_CLOUD, IS_STORAGE_CONFIGURED, RESPONSES_PER_PAGE } from "@/lib/constants"; import { IS_FORMBRICKS_CLOUD, IS_STORAGE_CONFIGURED, RESPONSES_PER_PAGE } from "@/lib/constants";
import { getPublicDomain } from "@/lib/getPublicUrl"; import { getPublicDomain } from "@/lib/getPublicUrl";
import { getResponseCountBySurveyId } from "@/lib/response/service"; import { getResponseCountBySurveyId, getResponses } from "@/lib/response/service";
import { getSurvey } from "@/lib/survey/service"; import { getSurvey } from "@/lib/survey/service";
import { getTagsByEnvironmentId } from "@/lib/tag/service"; import { getTagsByEnvironmentId } from "@/lib/tag/service";
import { getUser } from "@/lib/user/service"; import { getUser } from "@/lib/user/service";
@@ -56,6 +56,9 @@ const Page = async (props) => {
const isQuotasAllowed = await getIsQuotasEnabled(organizationBilling.plan); const isQuotasAllowed = await getIsQuotasEnabled(organizationBilling.plan);
const quotas = isQuotasAllowed ? await getQuotas(survey.id) : []; const quotas = isQuotasAllowed ? await getQuotas(survey.id) : [];
// Fetch initial responses on the server to prevent duplicate client-side fetch
const initialResponses = await getResponses(params.surveyId, RESPONSES_PER_PAGE, 0);
return ( return (
<PageContentWrapper> <PageContentWrapper>
<PageHeader <PageHeader
@@ -87,6 +90,7 @@ const Page = async (props) => {
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
isQuotasAllowed={isQuotasAllowed} isQuotasAllowed={isQuotasAllowed}
quotas={quotas} quotas={quotas}
initialResponses={initialResponses}
/> />
</PageContentWrapper> </PageContentWrapper>
); );