From 00a61f7abea7b7cb77e09e55276f4bc02c6b4dac Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:20:48 +0530 Subject: [PATCH] chore: response page optimization (#6843) Co-authored-by: igor-srdoc --- .../responses/components/ResponsePage.tsx | 39 ++++++++++++------- .../[surveyId]/(analysis)/responses/page.tsx | 6 ++- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx index 43dc716858..3529d4dad3 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx @@ -26,6 +26,7 @@ interface ResponsePageProps { isReadOnly: boolean; isQuotasAllowed: boolean; quotas: TSurveyQuota[]; + initialResponses?: TResponseWithQuotas[]; } export const ResponsePage = ({ @@ -39,11 +40,12 @@ export const ResponsePage = ({ isReadOnly, isQuotasAllowed, quotas, + initialResponses = [], }: ResponsePageProps) => { - const [responses, setResponses] = useState([]); - const [page, setPage] = useState(1); - const [hasMore, setHasMore] = useState(true); - const [isFetchingFirstPage, setFetchingFirstPage] = useState(true); + const [responses, setResponses] = useState(initialResponses); + const [page, setPage] = useState(null); + const [hasMore, setHasMore] = useState(initialResponses.length >= responsesPerPage); + const [isFetchingFirstPage, setIsFetchingFirstPage] = useState(false); const { selectedFilter, dateRange, resetState } = useResponseFilter(); const filters = useMemo( @@ -56,6 +58,7 @@ export const ResponsePage = ({ const searchParams = useSearchParams(); const fetchNextPage = useCallback(async () => { + if (page === null) return; const newPage = page + 1; let newResponses: TResponseWithQuotas[] = []; @@ -94,9 +97,14 @@ export const ResponsePage = ({ }, [searchParams, resetState]); useEffect(() => { - const fetchInitialResponses = async () => { + const fetchFilteredResponses = async () => { try { - setFetchingFirstPage(true); + // skip call for initial mount + if (page === null) { + setPage(1); + return; + } + setIsFetchingFirstPage(true); let responses: TResponseWithQuotas[] = []; const getResponsesActionResponse = await getResponsesAction({ @@ -110,19 +118,24 @@ export const ResponsePage = ({ if (responses.length < responsesPerPage) { setHasMore(false); + } else { + setHasMore(true); } setResponses(responses); } finally { - setFetchingFirstPage(false); + setIsFetchingFirstPage(false); } }; - fetchInitialResponses(); - }, [surveyId, filters, responsesPerPage]); - useEffect(() => { - setPage(1); - setHasMore(true); - }, [filters]); + // Only fetch if filters are applied (not on initial mount with no filters) + const hasFilters = + (selectedFilter && Object.keys(selectedFilter).length > 0) || + (dateRange && (dateRange.from || dateRange.to)); + + if (hasFilters) { + fetchFilteredResponses(); + } + }, [filters, responsesPerPage, selectedFilter, dateRange, surveyId]); return ( <> diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/page.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/page.tsx index 4413f48024..ae0b576f69 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/page.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/page.tsx @@ -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 { IS_FORMBRICKS_CLOUD, IS_STORAGE_CONFIGURED, RESPONSES_PER_PAGE } from "@/lib/constants"; 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 { getTagsByEnvironmentId } from "@/lib/tag/service"; import { getUser } from "@/lib/user/service"; @@ -56,6 +56,9 @@ const Page = async (props) => { const isQuotasAllowed = await getIsQuotasEnabled(organizationBilling.plan); 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 ( { isReadOnly={isReadOnly} isQuotasAllowed={isQuotasAllowed} quotas={quotas} + initialResponses={initialResponses} /> );