;
};
export const FormStylingSettings = ({
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HowToSendCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HowToSendCard.tsx
index 5be04bcb5d..ced2b8d9a2 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HowToSendCard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HowToSendCard.tsx
@@ -175,7 +175,7 @@ export const HowToSendCard = ({ localSurvey, setLocalSurvey, environment, locale
{t("common.connect_formbricks")}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionCard.tsx
index ebf1d77ab6..e3bb2fec25 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionCard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionCard.tsx
@@ -17,7 +17,7 @@ import { cn } from "@formbricks/lib/cn";
import { QUESTIONS_ICON_MAP, getTSurveyQuestionTypeEnumName } from "@formbricks/lib/utils/questions";
import { recallToHeadline } from "@formbricks/lib/utils/recall";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import {
TI18nString,
TSurvey,
@@ -43,7 +43,7 @@ import { RatingQuestionForm } from "./RatingQuestionForm";
interface QuestionCardProps {
localSurvey: TSurvey;
- product: TProduct;
+ project: TProject;
question: TSurveyQuestion;
questionIdx: number;
moveQuestion: (questionIndex: number, up: boolean) => void;
@@ -65,7 +65,7 @@ interface QuestionCardProps {
export const QuestionCard = ({
localSurvey,
- product,
+ project,
question,
questionIdx,
moveQuestion,
@@ -228,7 +228,7 @@ export const QuestionCard = ({
deleteCard={deleteQuestion}
moveCard={moveQuestion}
card={question}
- product={product}
+ project={project}
updateCard={updateQuestion}
addCard={addQuestion}
cardType="question"
@@ -358,7 +358,7 @@ export const QuestionCard = ({
) : question.type === TSurveyQuestionTypeEnum.FileUpload ? (
void;
updateQuestion: (questionIdx: number, updatedAttributes: any) => void;
deleteQuestion: (questionIdx: number) => void;
@@ -33,7 +33,7 @@ export const QuestionsDroppable = ({
invalidQuestions,
localSurvey,
moveQuestion,
- product,
+ project,
selectedLanguageCode,
setActiveQuestionId,
setSelectedLanguageCode,
@@ -54,7 +54,7 @@ export const QuestionsDroppable = ({
>;
activeQuestionId: TSurveyQuestionId | null;
setActiveQuestionId: (questionId: TSurveyQuestionId | null) => void;
- product: TProduct;
+ project: TProject;
invalidQuestions: string[] | null;
setInvalidQuestions: React.Dispatch>;
selectedLanguageCode: string;
@@ -71,7 +71,7 @@ export const QuestionsView = ({
setActiveQuestionId,
localSurvey,
setLocalSurvey,
- product,
+ project,
invalidQuestions,
setInvalidQuestions,
setSelectedLanguageCode,
@@ -447,7 +447,7 @@ export const QuestionsView = ({
collisionDetection={closestCorners}>
-
+
{t("environments.surveys.edit.waiting_period")}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SettingsView.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SettingsView.tsx
index ac06994bd5..62e54ddce3 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SettingsView.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SettingsView.tsx
@@ -1,5 +1,5 @@
import { AdvancedTargetingCard } from "@/modules/ee/advanced-targeting/components/advanced-targeting-card";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { TActionClass } from "@formbricks/types/action-classes";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TEnvironment } from "@formbricks/types/environment";
@@ -25,7 +25,7 @@ interface SettingsViewProps {
isUserTargetingAllowed?: boolean;
isFormbricksCloud: boolean;
locale: string;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}
export const SettingsView = ({
@@ -40,7 +40,7 @@ export const SettingsView = ({
isUserTargetingAllowed = false,
isFormbricksCloud,
locale,
- productPermission,
+ projectPermission,
}: SettingsViewProps) => {
const isAppSurvey = localSurvey.type === "app";
@@ -86,7 +86,7 @@ export const SettingsView = ({
environmentId={environment.id}
propActionClasses={actionClasses}
membershipRole={membershipRole}
- productPermission={productPermission}
+ projectPermission={projectPermission}
/>
>;
colors: string[];
@@ -39,7 +39,7 @@ interface StylingViewProps {
export const StylingView = ({
colors,
environment,
- product,
+ project,
localSurvey,
setLocalSurvey,
setStyling,
@@ -52,7 +52,7 @@ export const StylingView = ({
const t = useTranslations();
const form = useForm({
- defaultValues: localSurvey.styling ?? product.styling,
+ defaultValues: localSurvey.styling ?? project.styling,
});
const overwriteThemeStyling = form.watch("overwriteThemeStyling");
@@ -64,8 +64,8 @@ export const StylingView = ({
const [confirmResetStylingModalOpen, setConfirmResetStylingModalOpen] = useState(false);
const onResetThemeStyling = () => {
- const { styling: productStyling } = product;
- const { allowStyleOverwrite, ...baseStyling } = productStyling ?? {};
+ const { styling: projectStyling } = project;
+ const { allowStyleOverwrite, ...baseStyling } = projectStyling ?? {};
setStyling({
...baseStyling,
@@ -101,12 +101,12 @@ export const StylingView = ({
});
}, [setLocalSurvey]);
- const defaultProductStyling = useMemo(() => {
- const { styling: productStyling } = product;
- const { allowStyleOverwrite, ...baseStyling } = productStyling ?? {};
+ const defaultProjectStyling = useMemo(() => {
+ const { styling: projectStyling } = project;
+ const { allowStyleOverwrite, ...baseStyling } = projectStyling ?? {};
return baseStyling;
- }, [product]);
+ }, [project]);
const handleOverwriteToggle = (value: boolean) => {
// survey styling from the server is surveyStyling, it could either be set or not
@@ -114,12 +114,12 @@ export const StylingView = ({
setOverwriteThemeStyling(value);
- // if the toggle is turned on, we set the local styling to the product styling
+ // if the toggle is turned on, we set the local styling to the project styling
if (value) {
if (!styling) {
- // copy the product styling to the survey styling
+ // copy the project styling to the survey styling
setStyling({
- ...defaultProductStyling,
+ ...defaultProjectStyling,
overwriteThemeStyling: true,
});
return;
@@ -129,23 +129,23 @@ export const StylingView = ({
if (localStylingChanges) {
setStyling(localStylingChanges);
}
- // if there are no local styling changes, we set the styling to the product styling
+ // if there are no local styling changes, we set the styling to the project styling
else {
setStyling({
- ...defaultProductStyling,
+ ...defaultProjectStyling,
overwriteThemeStyling: true,
});
}
}
- // if the toggle is turned off, we store the local styling changes and set the styling to the product styling
+ // if the toggle is turned off, we store the local styling changes and set the styling to the project styling
else {
// copy the styling to localStylingChanges
setLocalStylingChanges(styling);
- // copy the product styling to the survey styling
+ // copy the project styling to the survey styling
setStyling({
- ...defaultProductStyling,
+ ...defaultProjectStyling,
overwriteThemeStyling: false,
});
}
@@ -184,7 +184,7 @@ export const StylingView = ({
open={formStylingOpen}
setOpen={setFormStylingOpen}
disabled={!overwriteThemeStyling}
- form={form as UseFormReturn}
+ form={form as UseFormReturn}
/>
}
+ project={project}
+ form={form as UseFormReturn}
/>
{localSurvey.type === "link" && (
@@ -204,7 +204,7 @@ export const StylingView = ({
colors={colors}
disabled={!overwriteThemeStyling}
isUnsplashConfigured={isUnsplashConfigured}
- form={form as UseFormReturn}
+ form={form as UseFormReturn}
/>
)}
@@ -225,7 +225,7 @@ export const StylingView = ({
{t("environments.surveys.edit.adjust_the_theme_in_the")}{" "}
{t("common.look_and_feel")}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyEditor.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyEditor.tsx
index 1b08429960..1ea1ecf83c 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyEditor.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyEditor.tsx
@@ -1,7 +1,7 @@
"use client";
import { FollowUpsView } from "@/modules/ee/survey-follow-ups/components/follow-ups-view";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { PreviewSurvey } from "@/modules/ui/components/preview-survey";
import { useCallback, useEffect, useRef, useState } from "react";
import { extractLanguageCodes, getEnabledLanguages } from "@formbricks/lib/i18n/utils";
@@ -12,11 +12,11 @@ import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TEnvironment } from "@formbricks/types/environment";
import { TOrganizationRole } from "@formbricks/types/memberships";
import { TOrganizationBillingPlan } from "@formbricks/types/organizations";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import { TSegment } from "@formbricks/types/segment";
import { TSurvey, TSurveyEditorTabs, TSurveyStyling } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
-import { refetchProductAction } from "../actions";
+import { refetchProjectAction } from "../actions";
import { LoadingSkeleton } from "./LoadingSkeleton";
import { QuestionsAudienceTabs } from "./QuestionsStylingSettingsTabs";
import { QuestionsView } from "./QuestionsView";
@@ -26,7 +26,7 @@ import { SurveyMenuBar } from "./SurveyMenuBar";
interface SurveyEditorProps {
survey: TSurvey;
- product: TProduct;
+ project: TProject;
environment: TEnvironment;
actionClasses: TActionClass[];
attributeClasses: TAttributeClass[];
@@ -41,15 +41,15 @@ interface SurveyEditorProps {
plan: TOrganizationBillingPlan;
isCxMode: boolean;
locale: TUserLocale;
+ projectPermission: TTeamPermission | null;
mailFrom: string;
isSurveyFollowUpsAllowed: boolean;
- productPermission: TTeamPermission | null;
userEmail: string;
}
export const SurveyEditor = ({
survey,
- product,
+ project,
environment,
actionClasses,
attributeClasses,
@@ -64,9 +64,9 @@ export const SurveyEditor = ({
plan,
isCxMode = false,
locale,
+ projectPermission,
mailFrom,
isSurveyFollowUpsAllowed = false,
- productPermission,
userEmail,
}: SurveyEditorProps) => {
const [activeView, setActiveView] = useState("questions");
@@ -75,19 +75,19 @@ export const SurveyEditor = ({
const [invalidQuestions, setInvalidQuestions] = useState(null);
const [selectedLanguageCode, setSelectedLanguageCode] = useState("default");
const surveyEditorRef = useRef(null);
- const [localProduct, setLocalProduct] = useState(product);
+ const [localProject, setLocalProject] = useState(project);
const [styling, setStyling] = useState(localSurvey?.styling);
const [localStylingChanges, setLocalStylingChanges] = useState(null);
- const fetchLatestProduct = useCallback(async () => {
- const refetchProductResponse = await refetchProductAction({ productId: localProduct.id });
- if (refetchProductResponse?.data) {
- setLocalProduct(refetchProductResponse.data);
+ const fetchLatestProject = useCallback(async () => {
+ const refetchProjectResponse = await refetchProjectAction({ projectId: localProject.id });
+ if (refetchProjectResponse?.data) {
+ setLocalProject(refetchProjectResponse.data);
}
- }, [localProduct.id]);
+ }, [localProject.id]);
- useDocumentVisibility(fetchLatestProduct);
+ useDocumentVisibility(fetchLatestProject);
useEffect(() => {
if (survey) {
@@ -107,20 +107,20 @@ export const SurveyEditor = ({
useEffect(() => {
const listener = () => {
if (document.visibilityState === "visible") {
- const fetchLatestProduct = async () => {
- const refetchProductResponse = await refetchProductAction({ productId: localProduct.id });
- if (refetchProductResponse?.data) {
- setLocalProduct(refetchProductResponse.data);
+ const fetchLatestProject = async () => {
+ const refetchProjectResponse = await refetchProjectAction({ projectId: localProject.id });
+ if (refetchProjectResponse?.data) {
+ setLocalProject(refetchProjectResponse.data);
}
};
- fetchLatestProduct();
+ fetchLatestProject();
}
};
document.addEventListener("visibilitychange", listener);
return () => {
document.removeEventListener("visibilitychange", listener);
};
- }, [localProduct.id]);
+ }, [localProject.id]);
// when the survey type changes, we need to reset the active question id to the first question
useEffect(() => {
@@ -152,7 +152,7 @@ export const SurveyEditor = ({
activeId={activeView}
setActiveId={setActiveView}
setInvalidQuestions={setInvalidQuestions}
- product={localProduct}
+ project={localProject}
responseCount={responseCount}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
@@ -167,7 +167,7 @@ export const SurveyEditor = ({
activeId={activeView}
setActiveId={setActiveView}
isCxMode={isCxMode}
- isStylingTabVisible={!!product.styling.allowStyleOverwrite}
+ isStylingTabVisible={!!project.styling.allowStyleOverwrite}
isSurveyFollowUpsAllowed={isSurveyFollowUpsAllowed}
/>
@@ -177,7 +177,7 @@ export const SurveyEditor = ({
setLocalSurvey={setLocalSurvey}
activeQuestionId={activeQuestionId}
setActiveQuestionId={setActiveQuestionId}
- product={localProduct}
+ project={localProject}
invalidQuestions={invalidQuestions}
setInvalidQuestions={setInvalidQuestions}
selectedLanguageCode={selectedLanguageCode ? selectedLanguageCode : "default"}
@@ -191,13 +191,13 @@ export const SurveyEditor = ({
/>
)}
- {activeView === "styling" && product.styling.allowStyleOverwrite && (
+ {activeView === "styling" && project.styling.allowStyleOverwrite && (
)}
@@ -241,7 +241,7 @@ export const SurveyEditor = ({
>;
setInvalidQuestions: React.Dispatch>;
- product: TProduct;
+ project: TProject;
responseCount: number;
selectedLanguageCode: string;
setSelectedLanguageCode: (selectedLanguage: string) => void;
@@ -52,7 +52,7 @@ export const SurveyMenuBar = ({
activeId,
setActiveId,
setInvalidQuestions,
- product,
+ project,
responseCount,
selectedLanguageCode,
isCxMode,
@@ -326,7 +326,7 @@ export const SurveyMenuBar = ({
{t("common.back")}
)}
- {product.name} /
+ {project.name} /
{
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyPlacementCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyPlacementCard.tsx
index 9915bbf3ad..191489d598 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyPlacementCard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyPlacementCard.tsx
@@ -9,7 +9,7 @@ import { useTranslations } from "next-intl";
import Link from "next/link";
import { useState } from "react";
import { TPlacement } from "@formbricks/types/common";
-import { TSurvey, TSurveyProductOverwrites } from "@formbricks/types/surveys/types";
+import { TSurvey, TSurveyProjectOverwrites } from "@formbricks/types/surveys/types";
import { Placement } from "./Placement";
interface SurveyPlacementCardProps {
@@ -26,17 +26,17 @@ export const SurveyPlacementCard = ({
const t = useTranslations();
const [open, setOpen] = useState(false);
- const { productOverwrites } = localSurvey ?? {};
- const { placement, clickOutsideClose, darkOverlay } = productOverwrites ?? {};
+ const { projectOverwrites } = localSurvey ?? {};
+ const { placement, clickOutsideClose, darkOverlay } = projectOverwrites ?? {};
- const setProductOverwrites = (productOverwrites: TSurveyProductOverwrites) => {
- setLocalSurvey({ ...localSurvey, productOverwrites });
+ const setProjectOverwrites = (projectOverwrites: TSurveyProjectOverwrites) => {
+ setLocalSurvey({ ...localSurvey, projectOverwrites: projectOverwrites });
};
const togglePlacement = () => {
- if (setProductOverwrites) {
- setProductOverwrites({
- ...productOverwrites,
+ if (setProjectOverwrites) {
+ setProjectOverwrites({
+ ...projectOverwrites,
placement: !!placement ? null : "bottomRight",
clickOutsideClose: false,
darkOverlay: false,
@@ -45,9 +45,9 @@ export const SurveyPlacementCard = ({
};
const handlePlacementChange = (placement: TPlacement) => {
- if (setProductOverwrites) {
- setProductOverwrites({
- ...productOverwrites,
+ if (setProjectOverwrites) {
+ setProjectOverwrites({
+ ...projectOverwrites,
placement,
});
}
@@ -56,18 +56,18 @@ export const SurveyPlacementCard = ({
const handleOverlay = (overlayType: string) => {
const darkOverlay = overlayType === "dark";
- if (setProductOverwrites) {
- setProductOverwrites({
- ...productOverwrites,
+ if (setProjectOverwrites) {
+ setProjectOverwrites({
+ ...projectOverwrites,
darkOverlay,
});
}
};
const handleClickOutsideClose = (clickOutsideClose: boolean) => {
- if (setProductOverwrites) {
- setProductOverwrites({
- ...productOverwrites,
+ if (setProjectOverwrites) {
+ setProjectOverwrites({
+ ...projectOverwrites,
clickOutsideClose,
});
}
@@ -141,7 +141,7 @@ export const SurveyPlacementCard = ({
{t("environments.surveys.edit.to_keep_the_placement_over_all_surveys_consistent_you_can")}{" "}
-
+
{t("environments.surveys.edit.set_the_global_placement_in_the_look_feel_settings")}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/WhenToSendCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/WhenToSendCard.tsx
index cacbd3b705..e8bd6db0f5 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/WhenToSendCard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/WhenToSendCard.tsx
@@ -1,6 +1,6 @@
"use client";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
import { Button } from "@/modules/ui/components/button";
@@ -29,7 +29,7 @@ interface WhenToSendCardProps {
environmentId: string;
propActionClasses: TActionClass[];
membershipRole?: TOrganizationRole;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}
export const WhenToSendCard = ({
@@ -38,7 +38,7 @@ export const WhenToSendCard = ({
setLocalSurvey,
propActionClasses,
membershipRole,
- productPermission,
+ projectPermission,
}: WhenToSendCardProps) => {
const t = useTranslations();
const [open, setOpen] = useState(localSurvey.type === "app" ? true : false);
@@ -47,7 +47,7 @@ export const WhenToSendCard = ({
const [randomizerToggle, setRandomizerToggle] = useState(localSurvey.displayPercentage ? true : false);
const { isMember } = getAccessFlags(membershipRole);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/page.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/page.tsx
index 3ef8049e3c..ca334a4524 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/page.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/page.tsx
@@ -5,7 +5,7 @@ import {
getMultiLanguagePermission,
getSurveyFollowUpsPermission,
} from "@/modules/ee/license-check/lib/utils";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { ErrorComponent } from "@/modules/ui/components/error-component";
import { getServerSession } from "next-auth";
@@ -23,7 +23,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getResponseCountBySurveyId } from "@formbricks/lib/response/service";
import { getSegments } from "@formbricks/lib/segment/service";
import { getSurvey } from "@formbricks/lib/survey/service";
@@ -44,7 +44,7 @@ const Page = async (props) => {
const t = await getTranslations();
const [
survey,
- product,
+ project,
environment,
actionClasses,
attributeClasses,
@@ -54,7 +54,7 @@ const Page = async (props) => {
segments,
] = await Promise.all([
getSurvey(params.surveyId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
getEnvironment(params.environmentId),
getActionClasses(params.environmentId),
getAttributeClasses(params.environmentId, undefined, { skipArchived: true }),
@@ -72,16 +72,16 @@ const Page = async (props) => {
throw new Error(t("common.organization_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isSurveyCreationDeletionDisabled = isMember && hasReadAccess;
const locale = session.user.id ? await getUserLocale(session.user.id) : undefined;
@@ -97,7 +97,7 @@ const Page = async (props) => {
!environment ||
!actionClasses ||
!attributeClasses ||
- !product ||
+ !project ||
!userEmail ||
isSurveyCreationDeletionDisabled
) {
@@ -109,13 +109,13 @@ const Page = async (props) => {
return (
({
surveyClosedMessage: {
enabled: false,
},
- productOverwrites: null,
+ projectOverwrites: null,
singleUse: null,
styling: null,
resultShareKey: null,
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/actions.ts b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/actions.ts
index 4b69130ee3..4193e2b6a1 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/actions.ts
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/actions.ts
@@ -2,7 +2,7 @@
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromEnvironmentId, getProductIdFromEnvironmentId } from "@/lib/utils/helper";
+import { getOrganizationIdFromEnvironmentId, getProjectIdFromEnvironmentId } from "@/lib/utils/helper";
import { getIsAIEnabled } from "@/modules/ee/license-check/lib/utils";
import { createId } from "@paralleldrive/cuid2";
import { generateObject } from "ai";
@@ -32,8 +32,8 @@ export const createAISurveyAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
minPermission: "readWrite",
},
],
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/TemplateContainer.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/TemplateContainer.tsx
index 4f2c7ecdee..71f6b7ef1e 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/TemplateContainer.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/TemplateContainer.tsx
@@ -10,22 +10,22 @@ import { useTranslations } from "next-intl";
import { useState } from "react";
import { getCustomSurveyTemplate } from "@formbricks/lib/templates";
import type { TEnvironment } from "@formbricks/types/environment";
-import type { TProduct, TProductConfigChannel, TProductConfigIndustry } from "@formbricks/types/product";
+import type { TProject, TProjectConfigChannel, TProjectConfigIndustry } from "@formbricks/types/project";
import type { TTemplate, TTemplateRole } from "@formbricks/types/templates";
import { TUser } from "@formbricks/types/user";
import { getMinimalSurvey } from "../../lib/minimalSurvey";
type TemplateContainerWithPreviewProps = {
environmentId: string;
- product: TProduct;
+ project: TProject;
environment: TEnvironment;
user: TUser;
- prefilledFilters: (TProductConfigChannel | TProductConfigIndustry | TTemplateRole | null)[];
+ prefilledFilters: (TProjectConfigChannel | TProjectConfigIndustry | TTemplateRole | null)[];
isAIEnabled: boolean;
};
export const TemplateContainerWithPreview = ({
- product,
+ project,
environment,
user,
prefilledFilters,
@@ -67,7 +67,7 @@ export const TemplateContainerWithPreview = ({
{
@@ -82,7 +82,7 @@ export const TemplateContainerWithPreview = ({
file.name}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx
index 3d13753940..bd5f5952f8 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx
@@ -1,5 +1,5 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
@@ -7,9 +7,9 @@ import { redirect } from "next/navigation";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getUser } from "@formbricks/lib/user/service";
-import { TProductConfigChannel, TProductConfigIndustry } from "@formbricks/types/product";
+import { TProjectConfigChannel, TProjectConfigIndustry } from "@formbricks/types/project";
import { TTemplateRole } from "@formbricks/types/templates";
import { TemplateContainerWithPreview } from "./components/TemplateContainer";
@@ -18,8 +18,8 @@ interface SurveyTemplateProps {
environmentId: string;
}>;
searchParams: Promise<{
- channel?: TProductConfigChannel;
- industry?: TProductConfigIndustry;
+ channel?: TProjectConfigChannel;
+ industry?: TProjectConfigIndustry;
role?: TTemplateRole;
}>;
}
@@ -35,18 +35,18 @@ const Page = async (props: SurveyTemplateProps) => {
throw new Error(t("common.session_not_found"));
}
- const [user, environment, product] = await Promise.all([
+ const [user, environment, project] = await Promise.all([
getUser(session.user.id),
getEnvironment(environmentId),
- getProductByEnvironmentId(environmentId),
+ getProjectByEnvironmentId(environmentId),
]);
if (!user) {
throw new Error(t("common.user_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
if (!environment) {
@@ -54,26 +54,26 @@ const Page = async (props: SurveyTemplateProps) => {
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
- product.organizationId
+ project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
if (isReadOnly) {
return redirect(`/environments/${environment.id}/surveys`);
}
- const prefilledFilters = [product.config.channel, product.config.industry, searchParams.role ?? null];
+ const prefilledFilters = [project.config.channel, project.config.industry, searchParams.role ?? null];
return (
{
const params = await props.params;
let attributeClasses = await getAttributeClasses(params.environmentId);
const t = await getTranslations();
- const product = await getProductByEnvironmentId(params.environmentId);
+ const project = await getProjectByEnvironmentId(params.environmentId);
const locale = await findMatchingLocale();
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const [organization, session] = await Promise.all([
@@ -47,9 +47,9 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/layout.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/layout.tsx
index 82e7ca27ce..bfbf252938 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/layout.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/layout.tsx
@@ -6,7 +6,7 @@ import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { AuthorizationError } from "@formbricks/types/errors";
const ConfigLayout = async (props) => {
@@ -40,9 +40,9 @@ const ConfigLayout = async (props) => {
return redirect(`/environments/${params.environmentId}/settings/billing`);
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
return children;
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseSection.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseSection.tsx
index 70941ea598..0f3188376f 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseSection.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseSection.tsx
@@ -1,9 +1,9 @@
import { ResponseTimeline } from "@/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseTimeline";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getResponsesByPersonId } from "@formbricks/lib/response/service";
import { getSurveys } from "@formbricks/lib/survey/service";
import { getUser } from "@formbricks/lib/user/service";
@@ -33,26 +33,26 @@ export const ResponseSection = async ({
const t = await getTranslations();
if (!session) {
- throw new Error(t("common.no_session_found"));
+ throw new Error(t("common.session_not_found"));
}
const user = await getUser(session.user.id);
if (!user) {
- throw new Error(t("common.no_user_found"));
+ throw new Error(t("common.user_not_found"));
}
if (!responses) {
throw new Error(t("environments.people.no_responses_found"));
}
- const product = await getProductByEnvironmentId(environment.id);
+ const project = await getProjectByEnvironmentId(environment.id);
- if (!product) {
- throw new Error(t("common.no_product_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const locale = await findMatchingLocale();
@@ -65,7 +65,7 @@ export const ResponseSection = async ({
environmentTags={environmentTags}
attributeClasses={attributeClasses}
locale={locale}
- productPermission={productPermission}
+ projectPermission={projectPermission}
/>
);
};
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseTimeline.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseTimeline.tsx
index cead3f9653..c399690ec4 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseTimeline.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseTimeline.tsx
@@ -1,7 +1,7 @@
"use client";
import { ResponseFeed } from "@/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponsesFeed";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { ArrowDownUpIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
@@ -20,7 +20,7 @@ interface ResponseTimelineProps {
environmentTags: TTag[];
attributeClasses: TAttributeClass[];
locale: TUserLocale;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}
export const ResponseTimeline = ({
@@ -31,7 +31,7 @@ export const ResponseTimeline = ({
environmentTags,
attributeClasses,
locale,
- productPermission,
+ projectPermission,
}: ResponseTimelineProps) => {
const t = useTranslations();
const [sortedResponses, setSortedResponses] = useState(responses);
@@ -64,7 +64,7 @@ export const ResponseTimeline = ({
environmentTags={environmentTags}
attributeClasses={attributeClasses}
locale={locale}
- productPermission={productPermission}
+ projectPermission={projectPermission}
/>
);
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponsesFeed.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponsesFeed.tsx
index f43a031993..5da236fd22 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponsesFeed.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponsesFeed.tsx
@@ -1,7 +1,7 @@
"use client";
import { SingleResponseCard } from "@/modules/analysis/components/SingleResponseCard";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { EmptySpaceFiller } from "@/modules/ui/components/empty-space-filler";
import { useEffect, useState } from "react";
@@ -23,7 +23,7 @@ interface ResponseTimelineProps {
environmentTags: TTag[];
attributeClasses: TAttributeClass[];
locale: TUserLocale;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}
export const ResponseFeed = ({
@@ -34,7 +34,7 @@ export const ResponseFeed = ({
environmentTags,
attributeClasses,
locale,
- productPermission,
+ projectPermission,
}: ResponseTimelineProps) => {
const [fetchedResponses, setFetchedResponses] = useState(responses);
@@ -69,7 +69,7 @@ export const ResponseFeed = ({
updateResponse={updateResponse}
attributeClasses={attributeClasses}
locale={locale}
- productPermission={productPermission}
+ projectPermission={projectPermission}
/>
))
)}
@@ -87,7 +87,7 @@ const ResponseSurveyCard = ({
updateResponse,
attributeClasses,
locale,
- productPermission,
+ projectPermission,
}: {
response: TResponse;
surveys: TSurvey[];
@@ -98,7 +98,7 @@ const ResponseSurveyCard = ({
updateResponse: (responseId: string, response: TResponse) => void;
attributeClasses: TAttributeClass[];
locale: TUserLocale;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}) => {
const survey = surveys.find((survey) => {
return survey.id === response.surveyId;
@@ -107,7 +107,7 @@ const ResponseSurveyCard = ({
const { membershipRole } = useMembershipRole(survey?.environmentId || "", user.id);
const { isMember } = getAccessFlags(membershipRole);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/page.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/page.tsx
index a91ed1ffe4..495fd9eb2b 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/page.tsx
@@ -2,7 +2,7 @@ import { AttributesSection } from "@/app/(app)/environments/[environmentId]/(peo
import { DeletePersonButton } from "@/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/DeletePersonButton";
import { ResponseSection } from "@/app/(app)/environments/[environmentId]/(people)/people/[personId]/components/ResponseSection";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
@@ -16,17 +16,17 @@ import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getPerson } from "@formbricks/lib/person/service";
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
const Page = async (props) => {
const params = await props.params;
const t = await getTranslations();
- const [environment, environmentTags, product, session, organization, person, attributes, attributeClasses] =
+ const [environment, environmentTags, project, session, organization, person, attributes, attributeClasses] =
await Promise.all([
getEnvironment(params.environmentId),
getTagsByEnvironmentId(params.environmentId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
getServerSession(authOptions),
getOrganizationByEnvironmentId(params.environmentId),
getPerson(params.personId),
@@ -34,8 +34,8 @@ const Page = async (props) => {
getAttributeClasses(params.environmentId),
]);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
if (!environment) {
@@ -57,8 +57,8 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/actions.ts b/apps/web/app/(app)/environments/[environmentId]/(people)/people/actions.ts
index eeaa757419..257fd860b6 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/actions.ts
@@ -5,8 +5,8 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"
import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromPersonId,
- getProductIdFromEnvironmentId,
- getProductIdFromPersonId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromPersonId,
} from "@/lib/utils/helper";
import { z } from "zod";
import { deletePerson, getPeople } from "@formbricks/lib/person/service";
@@ -30,9 +30,9 @@ export const getPersonsAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
@@ -56,9 +56,9 @@ export const deletePersonAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromPersonId(parsedInput.personId),
+ projectId: await getProjectIdFromPersonId(parsedInput.personId),
},
],
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation.tsx
index d37e6164f6..24278d12d3 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation.tsx
@@ -1,7 +1,7 @@
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
import { getTranslations } from "next-intl/server";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
-import { TProduct } from "@formbricks/types/product";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
+import { TProject } from "@formbricks/types/project";
interface PersonSecondaryNavigationProps {
activeId: string;
@@ -14,13 +14,13 @@ export const PersonSecondaryNavigation = async ({
environmentId,
loading,
}: PersonSecondaryNavigationProps) => {
- let product: TProduct | null = null;
+ let project: TProject | null = null;
const t = await getTranslations();
if (!loading && environmentId) {
- product = await getProductByEnvironmentId(environmentId);
+ project = await getProjectByEnvironmentId(environmentId);
- if (!product) {
- throw new Error("Product not found");
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
}
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/page.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/people/page.tsx
index 8f4434832c..4b544208b2 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/page.tsx
@@ -1,7 +1,7 @@
import { PersonDataView } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonDataView";
import { PersonSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { Button } from "@/modules/ui/components/button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -14,7 +14,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
@@ -35,17 +35,17 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
throw new Error(t("common.organization_not_found"));
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, product.id);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, project.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts b/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts
index 031468c44d..441226d06a 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts
@@ -2,7 +2,7 @@
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromSegmentId, getProductIdFromSegmentId } from "@/lib/utils/helper";
+import { getOrganizationIdFromSegmentId, getProjectIdFromSegmentId } from "@/lib/utils/helper";
import { z } from "zod";
import { deleteSegment, updateSegment } from "@formbricks/lib/segment/service";
import { ZId } from "@formbricks/types/common";
@@ -24,9 +24,9 @@ export const deleteBasicSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSegmentId(parsedInput.segmentId),
+ projectId: await getProjectIdFromSegmentId(parsedInput.segmentId),
},
],
});
@@ -51,9 +51,9 @@ export const updateBasicSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSegmentId(parsedInput.segmentId),
+ projectId: await getProjectIdFromSegmentId(parsedInput.segmentId),
},
],
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/segments/page.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/segments/page.tsx
index 7a91a8faa6..db135292ff 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/segments/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/segments/page.tsx
@@ -4,7 +4,7 @@ import { SegmentTable } from "@/app/(app)/environments/[environmentId]/(people)/
import { authOptions } from "@/modules/auth/lib/authOptions";
import { CreateSegmentModal } from "@/modules/ee/advanced-targeting/components/create-segment-modal";
import { getAdvancedTargetingPermission } from "@/modules/ee/license-check/lib/utils";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
@@ -16,18 +16,18 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSegments } from "@formbricks/lib/segment/service";
const Page = async (props) => {
const params = await props.params;
const t = await getTranslations();
- const [environment, segments, attributeClasses, organization, product] = await Promise.all([
+ const [environment, segments, attributeClasses, organization, project] = await Promise.all([
getEnvironment(params.environmentId),
getSegments(params.environmentId),
getAttributeClasses(params.environmentId, undefined, { skipArchived: true }),
getOrganizationByEnvironmentId(params.environmentId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
]);
const session = await getServerSession(authOptions);
@@ -39,8 +39,8 @@ const Page = async (props) => {
throw new Error(t("common.environment_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
if (!organization) {
@@ -56,9 +56,9 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, product.id);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, project.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/actions.ts b/apps/web/app/(app)/environments/[environmentId]/actions.ts
index 2b13c3b654..847066d687 100644
--- a/apps/web/app/(app)/environments/[environmentId]/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/actions.ts
@@ -2,71 +2,26 @@
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getIsMultiOrgEnabled, getRoleManagementPermission } from "@/modules/ee/license-check/lib/utils";
+import {
+ getOrganizationProjectsLimit,
+ getRoleManagementPermission,
+} from "@/modules/ee/license-check/lib/utils";
+import { createProject } from "@/modules/projects/settings/lib/project";
import { z } from "zod";
-import { createMembership } from "@formbricks/lib/membership/service";
-import { createOrganization, getOrganization } from "@formbricks/lib/organization/service";
-import { createProduct } from "@formbricks/lib/product/service";
+import { getOrganization } from "@formbricks/lib/organization/service";
+import { getOrganizationProjectsCount } from "@formbricks/lib/project/service";
import { updateUser } from "@formbricks/lib/user/service";
import { ZId } from "@formbricks/types/common";
import { OperationNotAllowedError } from "@formbricks/types/errors";
-import { ZProductUpdateInput } from "@formbricks/types/product";
-import { TUserNotificationSettings } from "@formbricks/types/user";
+import { ZProjectUpdateInput } from "@formbricks/types/project";
-const ZCreateOrganizationAction = z.object({
- organizationName: z.string(),
-});
-
-export const createOrganizationAction = authenticatedActionClient
- .schema(ZCreateOrganizationAction)
- .action(async ({ ctx, parsedInput }) => {
- const isMultiOrgEnabled = await getIsMultiOrgEnabled();
- if (!isMultiOrgEnabled)
- throw new OperationNotAllowedError(
- "Creating Multiple organization is restricted on your instance of Formbricks"
- );
-
- const newOrganization = await createOrganization({
- name: parsedInput.organizationName,
- });
-
- await createMembership(newOrganization.id, ctx.user.id, {
- role: "owner",
- accepted: true,
- });
-
- const product = await createProduct(newOrganization.id, {
- name: "My Product",
- });
-
- const updatedNotificationSettings: TUserNotificationSettings = {
- ...ctx.user.notificationSettings,
- alert: {
- ...ctx.user.notificationSettings?.alert,
- },
- weeklySummary: {
- ...ctx.user.notificationSettings?.weeklySummary,
- [product.id]: true,
- },
- unsubscribedOrganizationIds: Array.from(
- new Set([...(ctx.user.notificationSettings?.unsubscribedOrganizationIds || []), newOrganization.id])
- ),
- };
-
- await updateUser(ctx.user.id, {
- notificationSettings: updatedNotificationSettings,
- });
-
- return newOrganization;
- });
-
-const ZCreateProductAction = z.object({
+const ZCreateProjectAction = z.object({
organizationId: ZId,
- data: ZProductUpdateInput,
+ data: ZProjectUpdateInput,
});
-export const createProductAction = authenticatedActionClient
- .schema(ZCreateProductAction)
+export const createProjectAction = authenticatedActionClient
+ .schema(ZCreateProjectAction)
.action(async ({ parsedInput, ctx }) => {
const { user } = ctx;
@@ -78,20 +33,27 @@ export const createProductAction = authenticatedActionClient
access: [
{
data: parsedInput.data,
- schema: ZProductUpdateInput,
+ schema: ZProjectUpdateInput,
type: "organization",
roles: ["owner", "manager"],
},
],
});
+ const organization = await getOrganization(organizationId);
+
+ if (!organization) {
+ throw new Error("Organization not found");
+ }
+
+ const organizationProjectsLimit = await getOrganizationProjectsLimit(organization);
+ const organizationProjectsCount = await getOrganizationProjectsCount(organization.id);
+
+ if (organizationProjectsCount >= organizationProjectsLimit) {
+ throw new OperationNotAllowedError("Organization project limit reached");
+ }
+
if (parsedInput.data.teamIds && parsedInput.data.teamIds.length > 0) {
- const organization = await getOrganization(organizationId);
-
- if (!organization) {
- throw new Error("Organization not found");
- }
-
const canDoRoleManagement = await getRoleManagementPermission(organization);
if (!canDoRoleManagement) {
@@ -99,7 +61,7 @@ export const createProductAction = authenticatedActionClient
}
}
- const product = await createProduct(parsedInput.organizationId, parsedInput.data);
+ const project = await createProject(parsedInput.organizationId, parsedInput.data);
const updatedNotificationSettings = {
...user.notificationSettings,
alert: {
@@ -107,7 +69,7 @@ export const createProductAction = authenticatedActionClient
},
weeklySummary: {
...user.notificationSettings?.weeklySummary,
- [product.id]: true,
+ [project.id]: true,
},
};
@@ -115,5 +77,5 @@ export const createProductAction = authenticatedActionClient
notificationSettings: updatedNotificationSettings,
});
- return product;
+ return project;
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts b/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts
index b1c186b15e..420343820b 100644
--- a/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts
@@ -2,7 +2,7 @@
import { actionClient, authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromActionClassId, getProductIdFromActionClassId } from "@/lib/utils/helper";
+import { getOrganizationIdFromActionClassId, getProjectIdFromActionClassId } from "@/lib/utils/helper";
import { z } from "zod";
import { deleteActionClass, getActionClass, updateActionClass } from "@formbricks/lib/actionClass/service";
import { cache } from "@formbricks/lib/cache";
@@ -27,9 +27,9 @@ export const deleteActionClassAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromActionClassId(parsedInput.actionClassId),
+ projectId: await getProjectIdFromActionClassId(parsedInput.actionClassId),
},
],
});
@@ -59,9 +59,9 @@ export const updateActionClassAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromActionClassId(parsedInput.actionClassId),
+ projectId: await getProjectIdFromActionClassId(parsedInput.actionClassId),
},
],
});
@@ -89,9 +89,9 @@ export const getActiveInactiveSurveysAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromActionClassId(parsedInput.actionClassId),
+ projectId: await getProjectIdFromActionClassId(parsedInput.actionClassId),
},
],
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/actions/page.tsx b/apps/web/app/(app)/environments/[environmentId]/actions/page.tsx
index 24ec7f7c96..0c5abceee9 100644
--- a/apps/web/app/(app)/environments/[environmentId]/actions/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/actions/page.tsx
@@ -3,7 +3,7 @@ import { ActionClassDataRow } from "@/app/(app)/environments/[environmentId]/act
import { ActionTableHeading } from "@/app/(app)/environments/[environmentId]/actions/components/ActionTableHeading";
import { AddActionModal } from "@/app/(app)/environments/[environmentId]/actions/components/AddActionModal";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
@@ -16,7 +16,7 @@ import { getEnvironments } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
export const metadata: Metadata = {
@@ -27,10 +27,10 @@ const Page = async (props) => {
const params = await props.params;
const session = await getServerSession(authOptions);
const t = await getTranslations();
- const [actionClasses, organization, product] = await Promise.all([
+ const [actionClasses, organization, project] = await Promise.all([
getActionClasses(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
]);
const locale = await findMatchingLocale();
@@ -42,11 +42,11 @@ const Page = async (props) => {
throw new Error(t("common.organization_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
- const environments = await getEnvironments(product.id);
+ const environments = await getEnvironments(project.id);
const currentEnvironment = environments.find((env) => env.id === params.environmentId);
if (!currentEnvironment) {
@@ -60,9 +60,9 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember, isBilling } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, product.id);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, project.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
if (isBilling) {
return redirect(`/environments/${params.environmentId}/settings/billing`);
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx b/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx
index b876e79a54..ab4a3bb4eb 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx
@@ -1,7 +1,7 @@
import { MainNavigation } from "@/app/(app)/environments/[environmentId]/components/MainNavigation";
import { TopControlBar } from "@/app/(app)/environments/[environmentId]/components/TopControlBar";
-import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/utils";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getEnterpriseLicense, getOrganizationProjectsLimit } from "@/modules/ee/license-check/lib/utils";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { DevEnvironmentBanner } from "@/modules/ui/components/dev-environment-banner";
import { LimitsReachedBanner } from "@/modules/ui/components/limits-reached-banner";
import { PendingDowngradeBanner } from "@/modules/ui/components/pending-downgrade-banner";
@@ -17,7 +17,7 @@ import {
getOrganizationByEnvironmentId,
getOrganizationsByUserId,
} from "@formbricks/lib/organization/service";
-import { getUserProducts } from "@formbricks/lib/product/service";
+import { getUserProjects } from "@formbricks/lib/project/service";
import { getUser } from "@formbricks/lib/user/service";
interface EnvironmentLayoutProps {
@@ -47,13 +47,13 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En
throw new Error(t("common.environment_not_found"));
}
- const [products, environments] = await Promise.all([
- getUserProducts(user.id, organization.id),
- getEnvironments(environment.productId),
+ const [projects, environments] = await Promise.all([
+ getUserProjects(user.id, organization.id),
+ getEnvironments(environment.projectId),
]);
- if (!products || !environments || !organizations) {
- throw new Error(t("environments.products_environments_organizations_not_found"));
+ if (!projects || !environments || !organizations) {
+ throw new Error(t("environments.projects_environments_organizations_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
@@ -62,10 +62,10 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En
const { features, lastChecked, isPendingDowngrade, active } = await getEnterpriseLicense();
- const productPermission = await getProductPermissionByUserId(session.user.id, environment.productId);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, environment.projectId);
- if (isMember && !productPermission) {
- throw new Error(t("common.product_permission_not_found"));
+ if (isMember && !projectPermission) {
+ throw new Error(t("common.project_permission_not_found"));
}
const isMultiOrgEnabled = features?.isMultiOrgEnabled ?? false;
@@ -80,6 +80,8 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En
]);
}
+ const organizationProjectsLimit = await getOrganizationProjectsLimit(organization);
+
return (
@@ -105,18 +107,20 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En
environment={environment}
organization={organization}
organizations={organizations}
- products={products}
+ projects={projects}
+ organizationProjectsLimit={organizationProjectsLimit}
user={user}
isFormbricksCloud={IS_FORMBRICKS_CLOUD}
membershipRole={membershipRole}
isMultiOrgEnabled={isMultiOrgEnabled}
+ isLicenseActive={active}
/>
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx b/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx
index cc38428384..90fdd8972a 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx
@@ -5,6 +5,7 @@ import { NavigationLink } from "@/app/(app)/environments/[environmentId]/compone
import { formbricksLogout } from "@/app/lib/formbricks";
import FBLogo from "@/images/formbricks-wordmark.svg";
import { CreateOrganizationModal } from "@/modules/organization/components/CreateOrganizationModal";
+import { ProjectSwitcher } from "@/modules/projects/components/project-switcher";
import { ProfileAvatar } from "@/modules/ui/components/avatars";
import { Button } from "@/modules/ui/components/button";
import {
@@ -22,15 +23,11 @@ import {
} from "@/modules/ui/components/dropdown-menu";
import {
ArrowUpRightIcon,
- BlendIcon,
BlocksIcon,
ChevronRightIcon,
Cog,
CreditCardIcon,
- GlobeIcon,
- GlobeLockIcon,
KeyIcon,
- LinkIcon,
LogOutIcon,
MessageCircle,
MousePointerClick,
@@ -54,7 +51,7 @@ import { capitalizeFirstLetter } from "@formbricks/lib/utils/strings";
import { TEnvironment } from "@formbricks/types/environment";
import { TOrganizationRole } from "@formbricks/types/memberships";
import { TOrganization } from "@formbricks/types/organizations";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import { TUser } from "@formbricks/types/user";
import packageJson from "../../../../../package.json";
@@ -63,10 +60,12 @@ interface NavigationProps {
organizations: TOrganization[];
user: TUser;
organization: TOrganization;
- products: TProduct[];
+ projects: TProject[];
isMultiOrgEnabled: boolean;
isFormbricksCloud?: boolean;
membershipRole?: TOrganizationRole;
+ organizationProjectsLimit: number;
+ isLicenseActive: boolean;
}
export const MainNavigation = ({
@@ -74,10 +73,12 @@ export const MainNavigation = ({
organizations,
organization,
user,
- products,
+ projects,
isMultiOrgEnabled,
isFormbricksCloud = true,
membershipRole,
+ organizationProjectsLimit,
+ isLicenseActive,
}: NavigationProps) => {
const router = useRouter();
const pathname = usePathname();
@@ -89,7 +90,7 @@ export const MainNavigation = ({
const [isTextVisible, setIsTextVisible] = useState(true);
const [latestVersion, setLatestVersion] = useState("");
- const product = products.find((product) => product.id === environment.productId);
+ const project = projects.find((project) => project.id === environment.projectId);
const { isManager, isOwner, isMember, isBilling } = getAccessFlags(membershipRole);
const isOwnerOrManager = isManager || isOwner;
@@ -124,39 +125,31 @@ export const MainNavigation = ({
return [...organizations].sort((a, b) => a.name.localeCompare(b.name));
}, [organizations]);
- const sortedProducts = useMemo(() => {
+ const sortedProjects = useMemo(() => {
const channelOrder: (string | null)[] = ["website", "app", "link", null];
- const groupedProducts = products.reduce(
- (acc, product) => {
- const channel = product.config.channel;
+ const groupedProjects = projects.reduce(
+ (acc, project) => {
+ const channel = project.config.channel;
const key = channel !== null ? channel : "null";
acc[key] = acc[key] || [];
- acc[key].push(product);
+ acc[key].push(project);
return acc;
},
- {} as Record
+ {} as Record
);
- Object.keys(groupedProducts).forEach((channel) => {
- groupedProducts[channel].sort((a, b) => a.name.localeCompare(b.name));
+ Object.keys(groupedProjects).forEach((channel) => {
+ groupedProjects[channel].sort((a, b) => a.name.localeCompare(b.name));
});
- return channelOrder.flatMap((channel) => groupedProducts[channel !== null ? channel : "null"] || []);
- }, [products]);
-
- const handleEnvironmentChangeByProduct = (productId: string) => {
- router.push(`/products/${productId}/`);
- };
+ return channelOrder.flatMap((channel) => groupedProjects[channel !== null ? channel : "null"] || []);
+ }, [projects]);
const handleEnvironmentChangeByOrganization = (organizationId: string) => {
router.push(`/organizations/${organizationId}/`);
};
- const handleAddProduct = (organizationId: string) => {
- router.push(`/organizations/${organizationId}/products/new/mode`);
- };
-
const mainNavigation = useMemo(
() => [
{
@@ -189,9 +182,9 @@ export const MainNavigation = ({
},
{
name: t("common.configuration"),
- href: `/environments/${environment.id}/product/general`,
+ href: `/environments/${environment.id}/project/general`,
icon: Cog,
- isActive: pathname?.includes("/product"),
+ isActive: pathname?.includes("/project"),
},
],
[environment.id, pathname, isMember]
@@ -247,7 +240,7 @@ export const MainNavigation = ({
return (
<>
- {product && (
+ {project && (
)}
- {/* Product Switch */}
+ {/* Project Switch */}
{!isBilling && (
-
-
-
-
- {product.config.channel === "website" ? (
-
- ) : product.config.channel === "app" ? (
-
- ) : product.config.channel === "link" ? (
-
- ) : (
-
- )}
-
- {!isCollapsed && !isTextVisible && (
- <>
-
-
- {product.name}
-
-
- {product.config.channel === "link"
- ? "Link & Email"
- : capitalizeFirstLetter(product.config.channel)}
-
-
-
- >
- )}
-
-
-
- handleEnvironmentChangeByProduct(v)}>
- {sortedProducts.map((product) => (
-
-
- {product.config.channel === "website" ? (
-
- ) : product.config.channel === "app" ? (
-
- ) : product.config.channel === "link" ? (
-
- ) : (
-
- )}
-
- {product?.name}
-
- ))}
-
- {isOwnerOrManager && (
- <>
-
- handleAddProduct(organization.id)}
- icon={ }>
- {t("common.add_product")}
-
- >
- )}
-
-
+
)}
{/* User Switch */}
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/ProductNavItem.tsx b/apps/web/app/(app)/environments/[environmentId]/components/ProjectNavItem.tsx
similarity index 81%
rename from apps/web/app/(app)/environments/[environmentId]/components/ProductNavItem.tsx
rename to apps/web/app/(app)/environments/[environmentId]/components/ProjectNavItem.tsx
index 37b7f0fcb6..98a82c2980 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/ProductNavItem.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/ProjectNavItem.tsx
@@ -1,13 +1,13 @@
import Link from "next/link";
import { ReactNode } from "react";
-interface ProductNavItemProps {
+interface ProjectNavItemProps {
href: string;
children: ReactNode;
isActive: boolean;
}
-export const ProductNavItem = ({ href, children, isActive }: ProductNavItemProps) => {
+export const ProjectNavItem = ({ href, children, isActive }: ProjectNavItemProps) => {
const activeClass = "bg-slate-50 font-semibold";
const inactiveClass = "hover:bg-slate-50";
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx b/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx
index 75ce438821..c8ce79df60 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx
@@ -1,5 +1,5 @@
import { TopControlButtons } from "@/app/(app)/environments/[environmentId]/components/TopControlButtons";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { TEnvironment } from "@formbricks/types/environment";
import { TOrganizationRole } from "@formbricks/types/memberships";
@@ -8,14 +8,14 @@ interface SideBarProps {
environment: TEnvironment;
environments: TEnvironment[];
membershipRole?: TOrganizationRole;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}
export const TopControlBar = ({
environment,
environments,
membershipRole,
- productPermission,
+ projectPermission,
}: SideBarProps) => {
return (
@@ -26,7 +26,7 @@ export const TopControlBar = ({
environments={environments}
isFormbricksCloud={IS_FORMBRICKS_CLOUD}
membershipRole={membershipRole}
- productPermission={productPermission}
+ projectPermission={projectPermission}
/>
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx b/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx
index a5c35e7ded..d29d899cc3 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx
@@ -1,7 +1,7 @@
"use client";
import { EnvironmentSwitch } from "@/app/(app)/environments/[environmentId]/components/EnvironmentSwitch";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { Button } from "@/modules/ui/components/button";
import { CircleUserIcon, MessageCircleQuestionIcon, PlusIcon } from "lucide-react";
@@ -17,7 +17,7 @@ interface TopControlButtonsProps {
environments: TEnvironment[];
isFormbricksCloud: boolean;
membershipRole?: TOrganizationRole;
- productPermission: TTeamPermission | null;
+ projectPermission: TTeamPermission | null;
}
export const TopControlButtons = ({
@@ -25,13 +25,13 @@ export const TopControlButtons = ({
environments,
isFormbricksCloud,
membershipRole,
- productPermission,
+ projectPermission,
}: TopControlButtonsProps) => {
const t = useTranslations();
const router = useRouter();
const { isMember, isBilling } = getAccessFlags(membershipRole);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
return (
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator.tsx b/apps/web/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator.tsx
index 0d1b7d8d0d..9529466271 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator.tsx
@@ -12,13 +12,13 @@ export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProp
const stati = {
notImplemented: {
icon: AlertTriangleIcon,
- title: t("environments.product.app-connection.formbricks_sdk_not_connected"),
- subtitle: t("environments.product.app-connection.formbricks_sdk_not_connected_description"),
+ title: t("environments.project.app-connection.formbricks_sdk_not_connected"),
+ subtitle: t("environments.project.app-connection.formbricks_sdk_not_connected_description"),
},
running: {
icon: CheckIcon,
- title: t("environments.product.app-connection.receiving_data"),
- subtitle: t("environments.product.app-connection.formbricks_sdk_connected"),
+ title: t("environments.project.app-connection.receiving_data"),
+ subtitle: t("environments.project.app-connection.formbricks_sdk_connected"),
},
};
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts b/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts
index f46c2870ec..9378615fc6 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts
@@ -5,8 +5,8 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"
import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromIntegrationId,
- getProductIdFromEnvironmentId,
- getProductIdFromIntegrationId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromIntegrationId,
} from "@/lib/utils/helper";
import { z } from "zod";
import { createOrUpdateIntegration, deleteIntegration } from "@formbricks/lib/integration/service";
@@ -30,9 +30,9 @@ export const createOrUpdateIntegrationAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
@@ -56,8 +56,8 @@ export const deleteIntegrationAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromIntegrationId(parsedInput.integrationId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromIntegrationId(parsedInput.integrationId),
minPermission: "readWrite",
},
],
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/airtable/page.tsx b/apps/web/app/(app)/environments/[environmentId]/integrations/airtable/page.tsx
index 2139ac59c3..6c1c6bfcf8 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/airtable/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/airtable/page.tsx
@@ -1,6 +1,6 @@
import { AirtableWrapper } from "@/app/(app)/environments/[environmentId]/integrations/airtable/components/AirtableWrapper";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -15,7 +15,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrations } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSurveys } from "@formbricks/lib/survey/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationItem } from "@formbricks/types/integration";
@@ -40,9 +40,9 @@ const Page = async (props) => {
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const airtableIntegration: TIntegrationAirtable | undefined = integrations?.find(
@@ -58,13 +58,13 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
- product.organizationId
+ project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, environment?.productId);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/google-sheets/page.tsx b/apps/web/app/(app)/environments/[environmentId]/integrations/google-sheets/page.tsx
index 5686620e05..852fff787c 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/google-sheets/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/google-sheets/page.tsx
@@ -1,6 +1,6 @@
import { GoogleSheetWrapper } from "@/app/(app)/environments/[environmentId]/integrations/google-sheets/components/GoogleSheetWrapper";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -19,7 +19,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrations } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSurveys } from "@formbricks/lib/survey/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationGoogleSheets } from "@formbricks/types/integration/google-sheet";
@@ -43,9 +43,9 @@ const Page = async (props) => {
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const googleSheetIntegration: TIntegrationGoogleSheets | undefined = integrations?.find(
@@ -56,13 +56,13 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
- product.organizationId
+ project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, environment?.productId);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/notion/page.tsx b/apps/web/app/(app)/environments/[environmentId]/integrations/notion/page.tsx
index 6080183929..c7db627bfb 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/notion/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/notion/page.tsx
@@ -1,6 +1,6 @@
import { NotionWrapper } from "@/app/(app)/environments/[environmentId]/integrations/notion/components/NotionWrapper";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -21,7 +21,7 @@ import { getIntegrationByType } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getNotionDatabases } from "@formbricks/lib/notion/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSurveys } from "@formbricks/lib/survey/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationNotion, TIntegrationNotionDatabase } from "@formbricks/types/integration/notion";
@@ -51,9 +51,9 @@ const Page = async (props) => {
throw new Error(t("common.environment_not_found"));
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
let databasesArray: TIntegrationNotionDatabase[] = [];
@@ -64,13 +64,13 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
- product.organizationId
+ project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, environment?.productId);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/page.tsx b/apps/web/app/(app)/environments/[environmentId]/integrations/page.tsx
index 47263728ea..48b5be7dad 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/page.tsx
@@ -8,7 +8,7 @@ import SlackLogo from "@/images/slacklogo.png";
import WebhookLogo from "@/images/webhook.png";
import ZapierLogo from "@/images/zapier-small.png";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { Card } from "@/modules/ui/components/integration-card";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -66,9 +66,9 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember, isBilling } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, environment?.productId);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
@@ -222,7 +222,7 @@ const Page = async (props) => {
docsHref: "https://formbricks.com/docs/app-surveys/quickstart",
docsText: t("common.docs"),
docsNewTab: true,
- connectHref: `/environments/${environmentId}/product/app-connection`,
+ connectHref: `/environments/${environmentId}/project/app-connection`,
connectText: t("common.connect"),
connectNewTab: false,
label: "Javascript SDK",
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts b/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts
index b3188cbc3d..708a156fa9 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts
@@ -2,7 +2,7 @@
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromEnvironmentId, getProductIdFromEnvironmentId } from "@/lib/utils/helper";
+import { getOrganizationIdFromEnvironmentId, getProjectIdFromEnvironmentId } from "@/lib/utils/helper";
import { z } from "zod";
import { getSlackChannels } from "@formbricks/lib/slack/service";
import { ZId } from "@formbricks/types/common";
@@ -23,8 +23,8 @@ export const getSlackChannelsAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
minPermission: "readWrite",
},
],
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/slack/page.tsx b/apps/web/app/(app)/environments/[environmentId]/integrations/slack/page.tsx
index 57cc28bb39..e4a8450af7 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/slack/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/slack/page.tsx
@@ -1,6 +1,6 @@
import { SlackWrapper } from "@/app/(app)/environments/[environmentId]/integrations/slack/components/SlackWrapper";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -14,7 +14,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrationByType } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSurveys } from "@formbricks/lib/survey/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationSlack } from "@formbricks/types/integration/slack";
@@ -39,22 +39,22 @@ const Page = async (props) => {
throw new Error(t("common.environment_not_found"));
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const locale = await findMatchingLocale();
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
- product.organizationId
+ project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, environment?.productId);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts b/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts
index 49bd0963b6..59a787fa70 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts
@@ -5,7 +5,7 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"
import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromWebhookId,
- getProductIdFromEnvironmentId,
+ getProjectIdFromEnvironmentId,
} from "@/lib/utils/helper";
import { z } from "zod";
import { createWebhook, deleteWebhook, updateWebhook } from "@formbricks/lib/webhook/service";
@@ -30,9 +30,9 @@ export const createWebhookAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
@@ -56,9 +56,9 @@ export const deleteWebhookAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(parsedInput.id),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.id),
},
],
});
@@ -83,9 +83,9 @@ export const updateWebhookAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(parsedInput.webhookId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.webhookId),
},
],
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/page.tsx b/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/page.tsx
index 2a0a06b414..4bd8609359 100644
--- a/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/page.tsx
@@ -3,7 +3,7 @@ import { WebhookRowData } from "@/app/(app)/environments/[environmentId]/integra
import { WebhookTable } from "@/app/(app)/environments/[environmentId]/integrations/webhooks/components/WebhookTable";
import { WebhookTableHeading } from "@/app/(app)/environments/[environmentId]/integrations/webhooks/components/WebhookTableHeading";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
@@ -44,9 +44,9 @@ const Page = async (props) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session?.user.id, environment?.productId);
+ const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/layout.tsx b/apps/web/app/(app)/environments/[environmentId]/layout.tsx
index e1e9eb6f6f..d6e10c6d5b 100644
--- a/apps/web/app/(app)/environments/[environmentId]/layout.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/layout.tsx
@@ -8,7 +8,7 @@ import { notFound, redirect } from "next/navigation";
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getUser } from "@formbricks/lib/user/service";
import { AuthorizationError } from "@formbricks/types/errors";
import { FormbricksClient } from "../../components/FormbricksClient";
@@ -40,9 +40,9 @@ export const EnvLayout = async (props) => {
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
- const product = await getProductByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(params.environmentId);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const membership = await getMembershipByUserIdOrganizationId(session.user.id, organization.id);
diff --git a/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts b/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts
deleted file mode 100644
index 9c2e82dbf7..0000000000
--- a/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-"use server";
-
-import { authenticatedActionClient } from "@/lib/utils/action-client";
-import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromProductId } from "@/lib/utils/helper";
-import { z } from "zod";
-import { deleteProduct, getUserProducts } from "@formbricks/lib/product/service";
-import { ZId } from "@formbricks/types/common";
-
-const ZProductDeleteAction = z.object({
- productId: ZId,
-});
-
-export const deleteProductAction = authenticatedActionClient
- .schema(ZProductDeleteAction)
- .action(async ({ ctx, parsedInput }) => {
- const organizationId = await getOrganizationIdFromProductId(parsedInput.productId);
-
- await checkAuthorizationUpdated({
- userId: ctx.user.id,
- organizationId: organizationId,
- access: [
- {
- type: "organization",
- roles: ["owner", "manager"],
- },
- ],
- });
-
- const availableProducts = (await getUserProducts(ctx.user.id, organizationId)) ?? null;
-
- if (!!availableProducts && availableProducts?.length <= 1) {
- throw new Error("You can't delete the last product in the environment.");
- }
-
- // delete product
- return await deleteProduct(parsedInput.productId);
- });
diff --git a/apps/web/app/(app)/environments/[environmentId]/product/page.tsx b/apps/web/app/(app)/environments/[environmentId]/product/page.tsx
deleted file mode 100644
index edbb091e46..0000000000
--- a/apps/web/app/(app)/environments/[environmentId]/product/page.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import { redirect } from "next/navigation";
-
-const Page = async (props) => {
- const params = await props.params;
- return redirect(`/environments/${params.environmentId}/product/general`);
-};
-
-export default Page;
diff --git a/apps/web/app/(app)/environments/[environmentId]/product/teams/page.tsx b/apps/web/app/(app)/environments/[environmentId]/product/teams/page.tsx
deleted file mode 100644
index 4d8358c1e3..0000000000
--- a/apps/web/app/(app)/environments/[environmentId]/product/teams/page.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import { ProductTeams } from "@/modules/ee/teams/product-teams/page";
-
-export default ProductTeams;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/(setup)/app-connection/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/project/(setup)/app-connection/loading.tsx
new file mode 100644
index 0000000000..99f25bc677
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/(setup)/app-connection/loading.tsx
@@ -0,0 +1,3 @@
+import { AppConnectionLoading } from "@/modules/projects/settings/(setup)/app-connection/loading";
+
+export default AppConnectionLoading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/(setup)/app-connection/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/(setup)/app-connection/page.tsx
new file mode 100644
index 0000000000..a6d96f27be
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/(setup)/app-connection/page.tsx
@@ -0,0 +1,3 @@
+import { AppConnectionPage } from "@/modules/projects/settings/(setup)/app-connection/page";
+
+export default AppConnectionPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/api-keys/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/project/api-keys/loading.tsx
new file mode 100644
index 0000000000..68619f57fb
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/api-keys/loading.tsx
@@ -0,0 +1,3 @@
+import { APIKeysLoading } from "@/modules/projects/settings/api-keys/loading";
+
+export default APIKeysLoading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/api-keys/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/api-keys/page.tsx
new file mode 100644
index 0000000000..c631feeabc
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/api-keys/page.tsx
@@ -0,0 +1,3 @@
+import { APIKeysPage } from "@/modules/projects/settings/api-keys/page";
+
+export default APIKeysPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/general/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/project/general/loading.tsx
new file mode 100644
index 0000000000..2f245b546e
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/general/loading.tsx
@@ -0,0 +1,3 @@
+import { GeneralSettingsLoading } from "@/modules/projects/settings/general/loading";
+
+export default GeneralSettingsLoading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/general/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/general/page.tsx
new file mode 100644
index 0000000000..71eda04bf1
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/general/page.tsx
@@ -0,0 +1,3 @@
+import { GeneralSettingsPage } from "@/modules/projects/settings/general/page";
+
+export default GeneralSettingsPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/languages/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/project/languages/loading.tsx
new file mode 100644
index 0000000000..0b5a9bd2d8
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/languages/loading.tsx
@@ -0,0 +1,3 @@
+import { LanguagesLoading } from "@/modules/ee/languages/loading";
+
+export default LanguagesLoading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/languages/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/languages/page.tsx
new file mode 100644
index 0000000000..ee76ebc7d5
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/languages/page.tsx
@@ -0,0 +1,3 @@
+import { LanguagesPage } from "@/modules/ee/languages/page";
+
+export default LanguagesPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/layout.tsx b/apps/web/app/(app)/environments/[environmentId]/project/layout.tsx
new file mode 100644
index 0000000000..7fea4aafdd
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/layout.tsx
@@ -0,0 +1,3 @@
+import { ProjectSettingsLayout } from "@/modules/projects/settings/layout";
+
+export default ProjectSettingsLayout;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/look/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/project/look/loading.tsx
new file mode 100644
index 0000000000..f191b177f2
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/look/loading.tsx
@@ -0,0 +1,3 @@
+import { ProjectLookSettingsLoading } from "@/modules/projects/settings/look/loading";
+
+export default ProjectLookSettingsLoading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/look/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/look/page.tsx
new file mode 100644
index 0000000000..79a9c8a520
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/look/page.tsx
@@ -0,0 +1,3 @@
+import { ProjectLookSettingsPage } from "@/modules/projects/settings/look/page";
+
+export default ProjectLookSettingsPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/page.tsx
new file mode 100644
index 0000000000..e7943862dd
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/page.tsx
@@ -0,0 +1,3 @@
+import { ProjectSettingsPage } from "@/modules/projects/settings/page";
+
+export default ProjectSettingsPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/tags/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/project/tags/loading.tsx
new file mode 100644
index 0000000000..db598e3424
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/tags/loading.tsx
@@ -0,0 +1,3 @@
+import { TagsLoading } from "@/modules/projects/settings/tags/loading";
+
+export default TagsLoading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/tags/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/tags/page.tsx
new file mode 100644
index 0000000000..4c5acbe27d
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/tags/page.tsx
@@ -0,0 +1,3 @@
+import { TagsPage } from "@/modules/projects/settings/tags/page";
+
+export default TagsPage;
diff --git a/apps/web/app/(app)/environments/[environmentId]/project/teams/page.tsx b/apps/web/app/(app)/environments/[environmentId]/project/teams/page.tsx
new file mode 100644
index 0000000000..60fe580b26
--- /dev/null
+++ b/apps/web/app/(app)/environments/[environmentId]/project/teams/page.tsx
@@ -0,0 +1,3 @@
+import { ProjectTeams } from "@/modules/ee/teams/project-teams/page";
+
+export default ProjectTeams;
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/layout.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/layout.tsx
index 9689fdecc1..6d7a953f7e 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/layout.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/layout.tsx
@@ -2,7 +2,7 @@ import { authOptions } from "@/modules/auth/lib/authOptions";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
const AccountSettingsLayout = async (props) => {
const params = await props.params;
@@ -10,9 +10,9 @@ const AccountSettingsLayout = async (props) => {
const { children } = props;
const t = await getTranslations();
- const [organization, product, session] = await Promise.all([
+ const [organization, project, session] = await Promise.all([
getOrganizationByEnvironmentId(params.environmentId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
getServerSession(authOptions),
]);
@@ -20,8 +20,8 @@ const AccountSettingsLayout = async (props) => {
throw new Error(t("common.organization_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
if (!session) {
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/EditAlerts.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/EditAlerts.tsx
index 66cc21a6eb..8fb7417912 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/EditAlerts.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/EditAlerts.tsx
@@ -38,7 +38,7 @@ export const EditAlerts = ({
{t("environments.settings.notifications.auto_subscribe_to_new_surveys")}
- {membership.organization.products.some((product) =>
- product.environments.some((environment) => environment.surveys.length > 0)
+ {membership.organization.projects.some((project) =>
+ project.environments.some((environment) => environment.surveys.length > 0)
) ? (
- {membership.organization.products.map((product) => (
-
- {product.environments.map((environment) => (
+ {membership.organization.projects.map((project) => (
+
+ {project.environments.map((environment) => (
{environment.surveys.map((survey) => (
{survey.name}
-
{product.name}
+
{project.name}
-
{t("common.product")}
+
{t("common.project")}
{t("common.weekly_summary")}
- {membership.organization.products.map((product) => (
+ {membership.organization.projects.map((project) => (
-
{product?.name}
+ key={project.id}>
+
{project?.name}
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/NotificationSwitch.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/NotificationSwitch.tsx
index fe962d0b57..3874cdfcf7 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/NotificationSwitch.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/components/NotificationSwitch.tsx
@@ -8,7 +8,7 @@ import { TUserNotificationSettings } from "@formbricks/types/user";
import { updateNotificationSettingsAction } from "../actions";
interface NotificationSwitchProps {
- surveyOrProductOrOrganizationId: string;
+ surveyOrProjectOrOrganizationId: string;
notificationSettings: TUserNotificationSettings;
notificationType: "alert" | "weeklySummary" | "unsubscribedOrganizationIds";
autoDisableNotificationType?: string;
@@ -16,7 +16,7 @@ interface NotificationSwitchProps {
}
export const NotificationSwitch = ({
- surveyOrProductOrOrganizationId,
+ surveyOrProjectOrOrganizationId,
notificationSettings,
notificationType,
autoDisableNotificationType,
@@ -26,8 +26,8 @@ export const NotificationSwitch = ({
const t = useTranslations();
const isChecked =
notificationType === "unsubscribedOrganizationIds"
- ? !notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProductOrOrganizationId)
- : notificationSettings[notificationType][surveyOrProductOrOrganizationId] === true;
+ ? !notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProjectOrOrganizationId)
+ : notificationSettings[notificationType][surveyOrProjectOrOrganizationId] === true;
const handleSwitchChange = async () => {
setIsLoading(true);
@@ -35,19 +35,19 @@ export const NotificationSwitch = ({
let updatedNotificationSettings = { ...notificationSettings };
if (notificationType === "unsubscribedOrganizationIds") {
const unsubscribedOrganizationIds = updatedNotificationSettings.unsubscribedOrganizationIds ?? [];
- if (unsubscribedOrganizationIds.includes(surveyOrProductOrOrganizationId)) {
+ if (unsubscribedOrganizationIds.includes(surveyOrProjectOrOrganizationId)) {
updatedNotificationSettings.unsubscribedOrganizationIds = unsubscribedOrganizationIds.filter(
- (id) => id !== surveyOrProductOrOrganizationId
+ (id) => id !== surveyOrProjectOrOrganizationId
);
} else {
updatedNotificationSettings.unsubscribedOrganizationIds = [
...unsubscribedOrganizationIds,
- surveyOrProductOrOrganizationId,
+ surveyOrProjectOrOrganizationId,
];
}
} else {
- updatedNotificationSettings[notificationType][surveyOrProductOrOrganizationId] =
- !updatedNotificationSettings[notificationType][surveyOrProductOrOrganizationId];
+ updatedNotificationSettings[notificationType][surveyOrProjectOrOrganizationId] =
+ !updatedNotificationSettings[notificationType][surveyOrProjectOrOrganizationId];
}
await updateNotificationSettingsAction({ notificationSettings: updatedNotificationSettings });
@@ -57,12 +57,12 @@ export const NotificationSwitch = ({
useEffect(() => {
if (
autoDisableNotificationType &&
- autoDisableNotificationElementId === surveyOrProductOrOrganizationId &&
+ autoDisableNotificationElementId === surveyOrProjectOrOrganizationId &&
isChecked
) {
switch (notificationType) {
case "alert":
- if (notificationSettings[notificationType][surveyOrProductOrOrganizationId] === true) {
+ if (notificationSettings[notificationType][surveyOrProjectOrOrganizationId] === true) {
handleSwitchChange();
toast.success(
t(
@@ -76,7 +76,7 @@ export const NotificationSwitch = ({
break;
case "unsubscribedOrganizationIds":
- if (!notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProductOrOrganizationId)) {
+ if (!notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProjectOrOrganizationId)) {
handleSwitchChange();
toast.success(
t(
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/loading.tsx
index fe53c08d81..8ca8f72e5e 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/loading.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/loading.tsx
@@ -13,7 +13,7 @@ const Loading = () => {
skeletonLines: [{ classes: "h-6 w-28" }, { classes: "h-10 w-128" }, { classes: "h-10 w-128" }],
},
{
- title: t("environments.settings.notifications.weekly_summary_products"),
+ title: t("environments.settings.notifications.weekly_summary_projects"),
description: t("environments.settings.notifications.stay_up_to_date_with_a_Weekly_every_Monday"),
skeletonLines: [{ classes: "h-6 w-28" }, { classes: "h-10 w-128" }, { classes: "h-10 w-128" }],
},
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/page.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/page.tsx
index feab128a50..becc60a674 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/page.tsx
@@ -23,12 +23,12 @@ const setCompleteNotificationSettings = (
unsubscribedOrganizationIds: notificationSettings.unsubscribedOrganizationIds || [],
};
for (const membership of memberships) {
- for (const product of membership.organization.products) {
+ for (const project of membership.organization.projects) {
// set default values for weekly summary
- newNotificationSettings.weeklySummary[product.id] =
- (notificationSettings.weeklySummary && notificationSettings.weeklySummary[product.id]) || false;
+ newNotificationSettings.weeklySummary[project.id] =
+ (notificationSettings.weeklySummary && notificationSettings.weeklySummary[project.id]) || false;
// set default values for alerts
- for (const environment of product.environments) {
+ for (const environment of project.environments) {
for (const survey of environment.surveys) {
newNotificationSettings.alert[survey.id] =
notificationSettings[survey.id]?.responseFinished ||
@@ -50,17 +50,17 @@ const getMemberships = async (userId: string): Promise
=> {
},
OR: [
{
- // Fetch all products if user role is owner or manager
+ // Fetch all projects if user role is owner or manager
role: {
in: ["owner", "manager"],
},
},
{
- // Filter products based on team membership if user is not owner or manager
+ // Filter projects based on team membership if user is not owner or manager
organization: {
- products: {
+ projects: {
some: {
- productTeams: {
+ projectTeams: {
some: {
team: {
teamUsers: {
@@ -82,12 +82,12 @@ const getMemberships = async (userId: string): Promise => {
select: {
id: true,
name: true,
- products: {
+ projects: {
// Apply conditional filtering based on user's role
where: {
OR: [
{
- // Fetch all products if user is owner or manager
+ // Fetch all projects if user is owner or manager
organization: {
memberships: {
some: {
@@ -100,8 +100,8 @@ const getMemberships = async (userId: string): Promise => {
},
},
{
- // Only include products accessible through teams if user is not owner or manager
- productTeams: {
+ // Only include projects accessible through teams if user is not owner or manager
+ projectTeams: {
some: {
team: {
teamUsers: {
@@ -180,7 +180,7 @@ const Page = async (props) => {
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/types.ts b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/types.ts
index ca3f2848a7..9378358236 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/types.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/notifications/types.ts
@@ -4,7 +4,7 @@ export interface Membership {
organization: {
id: string;
name: string;
- products: {
+ projects: {
id: string;
name: string;
environments: {
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/enterprise/page.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/enterprise/page.tsx
index 2b8c27852e..cf4e1b80e6 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/enterprise/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/enterprise/page.tsx
@@ -46,7 +46,7 @@ const Page = async (props) => {
const paidFeatures = [
{
- title: t("environments.product.languages.multi_language_surveys"),
+ title: t("environments.project.languages.multi_language_surveys"),
comingSoon: false,
onRequest: false,
},
diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/layout.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/layout.tsx
index cbef90a105..151a07d279 100644
--- a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/layout.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/layout.tsx
@@ -2,7 +2,7 @@ import { authOptions } from "@/modules/auth/lib/authOptions";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
const Layout = async (props) => {
const params = await props.params;
@@ -10,9 +10,9 @@ const Layout = async (props) => {
const { children } = props;
const t = await getTranslations();
- const [organization, product, session] = await Promise.all([
+ const [organization, project, session] = await Promise.all([
getOrganizationByEnvironmentId(params.environmentId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
getServerSession(authOptions),
]);
@@ -20,8 +20,8 @@ const Layout = async (props) => {
throw new Error(t("common.organization_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
if (!session) {
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts
index 2e63bdca8c..7c7b68503f 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts
@@ -1,8 +1,9 @@
"use server";
+import { generateInsightsForSurvey } from "@/app/api/(internal)/insights/lib/utils";
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromSurveyId, getProductIdFromSurveyId } from "@/lib/utils/helper";
+import { getOrganizationIdFromSurveyId, getProjectIdFromSurveyId } from "@/lib/utils/helper";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { getResponseCountBySurveyId, getResponses } from "@formbricks/lib/response/service";
@@ -35,9 +36,9 @@ export const getResponsesAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -69,9 +70,9 @@ export const getSurveySummaryAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -98,12 +99,40 @@ export const getResponseCountAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
return getResponseCountBySurveyId(parsedInput.surveyId, parsedInput.filterCriteria);
});
+
+const ZGenerateInsightsForSurveyAction = z.object({
+ surveyId: ZId,
+});
+
+export const generateInsightsForSurveyAction = authenticatedActionClient
+ .schema(ZGenerateInsightsForSurveyAction)
+ .action(async ({ ctx, parsedInput }) => {
+ await checkAuthorizationUpdated({
+ userId: ctx.user.id,
+ organizationId: await getOrganizationIdFromSurveyId(parsedInput.surveyId),
+ access: [
+ {
+ type: "organization",
+ schema: ZGenerateInsightsForSurveyAction,
+ data: parsedInput,
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "projectTeam",
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
+ minPermission: "readWrite",
+ },
+ ],
+ });
+
+ generateInsightsForSurvey(parsedInput.surveyId);
+ });
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/EmptyInAppSurveys.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/EmptyInAppSurveys.tsx
index 49acaa3878..45a1ac6ea5 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/EmptyInAppSurveys.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/EmptyInAppSurveys.tsx
@@ -25,7 +25,7 @@ export const EmptyAppSurveys = ({ environment }: TEmptyAppSurveysProps) => {
{t("environments.surveys.summary.connect_your_website_or_app_with_formbricks_to_get_started")}
-
+
{t("common.connect")}
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 452cc83d96..bd4a9fa36e 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
@@ -5,7 +5,7 @@ import { SurveyAnalysisCTA } from "@/app/(app)/environments/[environmentId]/surv
import { needsInsightsGeneration } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getIsAIEnabled } from "@/modules/ee/license-check/lib/utils";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
@@ -20,7 +20,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getResponseCountBySurveyId } from "@formbricks/lib/response/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
@@ -45,9 +45,9 @@ const Page = async (props) => {
if (!survey) {
throw new Error(t("common.survey_not_found"));
}
- const product = await getProductByEnvironmentId(environment.id);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(environment.id);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const user = await getUser(session.user.id);
@@ -66,7 +66,7 @@ const Page = async (props) => {
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const permission = await getProductPermissionByUserId(session.user.id, product.id);
+ const permission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasReadAccess } = getTeamPermissionFlags(permission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts
index 27c8651f91..97da425d9b 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts
@@ -3,7 +3,7 @@
import { getEmailTemplateHtml } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate";
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromSurveyId, getProductIdFromSurveyId } from "@/lib/utils/helper";
+import { getOrganizationIdFromSurveyId, getProjectIdFromSurveyId } from "@/lib/utils/helper";
import { sendEmbedSurveyPreviewEmail } from "@/modules/email";
import { customAlphabet } from "nanoid";
import { z } from "zod";
@@ -27,9 +27,9 @@ export const sendEmbedSurveyPreviewEmailAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -70,9 +70,9 @@ export const generateResultShareUrlAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -108,8 +108,8 @@ export const getResultShareUrlAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
minPermission: "readWrite",
},
],
@@ -139,9 +139,9 @@ export const deleteResultShareUrlAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -170,9 +170,9 @@ export const getEmailHtmlAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/AppTab.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/AppTab.tsx
index bf3c93ff18..17202985ba 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/AppTab.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/AppTab.tsx
@@ -64,7 +64,7 @@ const WebAppTab = ({ environmentId }) => {
{t("common.follow_these")}{" "}
{t("environments.surveys.summary.setup_instructions")}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/WebsiteTab.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/WebsiteTab.tsx
index 0b825fbbd8..870b5b0bc5 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/WebsiteTab.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/WebsiteTab.tsx
@@ -94,7 +94,7 @@ const PopupTab = ({ environmentId }) => {
{t("common.follow_these")}{" "}
{t("environments.surveys.summary.setup_instructions")}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx
index 4d23e983e9..3c65ef968c 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx
@@ -1,6 +1,6 @@
import { getPreviewEmailTemplateHtml } from "@/modules/email/components/preview-email-template";
import { WEBAPP_URL } from "@formbricks/lib/constants";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getStyling } from "@formbricks/lib/utils/styling";
@@ -9,12 +9,12 @@ export const getEmailTemplateHtml = async (surveyId: string) => {
if (!survey) {
throw new Error("Survey not found");
}
- const product = await getProductByEnvironmentId(survey.environmentId);
- if (!product) {
- throw new Error("Product not found");
+ const project = await getProjectByEnvironmentId(survey.environmentId);
+ if (!project) {
+ throw new Error("Project not found");
}
- const styling = getStyling(product, survey);
+ const styling = getStyling(project, survey);
const surveyUrl = WEBAPP_URL + "/s/" + survey.id;
const html = await getPreviewEmailTemplateHtml(survey, surveyUrl, styling);
const doctype =
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx
index ec8c8706c9..cb63ffe66c 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx
@@ -5,7 +5,7 @@ import { SurveyAnalysisCTA } from "@/app/(app)/environments/[environmentId]/surv
import { needsInsightsGeneration } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getIsAIEnabled } from "@/modules/ee/license-check/lib/utils";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
@@ -23,7 +23,7 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getResponseCountBySurveyId } from "@formbricks/lib/response/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getUser } from "@formbricks/lib/user/service";
@@ -54,9 +54,9 @@ const Page = async (props) => {
throw new Error(t("common.survey_not_found"));
}
- const product = await getProductByEnvironmentId(environment.id);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ const project = await getProjectByEnvironmentId(environment.id);
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const user = await getUser(session.user.id);
@@ -73,8 +73,8 @@ const Page = async (props) => {
const totalResponseCount = await getResponseCountBySurveyId(params.surveyId);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts
index 5e648b1dac..3603d0f0bb 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts
@@ -2,7 +2,7 @@
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromSurveyId, getProductIdFromSurveyId } from "@/lib/utils/helper";
+import { getOrganizationIdFromSurveyId, getProjectIdFromSurveyId } from "@/lib/utils/helper";
import { getSurveyFollowUpsPermission } from "@/modules/ee/license-check/lib/utils";
import { checkMultiLanguagePermission } from "@/modules/ee/multi-language-surveys/lib/actions";
import { z } from "zod";
@@ -33,9 +33,9 @@ export const getResponsesDownloadUrlAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -65,9 +65,9 @@ export const getSurveyFilterDataAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -114,8 +114,8 @@ export const updateSurveyAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromSurveyId(parsedInput.id),
+ type: "projectTeam",
+ projectId: await getProjectIdFromSurveyId(parsedInput.id),
minPermission: "readWrite",
},
],
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/actions.ts
index e9224eaa47..7d63e7939c 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/actions.ts
@@ -6,12 +6,12 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"
import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromSurveyId,
- getProductIdFromEnvironmentId,
- getProductIdFromSurveyId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromSurveyId,
} from "@/lib/utils/helper";
import { getEnvironment } from "@/lib/utils/services";
import { z } from "zod";
-import { getUserProducts } from "@formbricks/lib/product/service";
+import { getUserProjects } from "@formbricks/lib/project/service";
import { copySurveyToOtherEnvironment, deleteSurvey } from "@formbricks/lib/survey/service";
import { generateSurveySingleUseId } from "@formbricks/lib/utils/singleUseSurveys";
import { ZId } from "@formbricks/types/common";
@@ -34,9 +34,9 @@ export const getSurveyAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
@@ -72,9 +72,9 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: sourceEnvironment.productId,
+ projectId: sourceEnvironment.projectId,
},
],
});
@@ -88,9 +88,9 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: targetEnvironment.productId,
+ projectId: targetEnvironment.projectId,
},
],
});
@@ -103,12 +103,12 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient
);
});
-const ZGetProductsByEnvironmentIdAction = z.object({
+const ZGetProjectsByEnvironmentIdAction = z.object({
environmentId: ZId,
});
-export const getProductsByEnvironmentIdAction = authenticatedActionClient
- .schema(ZGetProductsByEnvironmentIdAction)
+export const getProjectsByEnvironmentIdAction = authenticatedActionClient
+ .schema(ZGetProjectsByEnvironmentIdAction)
.action(async ({ ctx, parsedInput }) => {
const organizationId = await getOrganizationIdFromEnvironmentId(parsedInput.environmentId);
await checkAuthorizationUpdated({
@@ -120,14 +120,14 @@ export const getProductsByEnvironmentIdAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
- return await getUserProducts(ctx.user.id, organizationId);
+ return await getUserProjects(ctx.user.id, organizationId);
});
const ZDeleteSurveyAction = z.object({
@@ -146,8 +146,8 @@ export const deleteSurveyAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
minPermission: "readWrite",
},
],
@@ -173,8 +173,8 @@ export const generateSingleUseIdAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
minPermission: "readWrite",
},
],
@@ -204,9 +204,9 @@ export const getSurveysAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/components/CopySurveyForm.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/components/CopySurveyForm.tsx
index 2ee6984a7d..e651979152 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/components/CopySurveyForm.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/components/CopySurveyForm.tsx
@@ -14,15 +14,15 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useTranslations } from "next-intl";
import { useFieldArray, useForm } from "react-hook-form";
import toast from "react-hot-toast";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
export const CopySurveyForm = ({
- defaultProducts,
+ defaultProjects,
survey,
onCancel,
setOpen,
}: {
- defaultProducts: TProduct[];
+ defaultProjects: TProject[];
survey: TSurvey;
onCancel: () => void;
setOpen: (value: boolean) => void;
@@ -31,24 +31,24 @@ export const CopySurveyForm = ({
const form = useForm({
resolver: zodResolver(ZSurveyCopyFormValidation),
defaultValues: {
- products: defaultProducts.map((product) => ({
- product: product.id,
+ projects: defaultProjects.map((project) => ({
+ project: project.id,
environments: [],
})),
},
});
const formFields = useFieldArray({
- name: "products",
+ name: "projects",
control: form.control,
});
const onSubmit = async (data: TSurveyCopyFormData) => {
- const filteredData = data.products.filter((product) => product.environments.length > 0);
+ const filteredData = data.projects.filter((project) => project.environments.length > 0);
try {
- filteredData.map(async (product) => {
- product.environments.map(async (environment) => {
+ filteredData.map(async (project) => {
+ project.environments.map(async (environment) => {
await copySurveyToOtherEnvironmentAction({
environmentId: survey.environmentId,
surveyId: survey.id,
@@ -70,23 +70,23 @@ export const CopySurveyForm = ({
onSubmit={form.handleSubmit(onSubmit)}
className="relative flex h-full w-full flex-col gap-8 overflow-y-auto bg-white p-4">
- {formFields.fields.map((field, productIndex) => {
- const product = defaultProducts.find((product) => product.id === field.product);
+ {formFields.fields.map((field, projectIndex) => {
+ const project = defaultProjects.find((project) => project.id === field.project);
return (
-
+
-
{product?.name}
+
{project?.name}
- {product?.environments.map((environment) => {
+ {project?.environments.map((environment) => {
return (
{
return (
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyCopyOptions.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyCopyOptions.tsx
index 47cfca9248..0b9f1f5c52 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyCopyOptions.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyCopyOptions.tsx
@@ -1,12 +1,12 @@
"use client";
-import { getProductsByEnvironmentIdAction } from "@/app/(app)/environments/[environmentId]/surveys/actions";
+import { getProjectsByEnvironmentIdAction } from "@/app/(app)/environments/[environmentId]/surveys/actions";
import { TSurvey } from "@/app/(app)/environments/[environmentId]/surveys/types/surveys";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import { CopySurveyForm } from "./CopySurveyForm";
interface SurveyCopyOptionsProps {
@@ -17,26 +17,26 @@ interface SurveyCopyOptionsProps {
}
const SurveyCopyOptions = ({ environmentId, survey, onCancel, setOpen }: SurveyCopyOptionsProps) => {
- const [products, setProducts] = useState([]);
- const [productLoading, setProductLoading] = useState(true);
+ const [projects, setProjects] = useState([]);
+ const [projectLoading, setProjectLoading] = useState(true);
useEffect(() => {
- const fetchProducts = async () => {
- const getProductsByEnvironmentIdResponse = await getProductsByEnvironmentIdAction({ environmentId });
- if (getProductsByEnvironmentIdResponse?.data) {
- setProducts(getProductsByEnvironmentIdResponse?.data);
+ const fetchProjects = async () => {
+ const getProjectsByEnvironmentIdResponse = await getProjectsByEnvironmentIdAction({ environmentId });
+ if (getProjectsByEnvironmentIdResponse?.data) {
+ setProjects(getProjectsByEnvironmentIdResponse?.data);
} else {
- const errorMessage = getFormattedErrorMessage(getProductsByEnvironmentIdResponse);
+ const errorMessage = getFormattedErrorMessage(getProjectsByEnvironmentIdResponse);
toast.error(errorMessage);
}
- setProductLoading(false);
+ setProjectLoading(false);
};
- fetchProducts();
+ fetchProjects();
}, [environmentId]);
- if (productLoading) {
+ if (projectLoading) {
return (
@@ -44,7 +44,7 @@ const SurveyCopyOptions = ({ environmentId, survey, onCancel, setOpen }: SurveyC
);
}
- return ;
+ return ;
};
export default SurveyCopyOptions;
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyFilters.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyFilters.tsx
index 23872d7265..afcc0c2c18 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyFilters.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/components/SurveyFilters.tsx
@@ -13,14 +13,14 @@ import { ChevronDownIcon, X } from "lucide-react";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { useDebounce } from "react-use";
-import { TProductConfigChannel } from "@formbricks/types/product";
+import { TProjectConfigChannel } from "@formbricks/types/project";
import { TFilterOption, TSortOption, TSurveyFilters } from "@formbricks/types/surveys/types";
import { SurveyFilterDropdown } from "./SurveyFilterDropdown";
interface SurveyFilterProps {
surveyFilters: TSurveyFilters;
setSurveyFilters: React.Dispatch>;
- currentProductChannel: TProductConfigChannel;
+ currentProjectChannel: TProjectConfigChannel;
}
const creatorOptions: TFilterOption[] = [
@@ -57,7 +57,7 @@ const sortOptions: TSortOption[] = [
export const SurveyFilters = ({
surveyFilters,
setSurveyFilters,
- currentProductChannel,
+ currentProjectChannel,
}: SurveyFilterProps) => {
const { createdBy, sortBy, status, type } = surveyFilters;
const [name, setName] = useState("");
@@ -146,7 +146,7 @@ export const SurveyFilters = ({
toggleDropdown={toggleDropdown}
/>
- {currentProductChannel !== "link" && (
+ {currentProjectChannel !== "link" && (
{
const [surveys, setSurveys] = useState([]);
@@ -138,7 +138,7 @@ export const SurveysList = ({
{surveys.length > 0 ? (
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx
index 383937923f..e93d0ec723 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx
@@ -1,6 +1,6 @@
import { SurveysList } from "@/app/(app)/environments/[environmentId]/surveys/components/SurveyList";
import { authOptions } from "@/modules/auth/lib/authOptions";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { TemplateList } from "@/modules/surveys/components/TemplateList";
import { Button } from "@/modules/ui/components/button";
@@ -16,7 +16,7 @@ import { getEnvironment, getEnvironments } from "@formbricks/lib/environment/ser
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getSurveyCount } from "@formbricks/lib/survey/service";
import { getUser } from "@formbricks/lib/user/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
@@ -39,7 +39,7 @@ const Page = async (props: SurveyTemplateProps) => {
const searchParams = await props.searchParams;
const params = await props.params;
const session = await getServerSession(authOptions);
- const product = await getProductByEnvironmentId(params.environmentId);
+ const project = await getProjectByEnvironmentId(params.environmentId);
const organization = await getOrganizationByEnvironmentId(params.environmentId);
const t = await getTranslations();
if (!session) {
@@ -51,21 +51,21 @@ const Page = async (props: SurveyTemplateProps) => {
throw new Error(t("common.user_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
- const prefilledFilters = [product?.config.channel, product.config.industry, searchParams.role ?? null];
+ const prefilledFilters = [project?.config.channel, project.config.industry, searchParams.role ?? null];
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember, isBilling } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
- const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
+ const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
@@ -80,10 +80,10 @@ const Page = async (props: SurveyTemplateProps) => {
const surveyCount = await getSurveyCount(params.environmentId);
- const environments = await getEnvironments(product.id);
+ const environments = await getEnvironments(project.id);
const otherEnvironment = environments.find((e) => e.type !== environment.type)!;
- const currentProductChannel = product.config.channel ?? null;
+ const currentProjectChannel = project.config.channel ?? null;
const locale = await findMatchingLocale();
const CreateSurveyButton = () => {
return (
@@ -105,7 +105,7 @@ const Page = async (props: SurveyTemplateProps) => {
WEBAPP_URL={WEBAPP_URL}
userId={session.user.id}
surveysPerPage={SURVEYS_PER_PAGE}
- currentProductChannel={currentProductChannel}
+ currentProjectChannel={currentProjectChannel}
locale={locale}
/>
>
@@ -126,7 +126,7 @@ const Page = async (props: SurveyTemplateProps) => {
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/types/surveys.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/types/surveys.ts
index 6fd7b65658..b996248670 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/types/surveys.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/types/surveys.ts
@@ -26,9 +26,9 @@ export const ZSurvey = z.object({
export type TSurvey = z.infer
;
export const ZSurveyCopyFormValidation = z.object({
- products: z.array(
+ projects: z.array(
z.object({
- product: z.string(),
+ project: z.string(),
environments: z.array(z.string()),
})
),
diff --git a/apps/web/app/(redirects)/organizations/[organizationId]/route.ts b/apps/web/app/(redirects)/organizations/[organizationId]/route.ts
index d53215e76f..6d9620b42c 100644
--- a/apps/web/app/(redirects)/organizations/[organizationId]/route.ts
+++ b/apps/web/app/(redirects)/organizations/[organizationId]/route.ts
@@ -6,7 +6,7 @@ import { notFound } from "next/navigation";
import { getEnvironments } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
-import { getUserProducts } from "@formbricks/lib/product/service";
+import { getUserProjects } from "@formbricks/lib/project/service";
import { AuthenticationError, AuthorizationError } from "@formbricks/types/errors";
export const GET = async (_: Request, context: { params: Promise<{ organizationId: string }> }) => {
@@ -22,14 +22,14 @@ export const GET = async (_: Request, context: { params: Promise<{ organizationI
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organizationId);
const { isBilling } = getAccessFlags(currentUserMembership?.role);
- // redirect to first product's production environment
- const products = await getUserProducts(session.user.id, organizationId);
- if (products.length === 0) {
+ // redirect to first project's production environment
+ const projects = await getUserProjects(session.user.id, organizationId);
+ if (projects.length === 0) {
return redirect(`/organizations/${organizationId}/landing`);
}
- const firstProduct = products[0];
- const environments = await getEnvironments(firstProduct.id);
+ const firstProject = projects[0];
+ const environments = await getEnvironments(firstProject.id);
const prodEnvironment = environments.find((e) => e.type === "production");
if (!prodEnvironment) return notFound();
diff --git a/apps/web/app/(redirects)/products/[productId]/route.ts b/apps/web/app/(redirects)/projects/[projectId]/route.ts
similarity index 70%
rename from apps/web/app/(redirects)/products/[productId]/route.ts
rename to apps/web/app/(redirects)/projects/[projectId]/route.ts
index 761c40fe71..4c28c35fff 100644
--- a/apps/web/app/(redirects)/products/[productId]/route.ts
+++ b/apps/web/app/(redirects)/projects/[projectId]/route.ts
@@ -3,22 +3,22 @@ import { authOptions } from "@/modules/auth/lib/authOptions";
import { getServerSession } from "next-auth";
import { notFound, redirect } from "next/navigation";
import { getEnvironments } from "@formbricks/lib/environment/service";
-import { getProduct } from "@formbricks/lib/product/service";
+import { getProject } from "@formbricks/lib/project/service";
import { AuthenticationError, AuthorizationError } from "@formbricks/types/errors";
-export const GET = async (_: Request, context: { params: Promise<{ productId: string }> }) => {
+export const GET = async (_: Request, context: { params: Promise<{ projectId: string }> }) => {
const params = await context?.params;
- const productId = params.productId;
- if (!productId) return notFound();
+ const projectId = params.projectId;
+ if (!projectId) return notFound();
// check auth
const session = await getServerSession(authOptions);
if (!session) throw new AuthenticationError("Not authenticated");
- const product = await getProduct(productId);
- if (!product) return notFound();
- const hasAccess = await hasOrganizationAccess(session.user, product.organizationId);
+ const project = await getProject(projectId);
+ if (!project) return notFound();
+ const hasAccess = await hasOrganizationAccess(session.user, project.organizationId);
if (!hasAccess) throw new AuthorizationError("Unauthorized");
- // redirect to product's production environment
- const environments = await getEnvironments(product.id);
+ // redirect to project's production environment
+ const environments = await getEnvironments(project.id);
const prodEnvironment = environments.find((e) => e.type === "production");
if (!prodEnvironment) return notFound();
redirect(`/environments/${prodEnvironment.id}/`);
diff --git a/apps/web/app/api/(internal)/pipeline/route.ts b/apps/web/app/api/(internal)/pipeline/route.ts
index 1645ef76cb..c2a63432bb 100644
--- a/apps/web/app/api/(internal)/pipeline/route.ts
+++ b/apps/web/app/api/(internal)/pipeline/route.ts
@@ -117,7 +117,7 @@ export const POST = async (request: Request) => {
memberships: {
some: {
organization: {
- products: {
+ projects: {
some: {
environments: {
some: { id: environmentId },
@@ -141,9 +141,9 @@ export const POST = async (request: Request) => {
teamUsers: {
some: {
team: {
- productTeams: {
+ projectTeams: {
some: {
- product: {
+ project: {
environments: {
some: {
id: environmentId,
diff --git a/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts b/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts
index 9841b81955..79c0488a23 100644
--- a/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts
+++ b/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts
@@ -12,7 +12,7 @@ import {
export const getNotificationResponse = (
environment: TWeeklySummaryEnvironmentData,
- productName: string
+ projectName: string
): TWeeklySummaryNotificationResponse => {
const insights = {
totalCompletedResponses: 0,
@@ -73,7 +73,7 @@ export const getNotificationResponse = (
environmentId: environment.id,
currentDate: new Date(),
lastWeekDate,
- productName: productName,
+ projectName: projectName,
surveys,
insights,
};
diff --git a/apps/web/app/api/cron/weekly-summary/lib/product.ts b/apps/web/app/api/cron/weekly-summary/lib/project.ts
similarity index 92%
rename from apps/web/app/api/cron/weekly-summary/lib/product.ts
rename to apps/web/app/api/cron/weekly-summary/lib/project.ts
index 569bf43fb9..a7fa57e442 100644
--- a/apps/web/app/api/cron/weekly-summary/lib/product.ts
+++ b/apps/web/app/api/cron/weekly-summary/lib/project.ts
@@ -1,13 +1,13 @@
import { prisma } from "@formbricks/database";
-import { TWeeklySummaryProductData } from "@formbricks/types/weekly-summary";
+import { TWeeklySummaryProjectData } from "@formbricks/types/weekly-summary";
-export const getProductsByOrganizationId = async (
+export const getProjectsByOrganizationId = async (
organizationId: string
-): Promise => {
+): Promise => {
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
- return await prisma.product.findMany({
+ return await prisma.project.findMany({
where: {
organizationId: organizationId,
},
diff --git a/apps/web/app/api/cron/weekly-summary/route.ts b/apps/web/app/api/cron/weekly-summary/route.ts
index a56a13dccc..11ecdbe1cd 100644
--- a/apps/web/app/api/cron/weekly-summary/route.ts
+++ b/apps/web/app/api/cron/weekly-summary/route.ts
@@ -5,7 +5,7 @@ import { CRON_SECRET } from "@formbricks/lib/constants";
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { getNotificationResponse } from "./lib/notificationResponse";
import { getOrganizationIds } from "./lib/organization";
-import { getProductsByOrganizationId } from "./lib/product";
+import { getProjectsByOrganizationId } from "./lib/project";
const BATCH_SIZE = 500;
@@ -24,28 +24,28 @@ export const POST = async (): Promise => {
// Paginate through organizations
for (let i = 0; i < organizationIds.length; i += BATCH_SIZE) {
const batchedOrganizationIds = organizationIds.slice(i, i + BATCH_SIZE);
- // Fetch products for batched organizations asynchronously
- const batchedProductsPromises = batchedOrganizationIds.map((organizationId) =>
- getProductsByOrganizationId(organizationId)
+ // Fetch projects for batched organizations asynchronously
+ const batchedProjectsPromises = batchedOrganizationIds.map((organizationId) =>
+ getProjectsByOrganizationId(organizationId)
);
- const batchedProducts = await Promise.all(batchedProductsPromises);
- for (const products of batchedProducts) {
- for (const product of products) {
- const organizationMembers = product.organization.memberships;
+ const batchedProjects = await Promise.all(batchedProjectsPromises);
+ for (const projects of batchedProjects) {
+ for (const project of projects) {
+ const organizationMembers = project.organization.memberships;
const organizationMembersWithNotificationEnabled = organizationMembers.filter(
(member) =>
member.user.notificationSettings?.weeklySummary &&
- member.user.notificationSettings.weeklySummary[product.id]
+ member.user.notificationSettings.weeklySummary[project.id]
);
if (organizationMembersWithNotificationEnabled.length === 0) continue;
- const notificationResponse = getNotificationResponse(product.environments[0], product.name);
+ const notificationResponse = getNotificationResponse(project.environments[0], project.name);
if (notificationResponse.insights.numLiveSurvey === 0) {
for (const organizationMember of organizationMembersWithNotificationEnabled) {
- if (await hasUserEnvironmentAccess(organizationMember.user.id, product.environments[0].id)) {
+ if (await hasUserEnvironmentAccess(organizationMember.user.id, project.environments[0].id)) {
emailSendingPromises.push(
sendNoLiveSurveyNotificationEmail(
organizationMember.user.email,
@@ -59,7 +59,7 @@ export const POST = async (): Promise => {
}
for (const organizationMember of organizationMembersWithNotificationEnabled) {
- if (await hasUserEnvironmentAccess(organizationMember.user.id, product.environments[0].id)) {
+ if (await hasUserEnvironmentAccess(organizationMember.user.id, project.environments[0].id)) {
emailSendingPromises.push(
sendWeeklySummaryNotificationEmail(
organizationMember.user.email,
diff --git a/apps/web/app/api/v1/client/[environmentId]/app/sync/[userId]/route.ts b/apps/web/app/api/v1/client/[environmentId]/app/sync/[userId]/route.ts
index eb7dcecf60..0fb14684cf 100644
--- a/apps/web/app/api/v1/client/[environmentId]/app/sync/[userId]/route.ts
+++ b/apps/web/app/api/v1/client/[environmentId]/app/sync/[userId]/route.ts
@@ -15,7 +15,7 @@ import {
capturePosthogEnvironmentEvent,
sendPlanLimitsReachedEventToPosthogWeekly,
} from "@formbricks/lib/posthogServer";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
import { getSyncSurveys } from "@formbricks/lib/survey/service";
import { TJsAppStateSync, ZJsPeopleUserIdInput } from "@formbricks/types/js";
@@ -58,10 +58,10 @@ export const GET = async (
throw new Error("Environment does not exist");
}
- const product = await getProductByEnvironmentId(environmentId);
+ const project = await getProjectByEnvironmentId(environmentId);
- if (!product) {
- throw new Error("Product not found");
+ if (!project) {
+ throw new Error("Project not found");
}
if (!environment.appSetupCompleted) {
@@ -91,7 +91,13 @@ export const GET = async (
try {
await sendPlanLimitsReachedEventToPosthogWeekly(environmentId, {
plan: organization.billing.plan,
- limits: { monthly: { responses: monthlyResponseLimit, miu: null } },
+ limits: {
+ projects: null,
+ monthly: {
+ responses: monthlyResponseLimit,
+ miu: null,
+ },
+ },
});
} catch (error) {
console.error(`Error sending plan limits reached event to Posthog: ${error}`);
@@ -110,15 +116,15 @@ export const GET = async (
getActionClasses(environmentId),
]);
- if (!product) {
- throw new Error("Product not found");
+ if (!project) {
+ throw new Error("Project not found");
}
- const updatedProduct: any = {
- ...product,
- brandColor: product.styling.brandColor?.light ?? COLOR_DEFAULTS.brandColor,
- ...(product.styling.highlightBorderColor?.light && {
- highlightBorderColor: product.styling.highlightBorderColor.light,
+ const updatedProject: any = {
+ ...project,
+ brandColor: project.styling.brandColor?.light ?? COLOR_DEFAULTS.brandColor,
+ ...(project.styling.highlightBorderColor?.light && {
+ highlightBorderColor: project.styling.highlightBorderColor.light,
}),
};
const attributes = await getAttributes(person.id);
@@ -135,7 +141,7 @@ export const GET = async (
: [],
actionClasses,
language,
- product: updatedProduct,
+ project: updatedProject,
};
return responses.successResponse({ ...state }, true);
diff --git a/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts b/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts
index ca99392067..0c9bf2ff7c 100644
--- a/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts
+++ b/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts
@@ -14,8 +14,8 @@ import {
capturePosthogEnvironmentEvent,
sendPlanLimitsReachedEventToPosthogWeekly,
} from "@formbricks/lib/posthogServer";
-import { productCache } from "@formbricks/lib/product/cache";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { projectCache } from "@formbricks/lib/project/cache";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { surveyCache } from "@formbricks/lib/survey/cache";
import { getSurveys } from "@formbricks/lib/survey/service";
import { ResourceNotFoundError } from "@formbricks/types/errors";
@@ -33,10 +33,10 @@ export const getEnvironmentState = async (
cache(
async () => {
let revalidateEnvironment = false;
- const [environment, organization, product] = await Promise.all([
+ const [environment, organization, project] = await Promise.all([
getEnvironment(environmentId),
getOrganizationByEnvironmentId(environmentId),
- getProductByEnvironmentId(environmentId),
+ getProjectByEnvironmentId(environmentId),
]);
if (!environment) {
@@ -47,8 +47,8 @@ export const getEnvironmentState = async (
throw new ResourceNotFoundError("organization", null);
}
- if (!product) {
- throw new ResourceNotFoundError("product", null);
+ if (!project) {
+ throw new ResourceNotFoundError("project", null);
}
if (!environment.appSetupCompleted) {
@@ -81,8 +81,9 @@ export const getEnvironmentState = async (
await sendPlanLimitsReachedEventToPosthogWeekly(environmentId, {
plan: organization.billing.plan,
limits: {
+ projects: null,
monthly: {
- miu: organization.billing.limits.monthly.miu,
+ miu: null,
responses: organization.billing.limits.monthly.responses,
},
},
@@ -104,7 +105,7 @@ export const getEnvironmentState = async (
const state: TJsEnvironmentState["data"] = {
surveys: !isMonthlyResponsesLimitReached ? filteredSurveys : [],
actionClasses,
- product,
+ project: project,
};
return {
@@ -118,7 +119,7 @@ export const getEnvironmentState = async (
tags: [
environmentCache.tag.byId(environmentId),
organizationCache.tag.byEnvironmentId(environmentId),
- productCache.tag.byEnvironmentId(environmentId),
+ projectCache.tag.byEnvironmentId(environmentId),
surveyCache.tag.byEnvironmentId(environmentId),
actionClassCache.tag.byEnvironmentId(environmentId),
],
diff --git a/apps/web/app/api/v1/client/[environmentId]/environment/route.ts b/apps/web/app/api/v1/client/[environmentId]/environment/route.ts
index ba652cfb19..65da746d64 100644
--- a/apps/web/app/api/v1/client/[environmentId]/environment/route.ts
+++ b/apps/web/app/api/v1/client/[environmentId]/environment/route.ts
@@ -39,7 +39,7 @@ export const GET = async (
if (environmentState.revalidateEnvironment) {
environmentCache.revalidate({
id: inputValidation.data.environmentId,
- productId: environmentState.state.product.id,
+ projectId: environmentState.state.project.id,
});
}
diff --git a/apps/web/app/api/v1/management/me/route.ts b/apps/web/app/api/v1/management/me/route.ts
index 3cb18af099..a12c337a22 100644
--- a/apps/web/app/api/v1/management/me/route.ts
+++ b/apps/web/app/api/v1/management/me/route.ts
@@ -17,7 +17,7 @@ export const GET = async () => {
createdAt: true,
updatedAt: true,
type: true,
- product: {
+ project: {
select: {
id: true,
name: true,
diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx
index 91bed7907d..486f76e658 100644
--- a/apps/web/app/page.tsx
+++ b/apps/web/app/page.tsx
@@ -50,7 +50,7 @@ const Page = async () => {
if (!environmentId) {
console.error(t("common.failed_to_get_first_environment_of_user"));
if (isOwner || isManager) {
- return redirect(`/organizations/${userOrganizations[0].id}/products/new/mode`);
+ return redirect(`/organizations/${userOrganizations[0].id}/projects/new/mode`);
} else {
return redirect(`/organizations/${userOrganizations[0].id}/landing`);
}
diff --git a/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx b/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx
index 3369a72f54..1622b5e63c 100644
--- a/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx
+++ b/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx
@@ -13,7 +13,7 @@ import { ResponseQueue } from "@formbricks/lib/responseQueue";
import { SurveyState } from "@formbricks/lib/surveyState";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TJsFileUploadParams } from "@formbricks/types/js";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import {
TResponse,
TResponseData,
@@ -30,7 +30,7 @@ let setResponseData = (_: TResponseData) => {};
interface LinkSurveyProps {
survey: TSurvey;
- product: TProduct;
+ project: TProject;
userId?: string;
emailVerificationStatus?: string;
singleUseId?: string;
@@ -50,7 +50,7 @@ interface LinkSurveyProps {
export const LinkSurvey = ({
survey,
- product,
+ project,
userId,
emailVerificationStatus,
singleUseId,
@@ -173,7 +173,7 @@ export const LinkSurvey = ({
survey={survey}
isErrorComponent={true}
languageCode={languageCode}
- styling={product.styling}
+ styling={project.styling}
attributeClasses={attributeClasses}
locale={locale}
/>
@@ -185,7 +185,7 @@ export const LinkSurvey = ({
singleUseId={suId ?? ""}
survey={survey}
languageCode={languageCode}
- styling={product.styling}
+ styling={project.styling}
attributeClasses={attributeClasses}
locale={locale}
/>
@@ -193,23 +193,23 @@ export const LinkSurvey = ({
}
const determineStyling = () => {
- // allow style overwrite is disabled from the product
- if (!product.styling.allowStyleOverwrite) {
- return product.styling;
+ // allow style overwrite is disabled from the project
+ if (!project.styling.allowStyleOverwrite) {
+ return project.styling;
}
- // allow style overwrite is enabled from the product
- if (product.styling.allowStyleOverwrite) {
+ // allow style overwrite is enabled from the project
+ if (project.styling.allowStyleOverwrite) {
// survey style overwrite is disabled
if (!survey.styling?.overwriteThemeStyling) {
- return product.styling;
+ return project.styling;
}
// survey style overwrite is enabled
return survey.styling;
}
- return product.styling;
+ return project.styling;
};
const handleResetSurvey = () => {
@@ -219,7 +219,7 @@ export const LinkSurvey = ({
return (
void) => {
setIsError = f;
diff --git a/apps/web/app/s/[surveyId]/components/LinkSurveyWrapper.tsx b/apps/web/app/s/[surveyId]/components/LinkSurveyWrapper.tsx
index 0966b8b435..305e31cb32 100644
--- a/apps/web/app/s/[surveyId]/components/LinkSurveyWrapper.tsx
+++ b/apps/web/app/s/[surveyId]/components/LinkSurveyWrapper.tsx
@@ -5,16 +5,16 @@ import { MediaBackground } from "@/modules/ui/components/media-background";
import { ResetProgressButton } from "@/modules/ui/components/reset-progress-button";
import { type JSX, useState } from "react";
import { cn } from "@formbricks/lib/cn";
-import { TProduct, TProductStyling } from "@formbricks/types/product";
+import { TProject, TProjectStyling } from "@formbricks/types/project";
import { TSurvey, TSurveyStyling } from "@formbricks/types/surveys/types";
interface LinkSurveyWrapperProps {
children: JSX.Element;
- product: TProduct;
+ project: TProject;
survey: TSurvey;
isPreview: boolean;
isEmbed: boolean;
- determineStyling: () => TSurveyStyling | TProductStyling;
+ determineStyling: () => TSurveyStyling | TProjectStyling;
handleResetSurvey: () => void;
IMPRINT_URL?: string;
PRIVACY_URL?: string;
@@ -24,7 +24,7 @@ interface LinkSurveyWrapperProps {
export const LinkSurveyWrapper = ({
children,
- product,
+ project,
survey,
isPreview,
isEmbed,
@@ -60,9 +60,9 @@ export const LinkSurveyWrapper = ({
return (
-
+
- {!styling.isLogoHidden && product.logo?.url &&
}
+ {!styling.isLogoHidden && project.logo?.url &&
}
{isPreview && (
diff --git a/apps/web/app/s/[surveyId]/components/PinScreen.tsx b/apps/web/app/s/[surveyId]/components/PinScreen.tsx
index 144bcb41d6..fdd3803e54 100644
--- a/apps/web/app/s/[surveyId]/components/PinScreen.tsx
+++ b/apps/web/app/s/[surveyId]/components/PinScreen.tsx
@@ -9,13 +9,13 @@ import { useTranslations } from "next-intl";
import { useCallback, useEffect, useState } from "react";
import { cn } from "@formbricks/lib/cn";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import { TResponse } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys/types";
interface PinScreenProps {
surveyId: string;
- product: TProduct;
+ project: TProject;
userId?: string;
emailVerificationStatus?: string;
singleUseId?: string;
@@ -35,7 +35,7 @@ interface PinScreenProps {
export const PinScreen = (props: PinScreenProps) => {
const {
surveyId,
- product,
+ project,
webAppUrl,
emailVerificationStatus,
userId,
@@ -123,7 +123,7 @@ export const PinScreen = (props: PinScreenProps) => {
return (
{
}
}
- // get product and person
- const product = await getProductByEnvironmentId(survey.environmentId);
- if (!product) {
- throw new Error("Product not found");
+ // get project and person
+ const project = await getProjectByEnvironmentId(survey.environmentId);
+ if (!project) {
+ throw new Error("Project not found");
}
const attributeClasses = await getAttributeClasses(survey.environmentId);
@@ -165,7 +165,7 @@ const Page = async (props: LinkSurveyPageProps) => {
return (
{
return survey ? (
{
throw new Error(t("common.survey_not_found"));
}
const environmentId = survey.environmentId;
- const [environment, product, tags] = await Promise.all([
+ const [environment, project, tags] = await Promise.all([
getEnvironment(environmentId),
- getProductByEnvironmentId(environmentId),
+ getProjectByEnvironmentId(environmentId),
getTagsByEnvironmentId(environmentId),
]);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const totalResponseCount = await getResponseCountBySurveyId(surveyId);
diff --git a/apps/web/app/share/[sharingKey]/(analysis)/summary/page.tsx b/apps/web/app/share/[sharingKey]/(analysis)/summary/page.tsx
index c68c65c48a..0759855789 100644
--- a/apps/web/app/share/[sharingKey]/(analysis)/summary/page.tsx
+++ b/apps/web/app/share/[sharingKey]/(analysis)/summary/page.tsx
@@ -7,7 +7,7 @@ import { notFound } from "next/navigation";
import { getAttributeClasses } from "@formbricks/lib/attributeClass/service";
import { DEFAULT_LOCALE, WEBAPP_URL } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getResponseCountBySurveyId } from "@formbricks/lib/response/service";
import { getSurvey, getSurveyIdByResultShareKey } from "@formbricks/lib/survey/service";
@@ -25,18 +25,18 @@ const Page = async (props) => {
throw new Error(t("common.survey_not_found"));
}
const environmentId = survey.environmentId;
- const [environment, attributeClasses, product] = await Promise.all([
+ const [environment, attributeClasses, project] = await Promise.all([
getEnvironment(environmentId),
getAttributeClasses(environmentId),
- getProductByEnvironmentId(environmentId),
+ getProjectByEnvironmentId(environmentId),
]);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
const totalResponseCount = await getResponseCountBySurveyId(surveyId);
diff --git a/apps/web/lib/cache/team.ts b/apps/web/lib/cache/team.ts
index 6750dfd40d..b80cd7f1c7 100644
--- a/apps/web/lib/cache/team.ts
+++ b/apps/web/lib/cache/team.ts
@@ -3,7 +3,7 @@ import { revalidateTag } from "next/cache";
interface RevalidateProps {
id?: string;
userId?: string;
- productId?: string;
+ projectId?: string;
organizationId?: string;
}
@@ -12,8 +12,8 @@ export const teamCache = {
byId(id: string) {
return `team-${id}`;
},
- byProductId(productId: string) {
- return `product-teams-${productId}`;
+ byProjectId(projectId: string) {
+ return `project-teams-${projectId}`;
},
byUserId(userId: string) {
return `user-${userId}-teams`;
@@ -22,12 +22,12 @@ export const teamCache = {
return `organization-${organizationId}-teams`;
},
},
- revalidate({ id, productId, userId, organizationId }: RevalidateProps): void {
+ revalidate({ id, projectId, userId, organizationId }: RevalidateProps): void {
if (id) {
revalidateTag(this.tag.byId(id));
}
- if (productId) {
- revalidateTag(this.tag.byProductId(productId));
+ if (projectId) {
+ revalidateTag(this.tag.byProjectId(projectId));
}
if (userId) {
revalidateTag(this.tag.byUserId(userId));
diff --git a/apps/web/lib/utils/action-client-middleware.ts b/apps/web/lib/utils/action-client-middleware.ts
index 4c755b1187..588d114118 100644
--- a/apps/web/lib/utils/action-client-middleware.ts
+++ b/apps/web/lib/utils/action-client-middleware.ts
@@ -1,5 +1,5 @@
-import { getProductPermissionByUserId, getTeamRoleByTeamIdUserId } from "@/modules/ee/teams/lib/roles";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { getProjectPermissionByUserId, getTeamRoleByTeamIdUserId } from "@/modules/ee/teams/lib/roles";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { TTeamRole } from "@/modules/ee/teams/team-list/types/teams";
import { returnValidationErrors } from "next-safe-action";
import { ZodIssue, z } from "zod";
@@ -20,21 +20,21 @@ const formatErrors = (issues: ZodIssue[]): Record
export type TAccess =
| {
- type: "organization";
- schema?: z.ZodObject;
- data?: z.ZodObject["_output"];
- roles: TOrganizationRole[];
- }
+ type: "organization";
+ schema?: z.ZodObject;
+ data?: z.ZodObject["_output"];
+ roles: TOrganizationRole[];
+ }
| {
- type: "productTeam";
- minPermission?: TTeamPermission;
- productId: string;
- }
+ type: "projectTeam";
+ minPermission?: TTeamPermission;
+ projectId: string;
+ }
| {
- type: "team";
- minPermission?: TTeamRole;
- teamId: string;
- };
+ type: "team";
+ minPermission?: TTeamRole;
+ teamId: string;
+ };
const teamPermissionWeight = {
read: 1,
@@ -73,12 +73,12 @@ export const checkAuthorizationUpdated = async ({
return true;
}
} else {
- if (accessItem.type === "productTeam") {
- const productPermission = await getProductPermissionByUserId(userId, accessItem.productId);
+ if (accessItem.type === "projectTeam") {
+ const projectPermission = await getProjectPermissionByUserId(userId, accessItem.projectId);
if (
- !productPermission ||
+ !projectPermission ||
(accessItem.minPermission !== undefined &&
- teamPermissionWeight[productPermission] < teamPermissionWeight[accessItem.minPermission])
+ teamPermissionWeight[projectPermission] < teamPermissionWeight[accessItem.minPermission])
) {
continue;
}
diff --git a/apps/web/lib/utils/helper.ts b/apps/web/lib/utils/helper.ts
index 923bf0c53c..a41c57f32a 100644
--- a/apps/web/lib/utils/helper.ts
+++ b/apps/web/lib/utils/helper.ts
@@ -9,7 +9,7 @@ import {
getInvite,
getLanguage,
getPerson,
- getProduct,
+ getProject,
getResponse,
getResponseNote,
getSegment,
@@ -42,13 +42,13 @@ export const getFormattedErrorMessage = (result) => {
* GET organization ID from RESOURCE ID
*/
-export const getOrganizationIdFromProductId = async (productId: string) => {
- const product = await getProduct(productId);
- if (!product) {
- throw new ResourceNotFoundError("product", productId);
+export const getOrganizationIdFromProjectId = async (projectId: string) => {
+ const project = await getProject(projectId);
+ if (!project) {
+ throw new ResourceNotFoundError("project", projectId);
}
- return product.organizationId;
+ return project.organizationId;
};
export const getOrganizationIdFromEnvironmentId = async (environmentId: string) => {
@@ -57,7 +57,7 @@ export const getOrganizationIdFromEnvironmentId = async (environmentId: string)
throw new ResourceNotFoundError("environment", environmentId);
}
- return await getOrganizationIdFromProductId(environment.productId);
+ return await getOrganizationIdFromProjectId(environment.projectId);
};
export const getOrganizationIdFromSurveyId = async (surveyId: string) => {
@@ -174,7 +174,7 @@ export const getOrganizationIdFromLanguageId = async (languageId: string) => {
throw new ResourceNotFoundError("language", languageId);
}
- return await getOrganizationIdFromProductId(language.productId);
+ return await getOrganizationIdFromProjectId(language.projectId);
};
export const getOrganizationIdFromTeamId = async (teamId: string) => {
@@ -204,122 +204,122 @@ export const getOrganizationIdFromDocumentId = async (documentId: string) => {
return await getOrganizationIdFromEnvironmentId(document.environmentId);
};
-// product id helpers
-export const getProductIdFromEnvironmentId = async (environmentId: string) => {
+// project id helpers
+export const getProjectIdFromEnvironmentId = async (environmentId: string) => {
const environment = await getEnvironment(environmentId);
if (!environment) {
throw new ResourceNotFoundError("environment", environmentId);
}
- return environment.productId;
+ return environment.projectId;
};
-export const getProductIdFromSurveyId = async (surveyId: string) => {
+export const getProjectIdFromSurveyId = async (surveyId: string) => {
const survey = await getSurvey(surveyId);
if (!survey) {
throw new ResourceNotFoundError("survey", surveyId);
}
- return await getProductIdFromEnvironmentId(survey.environmentId);
+ return await getProjectIdFromEnvironmentId(survey.environmentId);
};
-export const getProductIdFromInsightId = async (insightId: string) => {
+export const getProjectIdFromInsightId = async (insightId: string) => {
const insight = await getInsight(insightId);
if (!insight) {
throw new ResourceNotFoundError("insight", insightId);
}
- return await getProductIdFromEnvironmentId(insight.environmentId);
+ return await getProjectIdFromEnvironmentId(insight.environmentId);
};
-export const getProductIdFromSegmentId = async (segmentId: string) => {
+export const getProjectIdFromSegmentId = async (segmentId: string) => {
const segment = await getSegment(segmentId);
if (!segment) {
throw new ResourceNotFoundError("segment", segmentId);
}
- return await getProductIdFromEnvironmentId(segment.environmentId);
+ return await getProjectIdFromEnvironmentId(segment.environmentId);
};
-export const getProductIdFromApiKeyId = async (apiKeyId: string) => {
+export const getProjectIdFromApiKeyId = async (apiKeyId: string) => {
const apiKey = await getApiKey(apiKeyId);
if (!apiKey) {
throw new ResourceNotFoundError("apiKey", apiKeyId);
}
- return await getProductIdFromEnvironmentId(apiKey.environmentId);
+ return await getProjectIdFromEnvironmentId(apiKey.environmentId);
};
-export const getProductIdFromActionClassId = async (actionClassId: string) => {
+export const getProjectIdFromActionClassId = async (actionClassId: string) => {
const actionClass = await getActionClass(actionClassId);
if (!actionClass) {
throw new ResourceNotFoundError("actionClass", actionClassId);
}
- return await getProductIdFromEnvironmentId(actionClass.environmentId);
+ return await getProjectIdFromEnvironmentId(actionClass.environmentId);
};
-export const getProductIdFromTagId = async (tagId: string) => {
+export const getProjectIdFromTagId = async (tagId: string) => {
const tag = await getTag(tagId);
if (!tag) {
throw new ResourceNotFoundError("tag", tagId);
}
- return await getProductIdFromEnvironmentId(tag.environmentId);
+ return await getProjectIdFromEnvironmentId(tag.environmentId);
};
-export const getProductIdFromLanguageId = async (languageId: string) => {
+export const getProjectIdFromLanguageId = async (languageId: string) => {
const language = await getLanguage(languageId);
if (!language) {
throw new ResourceNotFoundError("language", languageId);
}
- return language.productId;
+ return language.projectId;
};
-export const getProductIdFromResponseId = async (responseId: string) => {
+export const getProjectIdFromResponseId = async (responseId: string) => {
const response = await getResponse(responseId);
if (!response) {
throw new ResourceNotFoundError("response", responseId);
}
- return await getProductIdFromSurveyId(response.surveyId);
+ return await getProjectIdFromSurveyId(response.surveyId);
};
-export const getProductIdFromResponseNoteId = async (responseNoteId: string) => {
+export const getProjectIdFromResponseNoteId = async (responseNoteId: string) => {
const responseNote = await getResponseNote(responseNoteId);
if (!responseNote) {
throw new ResourceNotFoundError("responseNote", responseNoteId);
}
- return await getProductIdFromResponseId(responseNote.responseId);
+ return await getProjectIdFromResponseId(responseNote.responseId);
};
-export const getProductIdFromPersonId = async (personId: string) => {
+export const getProjectIdFromPersonId = async (personId: string) => {
const person = await getPerson(personId);
if (!person) {
throw new ResourceNotFoundError("person", personId);
}
- return await getProductIdFromEnvironmentId(person.environmentId);
+ return await getProjectIdFromEnvironmentId(person.environmentId);
};
-export const getProductIdFromDocumentId = async (documentId: string) => {
+export const getProjectIdFromDocumentId = async (documentId: string) => {
const document = await getDocument(documentId);
if (!document) {
throw new ResourceNotFoundError("document", documentId);
}
- return await getProductIdFromEnvironmentId(document.environmentId);
+ return await getProjectIdFromEnvironmentId(document.environmentId);
};
-export const getProductIdFromIntegrationId = async (integrationId: string) => {
+export const getProjectIdFromIntegrationId = async (integrationId: string) => {
const integration = await getIntegration(integrationId);
if (!integration) {
throw new ResourceNotFoundError("integration", integrationId);
}
- return await getProductIdFromEnvironmentId(integration.environmentId);
+ return await getProjectIdFromEnvironmentId(integration.environmentId);
};
// environment id helpers
diff --git a/apps/web/lib/utils/services.ts b/apps/web/lib/utils/services.ts
index e2cf91f466..1200c50ce1 100644
--- a/apps/web/lib/utils/services.ts
+++ b/apps/web/lib/utils/services.ts
@@ -12,7 +12,7 @@ import { environmentCache } from "@formbricks/lib/environment/cache";
import { integrationCache } from "@formbricks/lib/integration/cache";
import { inviteCache } from "@formbricks/lib/invite/cache";
import { personCache } from "@formbricks/lib/person/cache";
-import { productCache } from "@formbricks/lib/product/cache";
+import { projectCache } from "@formbricks/lib/project/cache";
import { responseCache } from "@formbricks/lib/response/cache";
import { responseNoteCache } from "@formbricks/lib/responseNote/cache";
import { segmentCache } from "@formbricks/lib/segment/cache";
@@ -119,7 +119,7 @@ export const getAttributeClass = reactCache(
);
export const getEnvironment = reactCache(
- async (environmentId: string): Promise<{ productId: string } | null> =>
+ async (environmentId: string): Promise<{ projectId: string } | null> =>
cache(
async () => {
validateInputs([environmentId, ZId]);
@@ -130,7 +130,7 @@ export const getEnvironment = reactCache(
id: environmentId,
},
select: {
- productId: true,
+ projectId: true,
},
});
return environment;
@@ -210,13 +210,13 @@ export const getInvite = reactCache(
)()
);
-export const getLanguage = async (languageId: string): Promise<{ productId: string }> => {
+export const getLanguage = async (languageId: string): Promise<{ projectId: string }> => {
try {
validateInputs([languageId, ZId]);
const language = await prisma.language.findFirst({
where: { id: languageId },
- select: { productId: true },
+ select: { projectId: true },
});
if (!language) {
@@ -261,20 +261,20 @@ export const getPerson = reactCache(
)()
);
-export const getProduct = reactCache(
- async (productId: string): Promise<{ organizationId: string } | null> =>
+export const getProject = reactCache(
+ async (projectId: string): Promise<{ organizationId: string } | null> =>
cache(
async () => {
- let productPrisma;
+ let projectPrisma;
try {
- productPrisma = await prisma.product.findUnique({
+ projectPrisma = await prisma.project.findUnique({
where: {
- id: productId,
+ id: projectId,
},
select: { organizationId: true },
});
- return productPrisma;
+ return projectPrisma;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
@@ -282,9 +282,9 @@ export const getProduct = reactCache(
throw error;
}
},
- [`utils-getProduct-${productId}`],
+ [`utils-getProject-${projectId}`],
{
- tags: [productCache.tag.byId(productId)],
+ tags: [projectCache.tag.byId(projectId)],
}
)()
);
@@ -561,17 +561,17 @@ export const getDocument = reactCache(
)()
);
-export const isProductPartOfOrganization = async (
+export const isProjectPartOfOrganization = async (
organizationId: string,
- productId: string
+ projectId: string
): Promise => {
try {
- const product = await getProduct(productId);
- if (!product) {
- throw new ResourceNotFoundError("Product", productId);
+ const project = await getProject(projectId);
+ if (!project) {
+ throw new ResourceNotFoundError("Project", projectId);
}
- return product.organizationId === organizationId;
+ return project.organizationId === organizationId;
} catch (error) {
throw error;
}
diff --git a/apps/web/modules/analysis/components/SingleResponseCard/actions.ts b/apps/web/modules/analysis/components/SingleResponseCard/actions.ts
index 5213d934bb..8e953980e1 100644
--- a/apps/web/modules/analysis/components/SingleResponseCard/actions.ts
+++ b/apps/web/modules/analysis/components/SingleResponseCard/actions.ts
@@ -7,9 +7,9 @@ import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromResponseId,
getOrganizationIdFromResponseNoteId,
- getProductIdFromEnvironmentId,
- getProductIdFromResponseId,
- getProductIdFromResponseNoteId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromResponseId,
+ getProjectIdFromResponseNoteId,
} from "@/lib/utils/helper";
import { getTag } from "@/lib/utils/services";
import { z } from "zod";
@@ -40,8 +40,8 @@ export const createTagAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
minPermission: "readWrite",
},
],
@@ -78,8 +78,8 @@ export const createTagToResponseAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromEnvironmentId(responseEnvironmentId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromEnvironmentId(responseEnvironmentId),
minPermission: "readWrite",
},
],
@@ -116,8 +116,8 @@ export const deleteTagOnResponseAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromEnvironmentId(responseEnvironmentId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromEnvironmentId(responseEnvironmentId),
minPermission: "readWrite",
},
],
@@ -142,8 +142,8 @@ export const deleteResponseAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromResponseId(parsedInput.responseId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromResponseId(parsedInput.responseId),
minPermission: "readWrite",
},
],
@@ -169,8 +169,8 @@ export const updateResponseNoteAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromResponseNoteId(parsedInput.responseNoteId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromResponseNoteId(parsedInput.responseNoteId),
minPermission: "readWrite",
},
],
@@ -195,8 +195,8 @@ export const resolveResponseNoteAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromResponseNoteId(parsedInput.responseNoteId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromResponseNoteId(parsedInput.responseNoteId),
minPermission: "readWrite",
},
],
@@ -222,8 +222,8 @@ export const createResponseNoteAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromResponseId(parsedInput.responseId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromResponseId(parsedInput.responseId),
minPermission: "readWrite",
},
],
@@ -248,9 +248,9 @@ export const getResponseAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromResponseId(parsedInput.responseId),
+ projectId: await getProjectIdFromResponseId(parsedInput.responseId),
},
],
});
diff --git a/apps/web/modules/analysis/components/SingleResponseCard/components/ResponseTagsWrapper.tsx b/apps/web/modules/analysis/components/SingleResponseCard/components/ResponseTagsWrapper.tsx
index 03e667f303..74917c6ab6 100644
--- a/apps/web/modules/analysis/components/SingleResponseCard/components/ResponseTagsWrapper.tsx
+++ b/apps/web/modules/analysis/components/SingleResponseCard/components/ResponseTagsWrapper.tsx
@@ -66,7 +66,7 @@ export const ResponseTagsWrapper: React.FC = ({
size="sm"
className="cursor-pointer p-0"
onClick={() => {
- router.push(`/environments/${environmentId}/product/tags`);
+ router.push(`/environments/${environmentId}/project/tags`);
}}>
diff --git a/apps/web/modules/ee/advanced-targeting/lib/actions.ts b/apps/web/modules/ee/advanced-targeting/lib/actions.ts
index ffd1516b72..22b85c3e6b 100644
--- a/apps/web/modules/ee/advanced-targeting/lib/actions.ts
+++ b/apps/web/modules/ee/advanced-targeting/lib/actions.ts
@@ -8,9 +8,9 @@ import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromSegmentId,
getOrganizationIdFromSurveyId,
- getProductIdFromEnvironmentId,
- getProductIdFromSegmentId,
- getProductIdFromSurveyId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromSegmentId,
+ getProjectIdFromSurveyId,
} from "@/lib/utils/helper";
import { getAdvancedTargetingPermission } from "@/modules/ee/license-check/lib/utils";
import { z } from "zod";
@@ -63,9 +63,9 @@ export const createSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
@@ -104,9 +104,9 @@ export const updateSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSegmentId(parsedInput.segmentId),
+ projectId: await getProjectIdFromSegmentId(parsedInput.segmentId),
},
],
});
@@ -153,9 +153,9 @@ export const loadNewSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(surveyEnvironmentId),
+ projectId: await getProjectIdFromEnvironmentId(surveyEnvironmentId),
},
],
});
@@ -191,9 +191,9 @@ export const cloneSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromEnvironmentId(surveyEnvironmentId),
+ projectId: await getProjectIdFromEnvironmentId(surveyEnvironmentId),
},
],
});
@@ -221,9 +221,9 @@ export const deleteSegmentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSegmentId(parsedInput.segmentId),
+ projectId: await getProjectIdFromSegmentId(parsedInput.segmentId),
},
],
});
@@ -251,9 +251,9 @@ export const resetSegmentFiltersAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
diff --git a/apps/web/modules/ee/billing/api/lib/constants.ts b/apps/web/modules/ee/billing/api/lib/constants.ts
index dfb3d027cd..0e23e1c235 100644
--- a/apps/web/modules/ee/billing/api/lib/constants.ts
+++ b/apps/web/modules/ee/billing/api/lib/constants.ts
@@ -9,6 +9,7 @@ export const CLOUD_PRICING_DATA = {
mainFeatures: [
"environments.settings.billing.unlimited_surveys",
"environments.settings.billing.unlimited_team_members",
+ "environments.settings.billing.3_projects",
"environments.settings.billing.1500_monthly_responses",
"environments.settings.billing.2000_monthly_identified_users",
"environments.settings.billing.website_surveys",
@@ -33,6 +34,7 @@ export const CLOUD_PRICING_DATA = {
"environments.settings.billing.unlimited_surveys",
"environments.settings.billing.remove_branding",
"environments.settings.billing.email_support",
+ "environments.settings.billing.3_projects",
"environments.settings.billing.5000_monthly_responses",
"environments.settings.billing.7500_monthly_identified_users",
],
@@ -50,6 +52,7 @@ export const CLOUD_PRICING_DATA = {
"environments.settings.billing.multi_language_surveys",
"environments.settings.billing.advanced_targeting",
"environments.settings.billing.priority_support",
+ "environments.settings.billing.5_projects",
"environments.settings.billing.10000_monthly_responses",
"environments.settings.billing.30000_monthly_identified_users",
],
@@ -66,6 +69,7 @@ export const CLOUD_PRICING_DATA = {
},
mainFeatures: [
"environments.settings.billing.everything_in_scale",
+ "environments.settings.billing.custom_project_limit",
"environments.settings.billing.custom_miu_limit",
"environments.settings.billing.premium_support_with_slas",
"environments.settings.billing.uptime_sla_99",
diff --git a/apps/web/modules/ee/billing/api/lib/subscription-created-or-updated.ts b/apps/web/modules/ee/billing/api/lib/subscription-created-or-updated.ts
index e2db72d289..514b6b5564 100644
--- a/apps/web/modules/ee/billing/api/lib/subscription-created-or-updated.ts
+++ b/apps/web/modules/ee/billing/api/lib/subscription-created-or-updated.ts
@@ -1,5 +1,5 @@
import Stripe from "stripe";
-import { PRODUCT_FEATURE_KEYS, STRIPE_API_VERSION } from "@formbricks/lib/constants";
+import { PROJECT_FEATURE_KEYS, STRIPE_API_VERSION } from "@formbricks/lib/constants";
import { env } from "@formbricks/lib/env";
import { getOrganization, updateOrganization } from "@formbricks/lib/organization/service";
import { ResourceNotFoundError } from "@formbricks/types/errors";
@@ -53,6 +53,7 @@ export const handleSubscriptionCreatedOrUpdated = async (event: Stripe.Event) =>
let responses: number | null = null;
let miu: number | null = null;
+ let projects: number | null = null;
if (product.metadata.responses === "unlimited") {
responses = null;
@@ -72,23 +73,32 @@ export const handleSubscriptionCreatedOrUpdated = async (event: Stripe.Event) =>
throw new Error("Invalid miu metadata in product");
}
+ if (product.metadata.projects === "unlimited") {
+ projects = null;
+ } else if (parseInt(product.metadata.projects) > 0) {
+ projects = parseInt(product.metadata.projects);
+ } else {
+ console.error("Invalid projects metadata in product: ", product.metadata.projects);
+ throw new Error("Invalid projects metadata in product");
+ }
+
const plan = ZOrganizationBillingPlan.parse(product.metadata.plan);
switch (plan) {
- case PRODUCT_FEATURE_KEYS.FREE:
- updatedBillingPlan = PRODUCT_FEATURE_KEYS.STARTUP;
+ case PROJECT_FEATURE_KEYS.FREE:
+ updatedBillingPlan = PROJECT_FEATURE_KEYS.STARTUP;
break;
- case PRODUCT_FEATURE_KEYS.STARTUP:
- updatedBillingPlan = PRODUCT_FEATURE_KEYS.STARTUP;
+ case PROJECT_FEATURE_KEYS.STARTUP:
+ updatedBillingPlan = PROJECT_FEATURE_KEYS.STARTUP;
break;
- case PRODUCT_FEATURE_KEYS.SCALE:
- updatedBillingPlan = PRODUCT_FEATURE_KEYS.SCALE;
+ case PROJECT_FEATURE_KEYS.SCALE:
+ updatedBillingPlan = PROJECT_FEATURE_KEYS.SCALE;
break;
- case PRODUCT_FEATURE_KEYS.ENTERPRISE:
- updatedBillingPlan = PRODUCT_FEATURE_KEYS.ENTERPRISE;
+ case PROJECT_FEATURE_KEYS.ENTERPRISE:
+ updatedBillingPlan = PROJECT_FEATURE_KEYS.ENTERPRISE;
break;
}
@@ -99,6 +109,7 @@ export const handleSubscriptionCreatedOrUpdated = async (event: Stripe.Event) =>
plan: updatedBillingPlan,
period,
limits: {
+ projects,
monthly: {
responses,
miu,
diff --git a/apps/web/modules/ee/billing/api/lib/subscription-deleted.ts b/apps/web/modules/ee/billing/api/lib/subscription-deleted.ts
index 7f816ad97c..d81299c4be 100644
--- a/apps/web/modules/ee/billing/api/lib/subscription-deleted.ts
+++ b/apps/web/modules/ee/billing/api/lib/subscription-deleted.ts
@@ -1,5 +1,5 @@
import Stripe from "stripe";
-import { BILLING_LIMITS, PRODUCT_FEATURE_KEYS } from "@formbricks/lib/constants";
+import { BILLING_LIMITS, PROJECT_FEATURE_KEYS } from "@formbricks/lib/constants";
import { getOrganization, updateOrganization } from "@formbricks/lib/organization/service";
import { ResourceNotFoundError } from "@formbricks/types/errors";
@@ -17,8 +17,9 @@ export const handleSubscriptionDeleted = async (event: Stripe.Event) => {
await updateOrganization(organizationId, {
billing: {
...organization.billing,
- plan: PRODUCT_FEATURE_KEYS.FREE,
+ plan: PROJECT_FEATURE_KEYS.FREE,
limits: {
+ projects: BILLING_LIMITS.FREE.PROJECTS,
monthly: {
responses: BILLING_LIMITS.FREE.RESPONSES,
miu: BILLING_LIMITS.FREE.MIU,
diff --git a/apps/web/modules/ee/billing/components/pricing-card.tsx b/apps/web/modules/ee/billing/components/pricing-card.tsx
index 3b95a50d8d..ff0a72cf33 100644
--- a/apps/web/modules/ee/billing/components/pricing-card.tsx
+++ b/apps/web/modules/ee/billing/components/pricing-card.tsx
@@ -23,7 +23,7 @@ interface PricingCardProps {
organization: TOrganization;
onUpgrade: () => Promise;
onManageSubscription: () => Promise;
- productFeatureKeys: {
+ projectFeatureKeys: {
FREE: string;
STARTUP: string;
SCALE: string;
@@ -37,20 +37,20 @@ export const PricingCard = ({
onUpgrade,
onManageSubscription,
organization,
- productFeatureKeys,
+ projectFeatureKeys,
}: PricingCardProps) => {
const t = useTranslations();
const [loading, setLoading] = useState(false);
const [upgradeModalOpen, setUpgradeModalOpen] = useState(false);
const isCurrentPlan = useMemo(() => {
- if (organization.billing.plan === productFeatureKeys.FREE && plan.id === productFeatureKeys.FREE) {
+ if (organization.billing.plan === projectFeatureKeys.FREE && plan.id === projectFeatureKeys.FREE) {
return true;
}
if (
- organization.billing.plan === productFeatureKeys.ENTERPRISE &&
- plan.id === productFeatureKeys.ENTERPRISE
+ organization.billing.plan === projectFeatureKeys.ENTERPRISE &&
+ plan.id === projectFeatureKeys.ENTERPRISE
) {
return true;
}
@@ -61,8 +61,8 @@ export const PricingCard = ({
organization.billing.plan,
plan.id,
planPeriod,
- productFeatureKeys.ENTERPRISE,
- productFeatureKeys.FREE,
+ projectFeatureKeys.ENTERPRISE,
+ projectFeatureKeys.FREE,
]);
const CTAButton = useMemo(() => {
@@ -70,8 +70,8 @@ export const PricingCard = ({
return null;
}
- if (plan.id !== productFeatureKeys.ENTERPRISE && plan.id !== productFeatureKeys.FREE) {
- if (organization.billing.plan === productFeatureKeys.FREE) {
+ if (plan.id !== projectFeatureKeys.ENTERPRISE && plan.id !== projectFeatureKeys.FREE) {
+ if (organization.billing.plan === projectFeatureKeys.FREE) {
return (
- {plan.id !== productFeatureKeys.ENTERPRISE
+ {plan.id !== projectFeatureKeys.ENTERPRISE
? planPeriod === "monthly"
? plan.price.monthly
: plan.price.yearly
@@ -156,7 +156,7 @@ export const PricingCard = ({
{CTAButton}
- {plan.id !== productFeatureKeys.FREE && isCurrentPlan && (
+ {plan.id !== projectFeatureKeys.FREE && isCurrentPlan && (
)}
- {organization.billing.plan !== plan.id && plan.id === productFeatureKeys.ENTERPRISE && (
+ {organization.billing.plan !== plan.id && plan.id === projectFeatureKeys.ENTERPRISE && (
onUpgrade()} className="flex justify-center">
{t("environments.settings.billing.contact_us")}
diff --git a/apps/web/modules/ee/billing/components/pricing-table.tsx b/apps/web/modules/ee/billing/components/pricing-table.tsx
index ce5c0d958c..9f0e929b07 100644
--- a/apps/web/modules/ee/billing/components/pricing-table.tsx
+++ b/apps/web/modules/ee/billing/components/pricing-table.tsx
@@ -19,13 +19,14 @@ interface PricingTableProps {
environmentId: string;
peopleCount: number;
responseCount: number;
+ projectCount: number;
stripePriceLookupKeys: {
STARTUP_MONTHLY: string;
STARTUP_YEARLY: string;
SCALE_MONTHLY: string;
SCALE_YEARLY: string;
};
- productFeatureKeys: {
+ projectFeatureKeys: {
FREE: string;
STARTUP: string;
SCALE: string;
@@ -38,8 +39,9 @@ export const PricingTable = ({
environmentId,
organization,
peopleCount,
- productFeatureKeys,
+ projectFeatureKeys,
responseCount,
+ projectCount,
stripePriceLookupKeys,
hasBillingRights,
}: PricingTableProps) => {
@@ -137,6 +139,8 @@ export const PricingTable = ({
organization.billing.plan === "enterprise" && organization.billing.limits.monthly.responses === null;
const peopleUnlimitedCheck =
organization.billing.plan === "enterprise" && organization.billing.limits.monthly.miu === null;
+ const projectsUnlimitedCheck =
+ organization.billing.plan === "enterprise" && organization.billing.limits.projects === null;
return (
@@ -197,7 +201,7 @@ export const PricingTable = ({
@@ -205,7 +209,7 @@ export const PricingTable = ({
{organization.billing.limits.monthly.miu && (
)}
- {peopleUnlimitedCheck &&
}
+ {peopleUnlimitedCheck && (
+
+ )}
+
+
+
+
{t("common.projects")}
+ {organization.billing.limits.projects && (
+
+ )}
+
+ {projectsUnlimitedCheck && (
+
+ )}
@@ -254,7 +285,7 @@ export const PricingTable = ({
await onUpgrade(plan.id);
}}
organization={organization}
- productFeatureKeys={productFeatureKeys}
+ projectFeatureKeys={projectFeatureKeys}
onManageSubscription={openCustomerPortal}
/>
))}
diff --git a/apps/web/modules/ee/billing/page.tsx b/apps/web/modules/ee/billing/page.tsx
index 9a1f05fe29..fcc01c1c3d 100644
--- a/apps/web/modules/ee/billing/page.tsx
+++ b/apps/web/modules/ee/billing/page.tsx
@@ -7,7 +7,7 @@ import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { notFound } from "next/navigation";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
-import { PRODUCT_FEATURE_KEYS, STRIPE_PRICE_LOOKUP_KEYS } from "@formbricks/lib/constants";
+import { PROJECT_FEATURE_KEYS, STRIPE_PRICE_LOOKUP_KEYS } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import {
@@ -15,6 +15,7 @@ import {
getMonthlyOrganizationResponseCount,
getOrganizationByEnvironmentId,
} from "@formbricks/lib/organization/service";
+import { getOrganizationProjectsCount } from "@formbricks/lib/project/service";
import { PricingTable } from "./components/pricing-table";
export const PricingPage = async (props) => {
@@ -35,9 +36,10 @@ export const PricingPage = async (props) => {
throw new Error(t("common.not_authorized"));
}
- const [peopleCount, responseCount] = await Promise.all([
+ const [peopleCount, responseCount, projectCount] = await Promise.all([
getMonthlyActiveOrganizationPeopleCount(organization.id),
getMonthlyOrganizationResponseCount(organization.id),
+ getOrganizationProjectsCount(organization.id),
]);
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
@@ -63,8 +65,9 @@ export const PricingPage = async (props) => {
environmentId={params.environmentId}
peopleCount={peopleCount}
responseCount={responseCount}
+ projectCount={projectCount}
stripePriceLookupKeys={STRIPE_PRICE_LOOKUP_KEYS}
- productFeatureKeys={PRODUCT_FEATURE_KEYS}
+ projectFeatureKeys={PROJECT_FEATURE_KEYS}
hasBillingRights={hasBillingRights}
/>
diff --git a/apps/web/modules/ee/insights/actions.ts b/apps/web/modules/ee/insights/actions.ts
index 07763016bc..154bbf1ce8 100644
--- a/apps/web/modules/ee/insights/actions.ts
+++ b/apps/web/modules/ee/insights/actions.ts
@@ -3,7 +3,7 @@
import { generateInsightsForSurvey } from "@/app/api/(internal)/insights/lib/utils";
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromSurveyId, getProductIdFromSurveyId } from "@/lib/utils/helper";
+import { getOrganizationIdFromSurveyId, getProjectIdFromSurveyId } from "@/lib/utils/helper";
import { getIsAIEnabled, getIsOrganizationAIReady } from "@/modules/ee/license-check/lib/utils";
import { z } from "zod";
import { getOrganization, updateOrganization } from "@formbricks/lib/organization/service";
@@ -45,8 +45,8 @@ export const generateInsightsForSurveyAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
minPermission: "readWrite",
},
],
diff --git a/apps/web/modules/ee/insights/components/insight-sheet/actions.ts b/apps/web/modules/ee/insights/components/insight-sheet/actions.ts
index c44ba27bb0..96f5d47167 100644
--- a/apps/web/modules/ee/insights/components/insight-sheet/actions.ts
+++ b/apps/web/modules/ee/insights/components/insight-sheet/actions.ts
@@ -8,9 +8,9 @@ import {
getOrganizationIdFromDocumentId,
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromInsightId,
- getProductIdFromDocumentId,
- getProductIdFromEnvironmentId,
- getProductIdFromInsightId,
+ getProjectIdFromDocumentId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromInsightId,
} from "@/lib/utils/helper";
import { checkAIPermission } from "@/modules/ee/insights/actions";
import {
@@ -52,9 +52,9 @@ export const getDocumentsByInsightIdSurveyIdQuestionIdAction = authenticatedActi
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromEnvironmentId(surveyEnvironmentId),
+ projectId: await getProjectIdFromEnvironmentId(surveyEnvironmentId),
},
],
});
@@ -90,9 +90,9 @@ export const getDocumentsByInsightIdAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromInsightId(parsedInput.insightId),
+ projectId: await getProjectIdFromInsightId(parsedInput.insightId),
},
],
});
@@ -129,9 +129,9 @@ export const updateDocumentAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "readWrite",
- productId: await getProductIdFromDocumentId(parsedInput.documentId),
+ projectId: await getProjectIdFromDocumentId(parsedInput.documentId),
},
],
});
diff --git a/apps/web/modules/ee/insights/experience/actions.ts b/apps/web/modules/ee/insights/experience/actions.ts
index 921af8cad8..f14304ef73 100644
--- a/apps/web/modules/ee/insights/experience/actions.ts
+++ b/apps/web/modules/ee/insights/experience/actions.ts
@@ -5,8 +5,8 @@ import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"
import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromInsightId,
- getProductIdFromEnvironmentId,
- getProductIdFromInsightId,
+ getProjectIdFromEnvironmentId,
+ getProjectIdFromInsightId,
} from "@/lib/utils/helper";
import { checkAIPermission } from "@/modules/ee/insights/actions";
import { z } from "zod";
@@ -35,9 +35,9 @@ export const getEnvironmentInsightsAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
@@ -70,9 +70,9 @@ export const getStatsAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
+ type: "projectTeam",
minPermission: "read",
- productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
@@ -101,8 +101,8 @@ export const updateInsightAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromInsightId(parsedInput.insightId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromInsightId(parsedInput.insightId),
minPermission: "readWrite",
},
],
diff --git a/apps/web/modules/ee/insights/experience/components/dashboard.tsx b/apps/web/modules/ee/insights/experience/components/dashboard.tsx
index 5ccc45a68b..dfc14479ad 100644
--- a/apps/web/modules/ee/insights/experience/components/dashboard.tsx
+++ b/apps/web/modules/ee/insights/experience/components/dashboard.tsx
@@ -9,13 +9,13 @@ import { Tabs, TabsList, TabsTrigger } from "@/modules/ui/components/tabs";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { TEnvironment } from "@formbricks/types/environment";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import { TUser, TUserLocale } from "@formbricks/types/user";
interface DashboardProps {
user: TUser;
environment: TEnvironment;
- product: TProduct;
+ project: TProject;
insightsPerPage: number;
documentsPerPage: number;
locale: TUserLocale;
@@ -23,7 +23,7 @@ interface DashboardProps {
export const Dashboard = ({
environment,
- product,
+ project,
user,
insightsPerPage,
documentsPerPage,
@@ -65,7 +65,7 @@ export const Dashboard = ({
- {t("environments.experience.insights_for_product", { productName })}
+ {t("environments.experience.insights_for_project", { projectName })}
{t("environments.experience.insights_description")}
diff --git a/apps/web/modules/ee/insights/experience/components/templates-card.tsx b/apps/web/modules/ee/insights/experience/components/templates-card.tsx
index 391ad42712..fa7d6c4ad4 100644
--- a/apps/web/modules/ee/insights/experience/components/templates-card.tsx
+++ b/apps/web/modules/ee/insights/experience/components/templates-card.tsx
@@ -4,18 +4,18 @@ import { TemplateList } from "@/modules/surveys/components/TemplateList";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/modules/ui/components/card";
import { useTranslations } from "next-intl";
import { TEnvironment } from "@formbricks/types/environment";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
import { TTemplateFilter } from "@formbricks/types/templates";
import { TUser } from "@formbricks/types/user";
interface TemplatesCardProps {
environment: TEnvironment;
- product: TProduct;
+ project: TProject;
user: TUser;
prefilledFilters: TTemplateFilter[];
}
-export const TemplatesCard = ({ environment, product, user, prefilledFilters }: TemplatesCardProps) => {
+export const TemplatesCard = ({ environment, project, user, prefilledFilters }: TemplatesCardProps) => {
const t = useTranslations();
return (
@@ -26,7 +26,7 @@ export const TemplatesCard = ({ environment, product, user, prefilledFilters }:
{
throw new Error("User not found");
}
- const [environment, product, organization] = await Promise.all([
+ const [environment, project, organization] = await Promise.all([
getEnvironment(params.environmentId),
- getProductByEnvironmentId(params.environmentId),
+ getProjectByEnvironmentId(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
]);
@@ -36,8 +36,8 @@ export const ExperiencePage = async (props) => {
throw new Error("Environment not found");
}
- if (!product) {
- throw new Error("Product not found");
+ if (!project) {
+ throw new Error("Project not found");
}
if (!organization) {
@@ -62,7 +62,7 @@ export const ExperiencePage = async (props) => {
{
+export const LanguagesLoading = () => {
const t = useTranslations();
return (
-
+
+ title={t("environments.project.languages.multi_language_surveys")}
+ description={t("environments.project.languages.multi_language_surveys_description")}>
{[...Array(3)].map((_, idx) => (
@@ -31,5 +31,3 @@ const Loading = () => {
);
};
-
-export default Loading;
diff --git a/apps/web/app/(app)/environments/[environmentId]/product/languages/page.tsx b/apps/web/modules/ee/languages/page.tsx
similarity index 71%
rename from apps/web/app/(app)/environments/[environmentId]/product/languages/page.tsx
rename to apps/web/modules/ee/languages/page.tsx
index a251607890..79d6b421bf 100644
--- a/apps/web/app/(app)/environments/[environmentId]/product/languages/page.tsx
+++ b/apps/web/modules/ee/languages/page.tsx
@@ -1,4 +1,3 @@
-import { ProductConfigNavigation } from "@/app/(app)/environments/[environmentId]/product/components/ProductConfigNavigation";
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { authOptions } from "@/modules/auth/lib/authOptions";
import {
@@ -6,8 +5,9 @@ import {
getRoleManagementPermission,
} from "@/modules/ee/license-check/lib/utils";
import { EditLanguage } from "@/modules/ee/multi-language-surveys/components/edit-language";
-import { getProductPermissionByUserId } from "@/modules/ee/teams/lib/roles";
+import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
+import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getServerSession } from "next-auth";
@@ -16,19 +16,19 @@ import { notFound } from "next/navigation";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganization } from "@formbricks/lib/organization/service";
-import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
+import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getUser } from "@formbricks/lib/user/service";
-const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
+export const LanguagesPage = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
const t = await getTranslations();
- const product = await getProductByEnvironmentId(params.environmentId);
+ const project = await getProjectByEnvironmentId(params.environmentId);
- if (!product) {
- throw new Error(t("common.product_not_found"));
+ if (!project) {
+ throw new Error(t("common.project_not_found"));
}
- const organization = await getOrganization(product?.organizationId);
+ const organization = await getOrganization(project?.organizationId);
if (!organization) {
throw new Error(t("common.organization_not_found"));
@@ -56,15 +56,15 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
- const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
- const { hasManageAccess } = getTeamPermissionFlags(productPermission);
+ const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
+ const { hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && !hasManageAccess;
return (
- }) => {
/>
-
+ title={t("environments.project.languages.multi_language_surveys")}
+ description={t("environments.project.languages.multi_language_surveys_description")}>
+
);
};
-
-export default Page;
diff --git a/apps/web/modules/ee/license-check/lib/utils.ts b/apps/web/modules/ee/license-check/lib/utils.ts
index 91fa7463e5..b797091b3a 100644
--- a/apps/web/modules/ee/license-check/lib/utils.ts
+++ b/apps/web/modules/ee/license-check/lib/utils.ts
@@ -14,7 +14,7 @@ import {
ENTERPRISE_LICENSE_KEY,
IS_AI_CONFIGURED,
IS_FORMBRICKS_CLOUD,
- PRODUCT_FEATURE_KEYS,
+ PROJECT_FEATURE_KEYS,
} from "@formbricks/lib/constants";
import { env } from "@formbricks/lib/env";
import { hashString } from "@formbricks/lib/hashString";
@@ -81,7 +81,7 @@ const fetchLicenseForE2ETesting = async (): Promise<{
// first call
const newResult = {
active: true,
- features: { isMultiOrgEnabled: true, twoFactorAuth: true, sso: true },
+ features: { isMultiOrgEnabled: true, projects: 3, twoFactorAuth: true, sso: true },
lastChecked: currentTime,
};
await setPreviousResult(newResult);
@@ -138,7 +138,7 @@ export const getEnterpriseLicense = async (): Promise<{
if (isValid === null) {
const newResult = {
active: false,
- features: { isMultiOrgEnabled: false, twoFactorAuth: false, sso: false },
+ features: { isMultiOrgEnabled: false, projects: 3, twoFactorAuth: false, sso: false },
lastChecked: new Date(),
};
@@ -244,28 +244,32 @@ export const fetchLicense = reactCache(
);
export const getRemoveInAppBrandingPermission = (organization: TOrganization): boolean => {
- if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PRODUCT_FEATURE_KEYS.FREE;
+ if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PROJECT_FEATURE_KEYS.FREE;
else if (!IS_FORMBRICKS_CLOUD) return true;
return false;
};
export const getRemoveLinkBrandingPermission = (organization: TOrganization): boolean => {
- if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PRODUCT_FEATURE_KEYS.FREE;
+ if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PROJECT_FEATURE_KEYS.FREE;
else if (!IS_FORMBRICKS_CLOUD) return true;
return false;
};
export const getSurveyFollowUpsPermission = async (organization: TOrganization): Promise
=> {
- if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PRODUCT_FEATURE_KEYS.FREE;
+ if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PROJECT_FEATURE_KEYS.FREE;
else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active;
return false;
};
export const getRoleManagementPermission = async (organization: TOrganization): Promise => {
+ if (E2E_TESTING) {
+ const previousResult = await fetchLicenseForE2ETesting();
+ return previousResult && previousResult.active !== null ? previousResult.active : false;
+ }
if (IS_FORMBRICKS_CLOUD)
return (
- organization.billing.plan === PRODUCT_FEATURE_KEYS.SCALE ||
- organization.billing.plan === PRODUCT_FEATURE_KEYS.ENTERPRISE
+ organization.billing.plan === PROJECT_FEATURE_KEYS.SCALE ||
+ organization.billing.plan === PROJECT_FEATURE_KEYS.ENTERPRISE
);
else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active;
return false;
@@ -274,15 +278,15 @@ export const getRoleManagementPermission = async (organization: TOrganization):
export const getAdvancedTargetingPermission = async (organization: TOrganization): Promise => {
if (IS_FORMBRICKS_CLOUD)
return (
- organization.billing.plan === PRODUCT_FEATURE_KEYS.SCALE ||
- organization.billing.plan === PRODUCT_FEATURE_KEYS.ENTERPRISE
+ organization.billing.plan === PROJECT_FEATURE_KEYS.SCALE ||
+ organization.billing.plan === PROJECT_FEATURE_KEYS.ENTERPRISE
);
else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active;
else return false;
};
export const getBiggerUploadFileSizePermission = async (organization: TOrganization): Promise => {
- if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PRODUCT_FEATURE_KEYS.FREE;
+ if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PROJECT_FEATURE_KEYS.FREE;
else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active;
return false;
};
@@ -294,8 +298,8 @@ export const getMultiLanguagePermission = async (organization: TOrganization): P
}
if (IS_FORMBRICKS_CLOUD)
return (
- organization.billing.plan === PRODUCT_FEATURE_KEYS.SCALE ||
- organization.billing.plan === PRODUCT_FEATURE_KEYS.ENTERPRISE
+ organization.billing.plan === PROJECT_FEATURE_KEYS.SCALE ||
+ organization.billing.plan === PROJECT_FEATURE_KEYS.ENTERPRISE
);
else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active;
return false;
@@ -337,9 +341,9 @@ export const getIsOrganizationAIReady = async (billingPlan: TOrganizationBilling
return (
IS_AI_CONFIGURED &&
(await getEnterpriseLicense()).active &&
- (billingPlan === PRODUCT_FEATURE_KEYS.STARTUP ||
- billingPlan === PRODUCT_FEATURE_KEYS.SCALE ||
- billingPlan === PRODUCT_FEATURE_KEYS.ENTERPRISE)
+ (billingPlan === PROJECT_FEATURE_KEYS.STARTUP ||
+ billingPlan === PROJECT_FEATURE_KEYS.SCALE ||
+ billingPlan === PROJECT_FEATURE_KEYS.ENTERPRISE)
);
}
@@ -349,3 +353,25 @@ export const getIsOrganizationAIReady = async (billingPlan: TOrganizationBilling
export const getIsAIEnabled = async (organization: TOrganization) => {
return organization.isAIEnabled && (await getIsOrganizationAIReady(organization.billing.plan));
};
+
+export const getOrganizationProjectsLimit = async (organization: TOrganization): Promise => {
+ if (E2E_TESTING) {
+ const previousResult = await fetchLicenseForE2ETesting();
+ return previousResult && previousResult.features ? (previousResult.features.projects ?? Infinity) : 3;
+ }
+
+ let limit: number;
+
+ if (IS_FORMBRICKS_CLOUD && (await getEnterpriseLicense()).active) {
+ limit = organization.billing.limits.projects ?? Infinity;
+ } else {
+ const licenseFeatures = await getLicenseFeatures();
+ if (!licenseFeatures) {
+ limit = 3;
+ } else {
+ limit = licenseFeatures.projects ?? Infinity;
+ }
+ }
+
+ return limit;
+};
diff --git a/apps/web/modules/ee/license-check/types/enterprise-license.ts b/apps/web/modules/ee/license-check/types/enterprise-license.ts
index ee3a0a1531..daa1b9c831 100644
--- a/apps/web/modules/ee/license-check/types/enterprise-license.ts
+++ b/apps/web/modules/ee/license-check/types/enterprise-license.ts
@@ -6,6 +6,7 @@ export type TEnterpriseLicenseStatus = z.infer;
const ZEnterpriseLicenseFeatures = z.object({
isMultiOrgEnabled: z.boolean(),
+ projects: z.number().nullable(),
twoFactorAuth: z.boolean(),
sso: z.boolean(),
});
diff --git a/apps/web/modules/ee/multi-language-surveys/components/default-language-select.tsx b/apps/web/modules/ee/multi-language-surveys/components/default-language-select.tsx
index e5eedfbac7..645c6d62ad 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/default-language-select.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/default-language-select.tsx
@@ -8,13 +8,13 @@ import {
} from "@/modules/ui/components/select";
import { useTranslations } from "next-intl";
import { getLanguageLabel } from "@formbricks/lib/i18n/utils";
-import type { TLanguage, TProduct } from "@formbricks/types/product";
+import type { TLanguage, TProject } from "@formbricks/types/project";
import type { ConfirmationModalProps } from "./multi-language-card";
interface DefaultLanguageSelectProps {
defaultLanguage?: TLanguage;
handleDefaultLanguageChange: (languageCode: string) => void;
- product: TProduct;
+ project: TProject;
setConfirmationModalInfo: (confirmationModal: ConfirmationModalProps) => void;
locale: string;
}
@@ -22,7 +22,7 @@ interface DefaultLanguageSelectProps {
export function DefaultLanguageSelect({
defaultLanguage,
handleDefaultLanguageChange,
- product,
+ project,
setConfirmationModalInfo,
locale,
}: DefaultLanguageSelectProps) {
@@ -60,7 +60,7 @@ export function DefaultLanguageSelect({
- {product.languages.map((language) => (
+ {project.languages.map((language) => (
string) =
.map((language) => language.alias!.toLowerCase().trim());
if (languageCodes.includes("")) {
- toast.error(t("environments.product.languages.please_select_a_language"), { duration: 2000 });
+ toast.error(t("environments.project.languages.please_select_a_language"), { duration: 2000 });
return false;
}
// Check for duplicates within the languageCodes and languageAliases
if (checkIfDuplicateExists(languageAliases) || checkIfDuplicateExists(languageCodes)) {
- toast.error(t("environments.product.languages.duplicate_language_or_language_id"), { duration: 4000 });
+ toast.error(t("environments.project.languages.duplicate_language_or_language_id"), { duration: 4000 });
return false;
}
// Check if any alias matches the identifier of any added languages
if (languageCodes.some((code) => languageAliases.includes(code))) {
- toast.error(t("environments.product.languages.conflict_between_identifier_and_alias"), {
+ toast.error(t("environments.project.languages.conflict_between_identifier_and_alias"), {
duration: 6000,
});
return false;
@@ -57,7 +57,7 @@ const validateLanguages = (languages: TLanguage[], t: (key: string) => string) =
// Check if the chosen alias matches an ISO identifier of a language that hasn’t been added
for (const alias of languageAliases) {
if (iso639Languages.some((language) => language.alpha2 === alias && !languageCodes.includes(alias))) {
- toast.error(t("environments.product.languages.conflict_between_selected_alias_and_another_language"), {
+ toast.error(t("environments.project.languages.conflict_between_selected_alias_and_another_language"), {
duration: 6000,
});
return false;
@@ -67,9 +67,9 @@ const validateLanguages = (languages: TLanguage[], t: (key: string) => string) =
return true;
};
-export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps) {
+export function EditLanguage({ project, locale, isReadOnly }: EditLanguageProps) {
const t = useTranslations();
- const [languages, setLanguages] = useState(product.languages);
+ const [languages, setLanguages] = useState(project.languages);
const [isEditing, setIsEditing] = useState(false);
const [confirmationModal, setConfirmationModal] = useState({
isOpen: false,
@@ -79,8 +79,8 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
});
useEffect(() => {
- setLanguages(product.languages);
- }, [product.languages]);
+ setLanguages(project.languages);
+ }, [project.languages]);
const handleAddLanguage = () => {
const newLanguage = { id: "new", createdAt: new Date(), updatedAt: new Date(), code: "", alias: "" };
@@ -102,14 +102,14 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
setConfirmationModal({
isOpen: true,
languageId,
- text: `${t("environments.product.languages.cannot_remove_language_warning")}:\n\n${surveyList}\n\n${t("environments.product.languages.remove_language_from_surveys_to_remove_it_from_product")}`,
+ text: `${t("environments.project.languages.cannot_remove_language_warning")}:\n\n${surveyList}\n\n${t("environments.project.languages.remove_language_from_surveys_to_remove_it_from_project")}`,
isButtonDisabled: true,
});
} else {
setConfirmationModal({
isOpen: true,
languageId,
- text: t("environments.product.languages.delete_language_confirmation"),
+ text: t("environments.project.languages.delete_language_confirmation"),
isButtonDisabled: false,
});
}
@@ -124,9 +124,9 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
const performLanguageDeletion = async (languageId: string) => {
try {
- await deleteLanguageAction({ languageId, productId: product.id });
+ await deleteLanguageAction({ languageId, projectId: project.id });
setLanguages((prev) => prev.filter((lang) => lang.id !== languageId));
- toast.success(t("environments.product.languages.language_deleted_successfully"));
+ toast.success(t("environments.project.languages.language_deleted_successfully"));
// Close the modal after deletion
setConfirmationModal((prev) => ({ ...prev, isOpen: false }));
} catch (err) {
@@ -136,7 +136,7 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
};
const handleCancelChanges = async () => {
- setLanguages(product.languages);
+ setLanguages(project.languages);
setIsEditing(false);
};
@@ -146,24 +146,24 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
languages.map((lang) => {
return lang.id === "new"
? createLanguageAction({
- productId: product.id,
+ projectId: project.id,
languageInput: { code: lang.code, alias: lang.alias },
})
: updateLanguageAction({
- productId: product.id,
+ projectId: project.id,
languageId: lang.id,
languageInput: { code: lang.code, alias: lang.alias },
});
})
);
- toast.success(t("environments.product.languages.languages_updated_successfully"));
+ toast.success(t("environments.project.languages.languages_updated_successfully"));
setIsEditing(false);
};
const AddLanguageButton: React.FC<{ onClick: () => void }> = ({ onClick }) =>
- isEditing && languages.length === product.languages.length ? (
+ isEditing && languages.length === project.languages.length ? (
- {t("environments.product.languages.add_language")}
+ {t("environments.project.languages.add_language")}
) : null;
@@ -191,7 +191,7 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
>
) : (
- {t("environments.product.languages.no_language_found")}
+ {t("environments.project.languages.no_language_found")}
)}
@@ -208,7 +208,7 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
/>
)}
performLanguageDeletion(confirmationModal.languageId)}
open={confirmationModal.isOpen}
@@ -216,7 +216,7 @@ export function EditLanguage({ product, locale, isReadOnly }: EditLanguageProps)
setConfirmationModal((prev) => ({ ...prev, isOpen: !prev.isOpen }));
}}
text={confirmationModal.text}
- title={t("environments.product.languages.remove_language")}
+ title={t("environments.project.languages.remove_language")}
/>
);
@@ -240,6 +240,6 @@ const EditSaveButtons: React.FC<{
) : (
- {t("environments.product.languages.edit_languages")}
+ {t("environments.project.languages.edit_languages")}
);
diff --git a/apps/web/modules/ee/multi-language-surveys/components/language-labels.tsx b/apps/web/modules/ee/multi-language-surveys/components/language-labels.tsx
index 2c4b7c7029..639bfbfec7 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/language-labels.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/language-labels.tsx
@@ -7,10 +7,10 @@ export function LanguageLabels() {
const t = useTranslations();
return (
-
{t("environments.product.languages.language")}
-
{t("environments.product.languages.identifier")}
+
{t("environments.project.languages.language")}
+
{t("environments.project.languages.identifier")}
- {t("environments.product.languages.alias")}
+ {t("environments.project.languages.alias")}
);
@@ -25,7 +25,7 @@ function AliasTooltip({ t }: { t: (key: string) => string }) {
- {t("environments.product.languages.alias_tooltip")}
+ {t("environments.project.languages.alias_tooltip")}
);
diff --git a/apps/web/modules/ee/multi-language-surveys/components/language-row.tsx b/apps/web/modules/ee/multi-language-surveys/components/language-row.tsx
index 060871c4aa..b073a88c66 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/language-row.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/language-row.tsx
@@ -1,7 +1,7 @@
import { Button } from "@/modules/ui/components/button";
import { Input } from "@/modules/ui/components/input";
import { useTranslations } from "next-intl";
-import type { TLanguage } from "@formbricks/types/product";
+import type { TLanguage } from "@formbricks/types/project";
import { TUserLocale } from "@formbricks/types/user";
import { LanguageSelect } from "./language-select";
diff --git a/apps/web/modules/ee/multi-language-surveys/components/language-select.tsx b/apps/web/modules/ee/multi-language-surveys/components/language-select.tsx
index 2b8deda35e..b079df84ed 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/language-select.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/language-select.tsx
@@ -6,7 +6,7 @@ import { useEffect, useRef, useState } from "react";
import type { TIso639Language } from "@formbricks/lib/i18n/utils";
import { iso639Languages } from "@formbricks/lib/i18n/utils";
import { useClickOutside } from "@formbricks/lib/utils/hooks/useClickOutside";
-import type { TLanguage } from "@formbricks/types/product";
+import type { TLanguage } from "@formbricks/types/project";
import { TUserLocale } from "@formbricks/types/user";
interface LanguageSelectProps {
@@ -70,7 +70,7 @@ export function LanguageSelect({ language, onLanguageChange, disabled, locale }:
onChange={(e) => {
setSearchTerm(e.target.value);
}}
- placeholder={t("environments.product.languages.search_items")}
+ placeholder={t("environments.project.languages.search_items")}
ref={inputRef}
type="text"
value={searchTerm}
diff --git a/apps/web/modules/ee/multi-language-surveys/components/language-toggle.tsx b/apps/web/modules/ee/multi-language-surveys/components/language-toggle.tsx
index 3a2d4b6cdc..42a443066b 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/language-toggle.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/language-toggle.tsx
@@ -2,7 +2,7 @@ import { Label } from "@/modules/ui/components/label";
import { Switch } from "@/modules/ui/components/switch";
import { useTranslations } from "next-intl";
import { getLanguageLabel } from "@formbricks/lib/i18n/utils";
-import type { TLanguage } from "@formbricks/types/product";
+import type { TLanguage } from "@formbricks/types/project";
import type { TUserLocale } from "@formbricks/types/user";
interface LanguageToggleProps {
diff --git a/apps/web/modules/ee/multi-language-surveys/components/localized-editor.tsx b/apps/web/modules/ee/multi-language-surveys/components/localized-editor.tsx
index 0c23073ad2..021c189fa5 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/localized-editor.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/localized-editor.tsx
@@ -94,7 +94,7 @@ export function LocalizedEditor({
{value && selectedLanguageCode !== "default" && value.default ? (
- {t("environments.product.languages.translate")}:
+ {t("environments.project.languages.translate")}:
- {t("environments.product.languages.incomplete_translations")}
+ {t("environments.project.languages.incomplete_translations")}
) : null}
diff --git a/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx b/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx
index b4183d49d5..b31d9687e3 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx
@@ -15,7 +15,7 @@ import type { FC } from "react";
import { useEffect, useMemo, useState } from "react";
import { cn } from "@formbricks/lib/cn";
import { addMultiLanguageLabels, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
-import type { TLanguage, TProduct } from "@formbricks/types/product";
+import type { TLanguage, TProject } from "@formbricks/types/project";
import type { TSurvey, TSurveyLanguage, TSurveyQuestionId } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { DefaultLanguageSelect } from "./default-language-select";
@@ -23,7 +23,7 @@ import { SecondaryLanguageSelect } from "./secondary-language-select";
interface MultiLanguageCardProps {
localSurvey: TSurvey;
- product: TProduct;
+ project: TProject;
setLocalSurvey: (survey: TSurvey) => void;
activeQuestionId: TSurveyQuestionId | null;
setActiveQuestionId: (questionId: TSurveyQuestionId | null) => void;
@@ -44,7 +44,7 @@ export interface ConfirmationModalProps {
export const MultiLanguageCard: FC = ({
activeQuestionId,
- product,
+ project,
localSurvey,
setActiveQuestionId,
setLocalSurvey,
@@ -120,7 +120,7 @@ export const MultiLanguageCard: FC = ({
};
const handleDefaultLanguageChange = (languageCode: string) => {
- const language = product.languages.find((lang) => lang.code === languageCode);
+ const language = project.languages.find((lang) => lang.code === languageCode);
if (language) {
let languageExists = false;
@@ -213,7 +213,7 @@ export const MultiLanguageCard: FC = ({
{
handleActivationSwitchLogic();
@@ -238,16 +238,16 @@ export const MultiLanguageCard: FC = ({
/>
) : (
<>
- {product.languages.length <= 1 && (
+ {project.languages.length <= 1 && (
- {product.languages.length === 0
+ {project.languages.length === 0
? t("environments.surveys.edit.no_languages_found_add_first_one_to_get_started")
: t(
- "environments.surveys.edit.you_need_to_have_two_or_more_languages_set_up_in_your_product_to_work_with_translations"
+ "environments.surveys.edit.you_need_to_have_two_or_more_languages_set_up_in_your_project_to_work_with_translations"
)}
)}
- {product.languages.length > 1 && (
+ {project.languages.length > 1 && (
{isMultiLanguageAllowed && !isMultiLanguageActivated ? (
@@ -262,7 +262,7 @@ export const MultiLanguageCard: FC = ({
@@ -270,7 +270,7 @@ export const MultiLanguageCard: FC = ({
= ({
)}
-
+
{t("environments.surveys.edit.manage_languages")}{" "}
diff --git a/apps/web/modules/ee/multi-language-surveys/components/secondary-language-select.tsx b/apps/web/modules/ee/multi-language-surveys/components/secondary-language-select.tsx
index f67b4302f5..09168f3607 100644
--- a/apps/web/modules/ee/multi-language-surveys/components/secondary-language-select.tsx
+++ b/apps/web/modules/ee/multi-language-surveys/components/secondary-language-select.tsx
@@ -1,11 +1,11 @@
import { useTranslations } from "next-intl";
-import type { TLanguage, TProduct } from "@formbricks/types/product";
+import type { TLanguage, TProject } from "@formbricks/types/project";
import type { TSurvey, TSurveyQuestionId } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { LanguageToggle } from "./language-toggle";
interface SecondaryLanguageSelectProps {
- product: TProduct;
+ project: TProject;
defaultLanguage: TLanguage;
setSelectedLanguageCode: (languageCode: string) => void;
setActiveQuestionId: (questionId: TSurveyQuestionId) => void;
@@ -15,7 +15,7 @@ interface SecondaryLanguageSelectProps {
}
export function SecondaryLanguageSelect({
- product,
+ project,
defaultLanguage,
setSelectedLanguageCode,
setActiveQuestionId,
@@ -35,7 +35,7 @@ export function SecondaryLanguageSelect({
{t("environments.surveys.edit.2_activate_translation_for_specific_languages")}:
- {product.languages
+ {project.languages
.filter((lang) => lang.id !== defaultLanguage.id)
.map((language) => (
{
export const createLanguageAction = authenticatedActionClient
.schema(ZCreateLanguageAction)
.action(async ({ ctx, parsedInput }) => {
- const organizationId = await getOrganizationIdFromProductId(parsedInput.productId);
+ const organizationId = await getOrganizationIdFromProjectId(parsedInput.projectId);
await checkAuthorizationUpdated({
userId: ctx.user.id,
@@ -55,32 +55,32 @@ export const createLanguageAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: parsedInput.productId,
+ type: "projectTeam",
+ projectId: parsedInput.projectId,
minPermission: "manage",
},
],
});
await checkMultiLanguagePermission(organizationId);
- return await createLanguage(parsedInput.productId, parsedInput.languageInput);
+ return await createLanguage(parsedInput.projectId, parsedInput.languageInput);
});
const ZDeleteLanguageAction = z.object({
languageId: ZId,
- productId: ZId,
+ projectId: ZId,
});
export const deleteLanguageAction = authenticatedActionClient
.schema(ZDeleteLanguageAction)
.action(async ({ ctx, parsedInput }) => {
- const languageProductId = await getProductIdFromLanguageId(parsedInput.languageId);
+ const languageProjectId = await getProjectIdFromLanguageId(parsedInput.languageId);
- if (languageProductId !== parsedInput.productId) {
+ if (languageProjectId !== parsedInput.projectId) {
throw new Error("Invalid language id");
}
- const organizationId = await getOrganizationIdFromProductId(parsedInput.productId);
+ const organizationId = await getOrganizationIdFromProjectId(parsedInput.projectId);
await checkAuthorizationUpdated({
userId: ctx.user.id,
@@ -91,15 +91,15 @@ export const deleteLanguageAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: parsedInput.productId,
+ type: "projectTeam",
+ projectId: parsedInput.projectId,
minPermission: "manage",
},
],
});
await checkMultiLanguagePermission(organizationId);
- return await deleteLanguage(parsedInput.languageId, parsedInput.productId);
+ return await deleteLanguage(parsedInput.languageId, parsedInput.projectId);
});
const ZGetSurveysUsingGivenLanguageAction = z.object({
@@ -120,8 +120,8 @@ export const getSurveysUsingGivenLanguageAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: await getProductIdFromLanguageId(parsedInput.languageId),
+ type: "projectTeam",
+ projectId: await getProjectIdFromLanguageId(parsedInput.languageId),
minPermission: "manage",
},
],
@@ -132,7 +132,7 @@ export const getSurveysUsingGivenLanguageAction = authenticatedActionClient
});
const ZUpdateLanguageAction = z.object({
- productId: ZId,
+ projectId: ZId,
languageId: ZId,
languageInput: ZLanguageInput,
});
@@ -140,13 +140,13 @@ const ZUpdateLanguageAction = z.object({
export const updateLanguageAction = authenticatedActionClient
.schema(ZUpdateLanguageAction)
.action(async ({ ctx, parsedInput }) => {
- const languageProductId = await getProductIdFromLanguageId(parsedInput.languageId);
+ const languageProductId = await getProjectIdFromLanguageId(parsedInput.languageId);
- if (languageProductId !== parsedInput.productId) {
+ if (languageProductId !== parsedInput.projectId) {
throw new Error("Invalid language id");
}
- const organizationId = await getOrganizationIdFromProductId(parsedInput.productId);
+ const organizationId = await getOrganizationIdFromProjectId(parsedInput.projectId);
await checkAuthorizationUpdated({
userId: ctx.user.id,
@@ -159,13 +159,13 @@ export const updateLanguageAction = authenticatedActionClient
roles: ["owner", "manager"],
},
{
- type: "productTeam",
- productId: parsedInput.productId,
+ type: "projectTeam",
+ projectId: parsedInput.projectId,
minPermission: "manage",
},
],
});
await checkMultiLanguagePermission(organizationId);
- return await updateLanguage(parsedInput.productId, parsedInput.languageId, parsedInput.languageInput);
+ return await updateLanguage(parsedInput.projectId, parsedInput.languageId, parsedInput.languageInput);
});
diff --git a/apps/web/modules/ee/role-management/lib/membership.ts b/apps/web/modules/ee/role-management/lib/membership.ts
index e03533e0e3..315ac98fbd 100644
--- a/apps/web/modules/ee/role-management/lib/membership.ts
+++ b/apps/web/modules/ee/role-management/lib/membership.ts
@@ -4,7 +4,7 @@ import { teamCache } from "@/lib/cache/team";
import { Prisma } from "@prisma/client";
import { prisma } from "@formbricks/database";
import { organizationCache } from "@formbricks/lib/organization/cache";
-import { productCache } from "@formbricks/lib/product/cache";
+import { projectCache } from "@formbricks/lib/project/cache";
import { validateInputs } from "@formbricks/lib/utils/validate";
import { ZString } from "@formbricks/types/common";
import { ResourceNotFoundError } from "@formbricks/types/errors";
@@ -74,7 +74,7 @@ export const updateMembership = async (
organizationId,
});
- productCache.revalidate({
+ projectCache.revalidate({
userId,
});
diff --git a/apps/web/modules/ee/teams/lib/roles.ts b/apps/web/modules/ee/teams/lib/roles.ts
index cf6c6e8527..9d3d6f4cd9 100644
--- a/apps/web/modules/ee/teams/lib/roles.ts
+++ b/apps/web/modules/ee/teams/lib/roles.ts
@@ -1,6 +1,6 @@
import "server-only";
import { teamCache } from "@/lib/cache/team";
-import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { TTeamRole } from "@/modules/ee/teams/team-list/types/teams";
import { Prisma } from "@prisma/client";
import { cache as reactCache } from "react";
@@ -11,16 +11,16 @@ import { validateInputs } from "@formbricks/lib/utils/validate";
import { ZId, ZString } from "@formbricks/types/common";
import { DatabaseError, UnknownError } from "@formbricks/types/errors";
-export const getProductPermissionByUserId = reactCache(
- async (userId: string, productId: string): Promise =>
+export const getProjectPermissionByUserId = reactCache(
+ async (userId: string, projectId: string): Promise =>
cache(
async () => {
- validateInputs([userId, ZString], [productId, ZString]);
+ validateInputs([userId, ZString], [projectId, ZString]);
try {
- const productMemberships = await prisma.productTeam.findMany({
+ const projectMemberships = await prisma.projectTeam.findMany({
where: {
- productId,
+ projectId,
team: {
teamUsers: {
some: {
@@ -31,10 +31,10 @@ export const getProductPermissionByUserId = reactCache(
},
});
- if (!productMemberships) return null;
+ if (!projectMemberships) return null;
let highestPermission: TTeamPermission | null = null;
- for (const membership of productMemberships) {
+ for (const membership of projectMemberships) {
if (membership.permission === "manage") {
highestPermission = "manage";
} else if (membership.permission === "readWrite" && highestPermission !== "manage") {
@@ -58,9 +58,9 @@ export const getProductPermissionByUserId = reactCache(
throw new UnknownError("Error while fetching membership");
}
},
- [`getProductPermissionByUserId-${userId}-${productId}`],
+ [`getProjectPermissionByUserId-${userId}-${projectId}`],
{
- tags: [teamCache.tag.byUserId(userId), teamCache.tag.byProductId(productId)],
+ tags: [teamCache.tag.byUserId(userId), teamCache.tag.byProjectId(projectId)],
}
)()
);
diff --git a/apps/web/modules/ee/teams/product-teams/actions.ts b/apps/web/modules/ee/teams/project-teams/actions.ts
similarity index 66%
rename from apps/web/modules/ee/teams/product-teams/actions.ts
rename to apps/web/modules/ee/teams/project-teams/actions.ts
index d89168b3cd..ce09d0d137 100644
--- a/apps/web/modules/ee/teams/product-teams/actions.ts
+++ b/apps/web/modules/ee/teams/project-teams/actions.ts
@@ -2,35 +2,35 @@
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
-import { getOrganizationIdFromProductId, getOrganizationIdFromTeamId } from "@/lib/utils/helper";
+import { getOrganizationIdFromProjectId, getOrganizationIdFromTeamId } from "@/lib/utils/helper";
import { checkRoleManagementPermission } from "@/modules/ee/role-management/actions";
import {
addTeamAccess,
removeTeamAccess,
updateTeamAccessPermission,
-} from "@/modules/ee/teams/product-teams/lib/teams";
+} from "@/modules/ee/teams/project-teams/lib/teams";
import { z } from "zod";
import { ZId } from "@formbricks/types/common";
import { ZTeamPermission } from "./types/teams";
const ZRemoveAccessAction = z.object({
- productId: z.string(),
+ projectId: z.string(),
teamId: z.string(),
});
export const removeAccessAction = authenticatedActionClient
.schema(ZRemoveAccessAction)
.action(async ({ ctx, parsedInput }) => {
- const productOrganizationId = await getOrganizationIdFromProductId(parsedInput.productId);
+ const projectOrganizationId = await getOrganizationIdFromProjectId(parsedInput.projectId);
const teamOrganizationId = await getOrganizationIdFromTeamId(parsedInput.teamId);
- if (productOrganizationId !== teamOrganizationId) {
- throw new Error("Team and product are not in the same organization");
+ if (projectOrganizationId !== teamOrganizationId) {
+ throw new Error("Team and project are not in the same organization");
}
await checkAuthorizationUpdated({
userId: ctx.user.id,
- organizationId: productOrganizationId,
+ organizationId: projectOrganizationId,
access: [
{
type: "organization",
@@ -39,20 +39,20 @@ export const removeAccessAction = authenticatedActionClient
],
});
- await checkRoleManagementPermission(productOrganizationId);
+ await checkRoleManagementPermission(projectOrganizationId);
- return await removeTeamAccess(parsedInput.productId, parsedInput.teamId);
+ return await removeTeamAccess(parsedInput.projectId, parsedInput.teamId);
});
const ZAddAccessAction = z.object({
- productId: z.string(),
+ projectId: z.string(),
teamIds: z.array(ZId),
});
export const addAccessAction = authenticatedActionClient
.schema(ZAddAccessAction)
.action(async ({ ctx, parsedInput }) => {
- const organizationId = await getOrganizationIdFromProductId(parsedInput.productId);
+ const organizationId = await getOrganizationIdFromProjectId(parsedInput.projectId);
await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId,
@@ -66,11 +66,11 @@ export const addAccessAction = authenticatedActionClient
await checkRoleManagementPermission(organizationId);
- return await addTeamAccess(parsedInput.productId, parsedInput.teamIds);
+ return await addTeamAccess(parsedInput.projectId, parsedInput.teamIds);
});
const ZUpdateAccessPermissionAction = z.object({
- productId: z.string(),
+ projectId: z.string(),
teamId: z.string(),
permission: ZTeamPermission,
});
@@ -78,16 +78,16 @@ const ZUpdateAccessPermissionAction = z.object({
export const updateAccessPermissionAction = authenticatedActionClient
.schema(ZUpdateAccessPermissionAction)
.action(async ({ ctx, parsedInput }) => {
- const productOrganizationId = await getOrganizationIdFromProductId(parsedInput.productId);
+ const projectOrganizationId = await getOrganizationIdFromProjectId(parsedInput.projectId);
const teamOrganizationId = await getOrganizationIdFromTeamId(parsedInput.teamId);
- if (productOrganizationId !== teamOrganizationId) {
- throw new Error("Team and product are not in the same organization");
+ if (projectOrganizationId !== teamOrganizationId) {
+ throw new Error("Team and project are not in the same organization");
}
await checkAuthorizationUpdated({
userId: ctx.user.id,
- organizationId: productOrganizationId,
+ organizationId: projectOrganizationId,
access: [
{
type: "organization",
@@ -96,10 +96,10 @@ export const updateAccessPermissionAction = authenticatedActionClient
],
});
- await checkRoleManagementPermission(productOrganizationId);
+ await checkRoleManagementPermission(projectOrganizationId);
return await updateTeamAccessPermission(
- parsedInput.productId,
+ parsedInput.projectId,
parsedInput.teamId,
parsedInput.permission
);
diff --git a/apps/web/modules/ee/teams/product-teams/components/access-table.tsx b/apps/web/modules/ee/teams/project-teams/components/access-table.tsx
similarity index 86%
rename from apps/web/modules/ee/teams/product-teams/components/access-table.tsx
rename to apps/web/modules/ee/teams/project-teams/components/access-table.tsx
index 045e68d64d..0903253deb 100644
--- a/apps/web/modules/ee/teams/product-teams/components/access-table.tsx
+++ b/apps/web/modules/ee/teams/project-teams/components/access-table.tsx
@@ -1,8 +1,8 @@
"use client";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
-import { removeAccessAction, updateAccessPermissionAction } from "@/modules/ee/teams/product-teams/actions";
-import { TProductTeam, TTeamPermission, ZTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { removeAccessAction, updateAccessPermissionAction } from "@/modules/ee/teams/project-teams/actions";
+import { TProjectTeam, TTeamPermission, ZTeamPermission } from "@/modules/ee/teams/project-teams/types/teams";
import { TeamPermissionMapping } from "@/modules/ee/teams/utils/teams";
import { AlertDialog } from "@/modules/ui/components/alert-dialog";
import { Button } from "@/modules/ui/components/button";
@@ -21,13 +21,13 @@ import { useState } from "react";
import toast from "react-hot-toast";
interface AccessTableProps {
- teams: TProductTeam[];
+ teams: TProjectTeam[];
environmentId: string;
- productId: string;
+ projectId: string;
isOwnerOrManager: boolean;
}
-export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager }: AccessTableProps) => {
+export const AccessTable = ({ teams, environmentId, projectId, isOwnerOrManager }: AccessTableProps) => {
const t = useTranslations();
const [selectedTeamId, setSelectedTeamId] = useState(null);
const [removeAccessModalOpen, setRemoveAccessModalOpen] = useState(false);
@@ -35,7 +35,7 @@ export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager
const router = useRouter();
const removeAccess = async (teamId: string) => {
- const removeAccessActionResponse = await removeAccessAction({ productId, teamId });
+ const removeAccessActionResponse = await removeAccessAction({ projectId, teamId });
if (removeAccessActionResponse?.data) {
router.refresh();
} else {
@@ -46,7 +46,7 @@ export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager
const handlePermissionChange = async (teamId: string, permission: TTeamPermission) => {
const updateAccessPermissionActionResponse = await updateAccessPermissionAction({
- productId,
+ projectId,
teamId,
permission,
});
@@ -70,8 +70,8 @@ export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager
- {t("environments.product.teams.team_name")}
- {t("environments.product.teams.permission")}
+ {t("environments.project.teams.team_name")}
+ {t("environments.project.teams.permission")}
{isOwnerOrManager && Actions }
@@ -79,7 +79,7 @@ export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager
{teams.length === 0 && (
- {t("environments.product.teams.no_teams_found")}
+ {t("environments.project.teams.no_teams_found")}
)}
@@ -105,13 +105,13 @@ export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager
- {t("environments.product.teams.read")}
+ {t("environments.project.teams.read")}
- {t("environments.product.teams.read_write")}
+ {t("environments.project.teams.read_write")}
- {t("environments.product.teams.manage")}
+ {t("environments.project.teams.manage")}
@@ -142,8 +142,8 @@ export const AccessTable = ({ teams, environmentId, productId, isOwnerOrManager
{
setSelectedTeamId(null);
diff --git a/apps/web/modules/ee/teams/product-teams/components/access-view.tsx b/apps/web/modules/ee/teams/project-teams/components/access-view.tsx
similarity index 64%
rename from apps/web/modules/ee/teams/product-teams/components/access-view.tsx
rename to apps/web/modules/ee/teams/project-teams/components/access-view.tsx
index cd841aa85d..e6572665d0 100644
--- a/apps/web/modules/ee/teams/product-teams/components/access-view.tsx
+++ b/apps/web/modules/ee/teams/project-teams/components/access-view.tsx
@@ -1,22 +1,22 @@
"use client";
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
-import { AccessTable } from "@/modules/ee/teams/product-teams/components/access-table";
-import { AddTeam } from "@/modules/ee/teams/product-teams/components/add-team";
-import { TOrganizationTeam, TProductTeam } from "@/modules/ee/teams/product-teams/types/teams";
+import { AccessTable } from "@/modules/ee/teams/project-teams/components/access-table";
+import { AddTeam } from "@/modules/ee/teams/project-teams/components/add-team";
+import { TOrganizationTeam, TProjectTeam } from "@/modules/ee/teams/project-teams/types/teams";
import { useTranslations } from "next-intl";
-import { TProduct } from "@formbricks/types/product";
+import { TProject } from "@formbricks/types/project";
interface AccessViewProps {
- product: TProduct;
- teams: TProductTeam[];
+ project: TProject;
+ teams: TProjectTeam[];
environmentId: string;
organizationTeams: TOrganizationTeam[];
isOwnerOrManager: boolean;
}
export const AccessView = ({
- product,
+ project,
teams,
organizationTeams,
environmentId,
@@ -27,21 +27,21 @@ export const AccessView = ({
<>
+ description={t("environments.project.teams.team_settings_description")}>
{isOwnerOrManager && (
)}
diff --git a/apps/web/modules/ee/teams/product-teams/components/add-team-modal.tsx b/apps/web/modules/ee/teams/project-teams/components/add-team-modal.tsx
similarity index 88%
rename from apps/web/modules/ee/teams/product-teams/components/add-team-modal.tsx
rename to apps/web/modules/ee/teams/project-teams/components/add-team-modal.tsx
index c7428a92f7..c8d9139284 100644
--- a/apps/web/modules/ee/teams/product-teams/components/add-team-modal.tsx
+++ b/apps/web/modules/ee/teams/project-teams/components/add-team-modal.tsx
@@ -1,7 +1,7 @@
"use client";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
-import { addAccessAction } from "@/modules/ee/teams/product-teams/actions";
+import { addAccessAction } from "@/modules/ee/teams/project-teams/actions";
import { Button } from "@/modules/ui/components/button";
import { Label } from "@/modules/ui/components/label";
import { Modal } from "@/modules/ui/components/modal";
@@ -17,10 +17,10 @@ interface AddTeamModalProps {
open: boolean;
setOpen: React.Dispatch
>;
teamOptions: { label: string; value: string }[];
- productId: string;
+ projectId: string;
}
-export const AddTeamModal = ({ open, setOpen, teamOptions, productId }: AddTeamModalProps) => {
+export const AddTeamModal = ({ open, setOpen, teamOptions, projectId }: AddTeamModalProps) => {
const t = useTranslations();
const [isLoading, setIsLoading] = useState(false);
const [selectedTeams, setSelectedTeams] = useState([]);
@@ -31,7 +31,7 @@ export const AddTeamModal = ({ open, setOpen, teamOptions, productId }: AddTeamM
e.preventDefault();
setIsLoading(true);
- const addTeamActionResponse = await addAccessAction({ productId, teamIds: selectedTeams });
+ const addTeamActionResponse = await addAccessAction({ projectId, teamIds: selectedTeams });
if (addTeamActionResponse?.data) {
router.refresh();
@@ -56,14 +56,14 @@ export const AddTeamModal = ({ open, setOpen, teamOptions, productId }: AddTeamM
-
{t("environments.product.teams.add_existing_team")}
+ {t("environments.project.teams.add_existing_team")}