diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/SurveyAnalysisNavigation.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/SurveyAnalysisNavigation.tsx
index 91cc527133..f03c478464 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/SurveyAnalysisNavigation.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/SurveyAnalysisNavigation.tsx
@@ -11,7 +11,7 @@ import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigatio
import { InboxIcon, PresentationIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useParams, usePathname, useSearchParams } from "next/navigation";
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useIntervalWhenFocused } from "@formbricks/lib/utils/hooks/useIntervalWhenFocused";
import { TSurvey } from "@formbricks/types/surveys/types";
@@ -44,7 +44,7 @@ export const SurveyAnalysisNavigation = ({
const filters = useMemo(
() => getFormattedFilters(survey, selectedFilter, dateRange),
- [selectedFilter, dateRange]
+ [selectedFilter, dateRange, survey]
);
const latestFiltersRef = useRef(filters);
@@ -61,24 +61,24 @@ export const SurveyAnalysisNavigation = ({
setTotalResponseCount(responseCount);
};
- const getFilteredResponseCount = () => {
+ const getFilteredResponseCount = useCallback(() => {
if (isSharingPage)
return getResponseCountBySurveySharingKeyAction({
sharingKey,
filterCriteria: latestFiltersRef.current,
});
return getResponseCountAction({ surveyId: survey.id, filterCriteria: latestFiltersRef.current });
- };
+ }, [isSharingPage, sharingKey, survey.id]);
- const fetchFilteredResponseCount = async () => {
+ const fetchFilteredResponseCount = useCallback(async () => {
const count = await getFilteredResponseCount();
const responseCount = count?.data ?? 0;
setFilteredResponseCount(responseCount);
- };
+ }, [getFilteredResponseCount]);
useEffect(() => {
fetchFilteredResponseCount();
- }, [filters, isSharingPage, sharingKey, survey.id]);
+ }, [filters, isSharingPage, sharingKey, survey.id, fetchFilteredResponseCount]);
useIntervalWhenFocused(
() => {
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 57214a1e3e..2f000b3671 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
@@ -134,7 +134,7 @@ export const ResponsePage = ({
setResponseCount(responseCount);
};
handleResponsesCount();
- }, [JSON.stringify(filters), isSharingPage, sharingKey, surveyId]);
+ }, [filters, isSharingPage, sharingKey, surveyId]);
useEffect(() => {
const fetchInitialResponses = async () => {
@@ -171,13 +171,13 @@ export const ResponsePage = ({
}
};
fetchInitialResponses();
- }, [surveyId, JSON.stringify(filters), responsesPerPage, sharingKey, isSharingPage]);
+ }, [surveyId, filters, responsesPerPage, sharingKey, isSharingPage]);
useEffect(() => {
setPage(1);
setHasMore(true);
setResponses([]);
- }, [JSON.stringify(filters)]);
+ }, [filters]);
return (
<>
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTable.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTable.tsx
index 5f873aa209..b904ef8354 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTable.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTable.tsx
@@ -77,26 +77,6 @@ export const ResponseTable = ({
// Generate columns
const columns = generateResponseTableColumns(survey, isExpanded ?? false, isReadOnly, t);
- // Load saved settings from localStorage
- useEffect(() => {
- const savedColumnOrder = localStorage.getItem(`${survey.id}-columnOrder`);
- const savedColumnVisibility = localStorage.getItem(`${survey.id}-columnVisibility`);
- const savedExpandedSettings = localStorage.getItem(`${survey.id}-rowExpand`);
-
- if (savedColumnOrder && JSON.parse(savedColumnOrder).length > 0) {
- setColumnOrder(JSON.parse(savedColumnOrder));
- } else {
- setColumnOrder(table.getAllLeafColumns().map((d) => d.id));
- }
-
- if (savedColumnVisibility) {
- setColumnVisibility(JSON.parse(savedColumnVisibility));
- }
- if (savedExpandedSettings !== null) {
- setIsExpanded(JSON.parse(savedExpandedSettings));
- }
- }, [survey.id]);
-
// Save settings to localStorage when they change
useEffect(() => {
if (columnOrder.length > 0) {
@@ -120,7 +100,7 @@ export const ResponseTable = ({
// Memoize table data and columns
const tableData: TResponseTableData[] = useMemo(
() => (isFetchingFirstPage ? Array(10).fill({}) : data),
- [data]
+ [data, isFetchingFirstPage]
);
const tableColumns = useMemo(
() =>
@@ -134,7 +114,7 @@ export const ResponseTable = ({
),
}))
: columns,
- [columns, data]
+ [columns, isFetchingFirstPage]
);
// React Table instance
@@ -160,6 +140,28 @@ export const ResponseTable = ({
},
});
+ const defaultColumnOrder = useMemo(() => table.getAllLeafColumns().map((d) => d.id), [table]);
+
+ // Modified useEffect
+ useEffect(() => {
+ const savedColumnOrder = localStorage.getItem(`${survey.id}-columnOrder`);
+ const savedColumnVisibility = localStorage.getItem(`${survey.id}-columnVisibility`);
+ const savedExpandedSettings = localStorage.getItem(`${survey.id}-rowExpand`);
+
+ if (savedColumnOrder && JSON.parse(savedColumnOrder).length > 0) {
+ setColumnOrder(JSON.parse(savedColumnOrder));
+ } else {
+ setColumnOrder(defaultColumnOrder);
+ }
+
+ if (savedColumnVisibility) {
+ setColumnVisibility(JSON.parse(savedColumnVisibility));
+ }
+ if (savedExpandedSettings !== null) {
+ setIsExpanded(JSON.parse(savedExpandedSettings));
+ }
+ }, [survey.id, defaultColumnOrder]);
+
// Handle column drag end
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
@@ -244,9 +246,7 @@ export const ResponseTable = ({
{data && hasMore && data.length > 0 && (
-
+
)}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx
index ef71aa5fdf..b9af311ec1 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx
@@ -15,7 +15,7 @@ import {
import { useTranslations } from "next-intl";
import Link from "next/link";
import { useRouter } from "next/navigation";
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import { TSurvey } from "@formbricks/types/surveys/types";
import { TUser } from "@formbricks/types/user";
import { EmbedView } from "./shareEmbedModal/EmbedView";
@@ -43,16 +43,20 @@ export const ShareEmbedSurvey = ({
const isSingleUseLinkSurvey = survey.singleUse?.enabled ?? false;
const { email } = user;
const t = useTranslations();
- const tabs = [
- { id: "email", label: t("environments.surveys.summary.embed_in_an_email"), icon: MailIcon },
- { id: "webpage", label: t("environments.surveys.summary.embed_on_website"), icon: Code2Icon },
- {
- id: "link",
- label: `${isSingleUseLinkSurvey ? t("environments.surveys.summary.single_use_links") : t("environments.surveys.summary.share_the_link")}`,
- icon: LinkIcon,
- },
- { id: "app", label: t("environments.surveys.summary.embed_in_app"), icon: SmartphoneIcon },
- ].filter((tab) => !(survey.type === "link" && tab.id === "app"));
+ const tabs = useMemo(
+ () =>
+ [
+ { id: "email", label: t("environments.surveys.summary.embed_in_an_email"), icon: MailIcon },
+ { id: "webpage", label: t("environments.surveys.summary.embed_on_website"), icon: Code2Icon },
+ {
+ id: "link",
+ label: `${isSingleUseLinkSurvey ? t("environments.surveys.summary.single_use_links") : t("environments.surveys.summary.share_the_link")}`,
+ icon: LinkIcon,
+ },
+ { id: "app", label: t("environments.surveys.summary.embed_in_app"), icon: SmartphoneIcon },
+ ].filter((tab) => !(survey.type === "link" && tab.id === "app")),
+ [t, isSingleUseLinkSurvey, survey.type]
+ );
const [activeId, setActiveId] = useState(survey.type === "link" ? tabs[0].id : tabs[3].id);
const [showView, setShowView] = useState<"start" | "embed" | "panel">("start");
@@ -62,7 +66,7 @@ export const ShareEmbedSurvey = ({
if (survey.type !== "link") {
setActiveId(tabs[3].id);
}
- }, [survey.type]);
+ }, [survey.type, tabs]);
useEffect(() => {
if (open) {
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SuccessMessage.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SuccessMessage.tsx
index be5b4f4a7e..9a91282408 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SuccessMessage.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SuccessMessage.tsx
@@ -47,7 +47,7 @@ export const SuccessMessage = ({ environment, survey }: SummaryMetadataProps) =>
window.history.replaceState({}, "", url.toString());
}
- }, [environment, isAppSurvey, searchParams, survey, widgetSetupCompleted]);
+ }, [environment, isAppSurvey, searchParams, survey, widgetSetupCompleted, t]);
return <>{confetti &&
}>;
};
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx
index 8d85cf73cb..f49c682ef1 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx
@@ -15,7 +15,7 @@ import {
getSummaryBySurveySharingKeyAction,
} from "@/app/share/[sharingKey]/actions";
import { useParams, useSearchParams } from "next/navigation";
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useIntervalWhenFocused } from "@formbricks/lib/utils/hooks/useIntervalWhenFocused";
import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall";
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
@@ -82,14 +82,14 @@ export const SummaryPage = ({
const filters = useMemo(
() => getFormattedFilters(survey, selectedFilter, dateRange),
- [selectedFilter, dateRange]
+ [selectedFilter, dateRange, survey]
);
// Use a ref to keep the latest state and props
const latestFiltersRef = useRef(filters);
latestFiltersRef.current = filters;
- const getResponseCount = () => {
+ const getResponseCount = useCallback(() => {
if (isSharingPage)
return getResponseCountBySurveySharingKeyAction({
sharingKey,
@@ -99,9 +99,9 @@ export const SummaryPage = ({
surveyId,
filterCriteria: latestFiltersRef.current,
});
- };
+ }, [isSharingPage, sharingKey, surveyId]);
- const getSummary = () => {
+ const getSummary = useCallback(() => {
if (isSharingPage)
return getSummaryBySurveySharingKeyAction({
sharingKey,
@@ -112,37 +112,39 @@ export const SummaryPage = ({
surveyId,
filterCriteria: latestFiltersRef.current,
});
- };
+ }, [isSharingPage, sharingKey, surveyId]);
- const handleInitialData = async (isInitialLoad = false) => {
- if (isInitialLoad) {
- setIsLoading(true);
- }
-
- try {
- const [updatedResponseCountData, updatedSurveySummary] = await Promise.all([
- getResponseCount(),
- getSummary(),
- ]);
-
- const responseCount = updatedResponseCountData?.data ?? 0;
- const surveySummary = updatedSurveySummary?.data ?? initialSurveySummary;
-
- // Update the state with new data
- setResponseCount(responseCount);
- setSurveySummary(surveySummary);
- } catch (error) {
- console.error(error);
- } finally {
+ const handleInitialData = useCallback(
+ async (isInitialLoad = false) => {
if (isInitialLoad) {
- setIsLoading(false);
+ setIsLoading(true);
}
- }
- };
+
+ try {
+ const [updatedResponseCountData, updatedSurveySummary] = await Promise.all([
+ getResponseCount(),
+ getSummary(),
+ ]);
+
+ const responseCount = updatedResponseCountData?.data ?? 0;
+ const surveySummary = updatedSurveySummary?.data ?? initialSurveySummary;
+
+ setResponseCount(responseCount);
+ setSurveySummary(surveySummary);
+ } catch (error) {
+ console.error(error);
+ } finally {
+ if (isInitialLoad) {
+ setIsLoading(false);
+ }
+ }
+ },
+ [getResponseCount, getSummary]
+ );
useEffect(() => {
handleInitialData(true);
- }, [JSON.stringify(filters), isSharingPage, sharingKey, surveyId]);
+ }, [filters, isSharingPage, sharingKey, surveyId, handleInitialData]);
useIntervalWhenFocused(
() => {
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/QuestionFilterComboBox.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/QuestionFilterComboBox.tsx
index 76c11f3218..08431e5e55 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/QuestionFilterComboBox.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/QuestionFilterComboBox.tsx
@@ -133,8 +133,9 @@ export const QuestionFilterComboBox = ({
) : (
{typeof filterComboBoxValue !== "string" &&
- filterComboBoxValue?.map((o) => (
+ filterComboBoxValue?.map((o, index) => (