diff --git a/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/components/ProductSettings.tsx b/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/components/ProductSettings.tsx
index a2300bf46d..948cf8be14 100644
--- a/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/components/ProductSettings.tsx
+++ b/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/components/ProductSettings.tsx
@@ -1,13 +1,16 @@
"use client";
import { createProductAction } from "@/app/(app)/environments/[environmentId]/actions";
+import { getFormattedErrorMessage } from "@/lib/utils/helper";
+import { TOrganizationTeam } from "@/modules/ee/teams/product-teams/types/teams";
+import { CreateTeamModal } from "@/modules/ee/teams/team-list/components/create-team-modal";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTranslations } from "next-intl";
import Image from "next/image";
import { useRouter } from "next/navigation";
+import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "react-hot-toast";
-import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { FORMBRICKS_SURVEYS_FILTERS_KEY_LS } from "@formbricks/lib/localStorage";
import { getPreviewSurvey } from "@formbricks/lib/styling/constants";
import {
@@ -29,6 +32,7 @@ import {
FormProvider,
} from "@formbricks/ui/components/Form";
import { Input } from "@formbricks/ui/components/Input";
+import { MultiSelect } from "@formbricks/ui/components/MultiSelect";
import { SurveyInline } from "@formbricks/ui/components/Survey";
interface ProductSettingsProps {
@@ -37,6 +41,8 @@ interface ProductSettingsProps {
channel: TProductConfigChannel;
industry: TProductConfigIndustry;
defaultBrandColor: string;
+ organizationTeams: TOrganizationTeam[];
+ canDoRoleManagement: boolean;
locale: string;
}
@@ -46,8 +52,12 @@ export const ProductSettings = ({
channel,
industry,
defaultBrandColor,
+ organizationTeams,
+ canDoRoleManagement = false,
locale,
}: ProductSettingsProps) => {
+ const [createTeamModalOpen, setCreateTeamModalOpen] = useState(false);
+
const router = useRouter();
const t = useTranslations();
const addProduct = async (data: TProductUpdateInput) => {
@@ -57,6 +67,7 @@ export const ProductSettings = ({
data: {
...data,
config: { channel, industry },
+ teamIds: data.teamIds,
},
});
@@ -92,6 +103,7 @@ export const ProductSettings = ({
defaultValues: {
name: "",
styling: { allowStyleOverwrite: true, brandColor: { light: defaultBrandColor } },
+ teamIds: [],
},
resolver: zodResolver(ZProductUpdateInput),
});
@@ -99,6 +111,11 @@ export const ProductSettings = ({
const brandColor = form.watch("styling.brandColor.light") ?? defaultBrandColor;
const { isSubmitting } = form.formState;
+ const organizationTeamsOptions = organizationTeams.map((team) => ({
+ label: team.name,
+ value: team.id,
+ }));
+
return (
@@ -155,8 +172,41 @@ export const ProductSettings = ({
)}
/>
+ {canDoRoleManagement && (
+
(
+
+
+
+ Teams
+ Who all can access this product?
+
+
+
+
+
+ field.onChange(teamIds)}
+ options={organizationTeamsOptions}
+ />
+ {error?.message && {error.message}}
+
+
+
+ )}
+ />
+ )}
-
@@ -175,7 +225,7 @@ export const ProductSettings = ({
/>
)}
{t("common.preview")}
-
+ {
+ form.setValue("teamIds", [...(form.getValues("teamIds") || []), teamId]);
+ }}
+ />
);
};
diff --git a/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/page.tsx b/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/page.tsx
index cb9388754f..929d93d04c 100644
--- a/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/page.tsx
+++ b/apps/web/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/page.tsx
@@ -1,11 +1,15 @@
+import { getTeamsByOrganizationId } from "@/app/(app)/(onboarding)/lib/onboarding";
import { getCustomHeadline } from "@/app/(app)/(onboarding)/lib/utils";
import { ProductSettings } from "@/app/(app)/(onboarding)/organizations/[organizationId]/products/new/settings/components/ProductSettings";
import { XIcon } from "lucide-react";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
+import { redirect } from "next/navigation";
+import { getRoleManagementPermission } from "@formbricks/ee/lib/service";
import { authOptions } from "@formbricks/lib/authOptions";
import { DEFAULT_BRAND_COLOR, DEFAULT_LOCALE } from "@formbricks/lib/constants";
-import { getProducts } from "@formbricks/lib/product/service";
+import { getOrganization } from "@formbricks/lib/organization/service";
+import { getUserProducts } from "@formbricks/lib/product/service";
import { getUserLocale } from "@formbricks/lib/user/service";
import { TProductConfigChannel, TProductConfigIndustry, TProductMode } from "@formbricks/types/product";
import { Button } from "@formbricks/ui/components/Button";
@@ -25,12 +29,31 @@ interface ProductSettingsPageProps {
const Page = async ({ params, searchParams }: ProductSettingsPageProps) => {
const t = await getTranslations();
const session = await getServerSession(authOptions);
+
+ if (!session || !session.user) {
+ return redirect(`/auth/login`);
+ }
+
const channel = searchParams.channel || null;
const industry = searchParams.industry || null;
const mode = searchParams.mode || "surveys";
const locale = session?.user.id ? await getUserLocale(session.user.id) : undefined;
const customHeadline = getCustomHeadline(channel);
- const products = await getProducts(params.organizationId);
+ const products = await getUserProducts(session.user.id, params.organizationId);
+
+ const organizationTeams = await getTeamsByOrganizationId(params.organizationId);
+
+ const organization = await getOrganization(params.organizationId);
+
+ if (!organization) {
+ throw new Error(t("common.organization_not_found"));
+ }
+
+ const canDoRoleManagement = await getRoleManagementPermission(organization);
+
+ if (!organizationTeams) {
+ throw new Error(t("common.organization_teams_not_found"));
+ }
return (
@@ -51,6 +74,8 @@ const Page = async ({ params, searchParams }: ProductSettingsPageProps) => {
channel={channel}
industry={industry}
defaultBrandColor={DEFAULT_BRAND_COLOR}
+ organizationTeams={organizationTeams}
+ canDoRoleManagement={canDoRoleManagement}
locale={locale ?? DEFAULT_LOCALE}
/>
{products.length >= 1 && (
diff --git a/apps/web/app/(app)/(onboarding)/organizations/actions.ts b/apps/web/app/(app)/(onboarding)/organizations/actions.ts
index ef445948c0..9d3c80439f 100644
--- a/apps/web/app/(app)/(onboarding)/organizations/actions.ts
+++ b/apps/web/app/(app)/(onboarding)/organizations/actions.ts
@@ -1,19 +1,19 @@
"use server";
+import { authenticatedActionClient } from "@/lib/utils/action-client";
+import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
+import { sendInviteMemberEmail } from "@/modules/email";
import { z } from "zod";
-import { sendInviteMemberEmail } from "@formbricks/email";
-import { authenticatedActionClient } from "@formbricks/lib/actionClient";
-import { checkAuthorization } from "@formbricks/lib/actionClient/utils";
import { INVITE_DISABLED } from "@formbricks/lib/constants";
import { inviteUser } from "@formbricks/lib/invite/service";
import { ZId } from "@formbricks/types/common";
import { AuthenticationError } from "@formbricks/types/errors";
-import { ZMembershipRole } from "@formbricks/types/memberships";
+import { ZOrganizationRole } from "@formbricks/types/memberships";
const ZInviteOrganizationMemberAction = z.object({
organizationId: ZId,
email: z.string(),
- role: ZMembershipRole,
+ role: ZOrganizationRole,
inviteMessage: z.string(),
});
@@ -24,10 +24,15 @@ export const inviteOrganizationMemberAction = authenticatedActionClient
throw new AuthenticationError("Invite disabled");
}
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: parsedInput.organizationId,
- rules: ["membership", "create"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ ],
});
const invite = await inviteUser({
diff --git a/apps/web/app/(app)/(onboarding)/types/onboarding.ts b/apps/web/app/(app)/(onboarding)/types/onboarding.ts
new file mode 100644
index 0000000000..ead024f3ba
--- /dev/null
+++ b/apps/web/app/(app)/(onboarding)/types/onboarding.ts
@@ -0,0 +1,8 @@
+import { z } from "zod";
+
+export const ZOrganizationTeam = z.object({
+ id: z.string().cuid2(),
+ name: z.string(),
+});
+
+export type TOrganizationTeam = z.infer;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts
index 4e26ebd800..26032f64bf 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts
@@ -1,16 +1,20 @@
"use server";
-import { z } from "zod";
-import { createActionClass } from "@formbricks/lib/actionClass/service";
-import { actionClient, authenticatedActionClient } from "@formbricks/lib/actionClient";
-import { checkAuthorization } from "@formbricks/lib/actionClient/utils";
-import { UNSPLASH_ACCESS_KEY, UNSPLASH_ALLOWED_DOMAINS } from "@formbricks/lib/constants";
+import { actionClient, authenticatedActionClient } from "@/lib/utils/action-client";
+import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
import {
getOrganizationIdFromEnvironmentId,
getOrganizationIdFromProductId,
getOrganizationIdFromSegmentId,
getOrganizationIdFromSurveyId,
-} from "@formbricks/lib/organization/utils";
+ getProductIdFromEnvironmentId,
+ getProductIdFromSegmentId,
+ getProductIdFromSurveyId,
+} from "@/lib/utils/helper";
+import { getSegment, getSurvey } from "@/lib/utils/services";
+import { z } from "zod";
+import { createActionClass } from "@formbricks/lib/actionClass/service";
+import { UNSPLASH_ACCESS_KEY, UNSPLASH_ALLOWED_DOMAINS } from "@formbricks/lib/constants";
import { getProduct } from "@formbricks/lib/product/service";
import {
cloneSegment,
@@ -28,11 +32,22 @@ import { ZSurvey } from "@formbricks/types/surveys/types";
export const updateSurveyAction = authenticatedActionClient
.schema(ZSurvey)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromSurveyId(parsedInput.id),
- rules: ["survey", "update"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ productId: await getProductIdFromSurveyId(parsedInput.id),
+ minPermission: "readWrite",
+ },
+ ],
});
+
return await updateSurvey(parsedInput);
});
@@ -43,10 +58,20 @@ const ZRefetchProductAction = z.object({
export const refetchProductAction = authenticatedActionClient
.schema(ZRefetchProductAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromProductId(parsedInput.productId),
- rules: ["product", "read"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: parsedInput.productId,
+ },
+ ],
});
return await getProduct(parsedInput.productId);
@@ -64,10 +89,30 @@ const ZCreateBasicSegmentAction = z.object({
export const createBasicSegmentAction = authenticatedActionClient
.schema(ZCreateBasicSegmentAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ const surveyEnvironment = await getSurvey(parsedInput.surveyId);
+
+ if (!surveyEnvironment) {
+ throw new Error("Survey not found");
+ }
+
+ if (surveyEnvironment.environmentId !== parsedInput.environmentId) {
+ throw new Error("Survey and segment are not in the same environment");
+ }
+
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
- organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId),
- rules: ["segment", "create"],
+ organizationId: await getOrganizationIdFromEnvironmentId(surveyEnvironment.environmentId),
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ },
+ ],
});
const parsedFilters = ZSegmentFilters.safeParse(parsedInput.filters);
@@ -99,10 +144,22 @@ const ZUpdateBasicSegmentAction = z.object({
export const updateBasicSegmentAction = authenticatedActionClient
.schema(ZUpdateBasicSegmentAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromSegmentId(parsedInput.segmentId),
- rules: ["segment", "update"],
+ access: [
+ {
+ schema: ZSegmentUpdateInput,
+ data: parsedInput.data,
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: await getProductIdFromSegmentId(parsedInput.segmentId),
+ },
+ ],
});
const { filters } = parsedInput.data;
@@ -127,16 +184,36 @@ const ZLoadNewBasicSegmentAction = z.object({
export const loadNewBasicSegmentAction = authenticatedActionClient
.schema(ZLoadNewBasicSegmentAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
- userId: ctx.user.id,
- organizationId: await getOrganizationIdFromSegmentId(parsedInput.surveyId),
- rules: ["segment", "read"],
- });
+ const surveyEnvironment = await getSurvey(parsedInput.surveyId);
+ const segmentEnvironment = await getSegment(parsedInput.segmentId);
- await checkAuthorization({
+ if (!surveyEnvironment || !segmentEnvironment) {
+ if (!surveyEnvironment) {
+ throw new Error("Survey not found");
+ }
+ if (!segmentEnvironment) {
+ throw new Error("Segment not found");
+ }
+ }
+
+ if (surveyEnvironment.environmentId !== segmentEnvironment.environmentId) {
+ throw new Error("Segment and survey are not in the same environment");
+ }
+
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromSurveyId(parsedInput.surveyId),
- rules: ["survey", "update"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ },
+ ],
});
return await loadNewSegmentInSurvey(parsedInput.surveyId, parsedInput.segmentId);
@@ -150,16 +227,36 @@ const ZCloneBasicSegmentAction = z.object({
export const cloneBasicSegmentAction = authenticatedActionClient
.schema(ZCloneBasicSegmentAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
- userId: ctx.user.id,
- organizationId: await getOrganizationIdFromSegmentId(parsedInput.segmentId),
- rules: ["segment", "create"],
- });
+ const surveyEnvironment = await getSurvey(parsedInput.surveyId);
+ const segmentEnvironment = await getSegment(parsedInput.segmentId);
- await checkAuthorization({
+ if (!surveyEnvironment || !segmentEnvironment) {
+ if (!surveyEnvironment) {
+ throw new Error("Survey not found");
+ }
+ if (!segmentEnvironment) {
+ throw new Error("Segment not found");
+ }
+ }
+
+ if (surveyEnvironment.environmentId !== segmentEnvironment.environmentId) {
+ throw new Error("Segment and survey are not in the same environment");
+ }
+
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromSurveyId(parsedInput.surveyId),
- rules: ["survey", "read"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ },
+ ],
});
return await cloneSegment(parsedInput.segmentId, parsedInput.surveyId);
@@ -172,10 +269,20 @@ const ZResetBasicSegmentFiltersAction = z.object({
export const resetBasicSegmentFiltersAction = authenticatedActionClient
.schema(ZResetBasicSegmentFiltersAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromSurveyId(parsedInput.surveyId),
- rules: ["segment", "update"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: await getProductIdFromSurveyId(parsedInput.surveyId),
+ },
+ ],
});
return await resetSegmentInSurvey(parsedInput.surveyId);
@@ -267,10 +374,20 @@ const ZCreateActionClassAction = z.object({
export const createActionClassAction = authenticatedActionClient
.schema(ZCreateActionClassAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.action.environmentId),
- rules: ["actionClass", "create"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ minPermission: "readWrite",
+ productId: await getProductIdFromEnvironmentId(parsedInput.action.environmentId),
+ },
+ ],
});
return await createActionClass(parsedInput.action.environmentId, parsedInput.action);
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddActionModal.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddActionModal.tsx
index 8e9bbf0037..fe04c7b3f9 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddActionModal.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddActionModal.tsx
@@ -13,7 +13,7 @@ interface AddActionModalProps {
environmentId: string;
actionClasses: TActionClass[];
setActionClasses: React.Dispatch>;
- isViewer: boolean;
+ isReadOnly: boolean;
localSurvey: TSurvey;
setLocalSurvey: React.Dispatch>;
}
@@ -25,7 +25,7 @@ export const AddActionModal = ({
setActionClasses,
localSurvey,
setLocalSurvey,
- isViewer,
+ isReadOnly,
environmentId,
}: AddActionModalProps) => {
const t = useTranslations();
@@ -48,7 +48,7 @@ export const AddActionModal = ({
actionClasses={actionClasses}
setActionClasses={setActionClasses}
setOpen={setOpen}
- isViewer={isViewer}
+ isReadOnly={isReadOnly}
setLocalSurvey={setLocalSurvey}
environmentId={environmentId}
/>
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddressQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddressQuestionForm.tsx
index f0824a7d47..a13787af49 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddressQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddressQuestionForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -9,7 +10,6 @@ import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyAddressQuestion } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { QuestionToggleTable } from "@formbricks/ui/components/QuestionToggleTable";
interface AddressQuestionFormProps {
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CTAQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CTAQuestionForm.tsx
index 8bb1f9fdc2..59a116acd7 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CTAQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CTAQuestionForm.tsx
@@ -1,16 +1,16 @@
"use client";
+import { LocalizedEditor } from "@/modules/ee/multi-language-surveys/components/localized-editor";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useTranslations } from "next-intl";
import { useState } from "react";
-import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyCTAQuestion } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
const options = [
{
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CalQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CalQuestionForm.tsx
index 3b93846eda..e5c6d0db5e 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CalQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CalQuestionForm.tsx
@@ -1,3 +1,4 @@
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
@@ -9,7 +10,6 @@ import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionTo
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
interface CalQuestionFormProps {
localSurvey: TSurvey;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ConsentQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ConsentQuestionForm.tsx
index 66074f61f9..a126b29f26 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ConsentQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ConsentQuestionForm.tsx
@@ -1,13 +1,13 @@
"use client";
+import { LocalizedEditor } from "@/modules/ee/multi-language-surveys/components/localized-editor";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useTranslations } from "next-intl";
import { useState } from "react";
-import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyConsentQuestion } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
interface ConsentQuestionFormProps {
localSurvey: TSurvey;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ContactInfoQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ContactInfoQuestionForm.tsx
index 58f0bf9f04..0b37f18da9 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ContactInfoQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ContactInfoQuestionForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -9,7 +10,6 @@ import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyContactInfoQuestion } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { QuestionToggleTable } from "@formbricks/ui/components/QuestionToggleTable";
interface ContactInfoQuestionFormProps {
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CreateNewActionTab.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CreateNewActionTab.tsx
index 37b864d7d6..2ed652adb6 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CreateNewActionTab.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CreateNewActionTab.tsx
@@ -24,7 +24,7 @@ import { createActionClassAction } from "../actions";
interface CreateNewActionTabProps {
actionClasses: TActionClass[];
setActionClasses: React.Dispatch>;
- isViewer: boolean;
+ isReadOnly: boolean;
setLocalSurvey?: React.Dispatch>;
setOpen: React.Dispatch>;
environmentId: string;
@@ -34,7 +34,7 @@ export const CreateNewActionTab = ({
actionClasses,
setActionClasses,
setOpen,
- isViewer,
+ isReadOnly,
setLocalSurvey,
environmentId,
}: CreateNewActionTabProps) => {
@@ -87,7 +87,7 @@ export const CreateNewActionTab = ({
const submitHandler = async (data: TActionClassInput) => {
const { type } = data;
try {
- if (isViewer) {
+ if (isReadOnly) {
throw new Error(t("common.you_are_not_authorised_to_perform_this_action"));
}
@@ -247,9 +247,9 @@ export const CreateNewActionTab = ({
{watch("type") === "code" ? (
-
+
) : (
-
+
)}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/DateQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/DateQuestionForm.tsx
index bef9433032..15c1a1433d 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/DateQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/DateQuestionForm.tsx
@@ -1,3 +1,4 @@
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -8,7 +9,6 @@ import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
interface IDateQuestionFormProps {
localSurvey: TSurvey;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx
index 96c0edfef6..6efa36edfe 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx
@@ -1,18 +1,18 @@
"use client";
+import { LocalizedEditor } from "@/modules/ee/multi-language-surveys/components/localized-editor";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import * as Collapsible from "@radix-ui/react-collapsible";
import { Hand } from "lucide-react";
import { useTranslations } from "next-intl";
import { usePathname } from "next/navigation";
import { useState } from "react";
-import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
import { cn } from "@formbricks/lib/cn";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyQuestionId, TSurveyWelcomeCard } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { FileInput } from "@formbricks/ui/components/FileInput";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
interface EditWelcomeCardProps {
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EndScreenForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EndScreenForm.tsx
index d705ced5fb..9c42f77d32 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EndScreenForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EndScreenForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
@@ -8,7 +9,6 @@ import { TSurvey, TSurveyEndScreenCard } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
interface EndScreenFormProps {
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/FileUploadQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/FileUploadQuestionForm.tsx
index 804d13fda3..184c49c388 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/FileUploadQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/FileUploadQuestionForm.tsx
@@ -1,5 +1,7 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
+import { useGetBillingInfo } from "@/modules/utils/hooks/useGetBillingInfo";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { PlusIcon, XCircleIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -8,7 +10,6 @@ import { useMemo, useState } from "react";
import { toast } from "react-hot-toast";
import { extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { createI18nString } from "@formbricks/lib/i18n/utils";
-import { useGetBillingInfo } from "@formbricks/lib/organization/hooks/useGetBillingInfo";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TAllowedFileExtension, ZAllowedFileExtension } from "@formbricks/types/common";
import { TProduct } from "@formbricks/types/product";
@@ -17,7 +18,6 @@ import { TUserLocale } from "@formbricks/types/user";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
interface FileUploadFormProps {
localSurvey: TSurvey;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
index 11872f1832..33cf6a13f2 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { PlusIcon, TrashIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -9,7 +10,6 @@ import { TI18nString, TSurvey, TSurveyMatrixQuestion } from "@formbricks/types/s
import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { ShuffleOptionSelect } from "@formbricks/ui/components/ShuffleOptionSelect";
import { isLabelValidForAllLanguages } from "../lib/validation";
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx
index eab1d700e6..ec35f363e6 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MultipleChoiceQuestionForm.tsx
@@ -1,6 +1,7 @@
"use client";
import { findOptionUsedInLogic } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { DndContext } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { useAutoAnimate } from "@formkit/auto-animate/react";
@@ -21,7 +22,6 @@ import {
import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { ShuffleOptionSelect } from "@formbricks/ui/components/ShuffleOptionSelect";
import { QuestionOptionChoice } from "./QuestionOptionChoice";
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/NPSQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/NPSQuestionForm.tsx
index 47b257e5da..08152c120f 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/NPSQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/NPSQuestionForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -9,7 +10,6 @@ import { TSurvey, TSurveyNPSQuestion } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
interface NPSQuestionFormProps {
localSurvey: TSurvey;
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/OpenQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/OpenQuestionForm.tsx
index 8d08b3d4d9..71789f69be 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/OpenQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/OpenQuestionForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { HashIcon, LinkIcon, MailIcon, MessageSquareTextIcon, PhoneIcon, PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -14,7 +15,6 @@ import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
const questionTypes = [
{ value: "text", label: "common.text", icon: },
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/PictureSelectionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/PictureSelectionForm.tsx
index eeac9c7817..1e1d8e7f09 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/PictureSelectionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/PictureSelectionForm.tsx
@@ -1,3 +1,4 @@
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { createId } from "@paralleldrive/cuid2";
import { PlusIcon } from "lucide-react";
@@ -10,7 +11,6 @@ import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
import { FileInput } from "@formbricks/ui/components/FileInput";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
interface PictureSelectionFormProps {
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 8ec5de23c0..c358fc95b1 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
@@ -3,6 +3,7 @@
import { ContactInfoQuestionForm } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ContactInfoQuestionForm";
import { RankingQuestionForm } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm";
import { formatTextWithSlashes } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useAutoAnimate } from "@formkit/auto-animate/react";
@@ -24,7 +25,6 @@ import {
} from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
import { AddressQuestionForm } from "./AddressQuestionForm";
import { AdvancedSettings } from "./AdvancedSettings";
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionOptionChoice.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionOptionChoice.tsx
index 54f9c7d344..fcf2f93ee7 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionOptionChoice.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionOptionChoice.tsx
@@ -1,3 +1,4 @@
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { GripVerticalIcon, PlusIcon, TrashIcon } from "lucide-react";
@@ -14,7 +15,6 @@ import {
TSurveyRankingQuestion,
} from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { isLabelValidForAllLanguages } from "../lib/validation";
interface ChoiceProps {
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx
index 20bf96310e..187fb42e93 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx
@@ -3,6 +3,7 @@
import { AddEndingCardButton } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddEndingCardButton";
import { SurveyVariablesCard } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCard";
import { findQuestionUsedInLogic } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
+import { MultiLanguageCard } from "@/modules/ee/multi-language-surveys/components/multi-language-card";
import {
DndContext,
DragEndEvent,
@@ -17,7 +18,6 @@ import { createId } from "@paralleldrive/cuid2";
import { useTranslations } from "next-intl";
import React, { SetStateAction, useEffect, useMemo } from "react";
import toast from "react-hot-toast";
-import { MultiLanguageCard } from "@formbricks/ee/multi-language/components/multi-language-card";
import { addMultiLanguageLabels, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
import { isConditionGroup } from "@formbricks/lib/surveyLogic/utils";
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx
index d27111c731..69c539e748 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm.tsx
@@ -1,5 +1,6 @@
"use client";
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { DndContext } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { useAutoAnimate } from "@formkit/auto-animate/react";
@@ -13,7 +14,6 @@ import { TI18nString, TSurvey, TSurveyRankingQuestion } from "@formbricks/types/
import { TUserLocale } from "@formbricks/types/user";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { ShuffleOptionSelect } from "@formbricks/ui/components/ShuffleOptionSelect";
import { QuestionOptionChoice } from "./QuestionOptionChoice";
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RatingQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RatingQuestionForm.tsx
index 164952a63a..5d69a93a91 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RatingQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RatingQuestionForm.tsx
@@ -1,3 +1,4 @@
+import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { HashIcon, PlusIcon, SmileIcon, StarIcon } from "lucide-react";
import { useTranslations } from "next-intl";
@@ -8,7 +9,6 @@ import { TUserLocale } from "@formbricks/types/user";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
-import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Dropdown } from "./RatingTypeDropdown";
interface RatingQuestionFormProps {
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 c1da9a5707..ac06994bd5 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,8 +1,9 @@
-import { AdvancedTargetingCard } from "@formbricks/ee/advanced-targeting/components/advanced-targeting-card";
+import { AdvancedTargetingCard } from "@/modules/ee/advanced-targeting/components/advanced-targeting-card";
+import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
import { TActionClass } from "@formbricks/types/action-classes";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TEnvironment } from "@formbricks/types/environment";
-import { TMembershipRole } from "@formbricks/types/memberships";
+import { TOrganizationRole } from "@formbricks/types/memberships";
import { TSegment } from "@formbricks/types/segment";
import { TSurvey } from "@formbricks/types/surveys/types";
import { HowToSendCard } from "./HowToSendCard";
@@ -20,10 +21,11 @@ interface SettingsViewProps {
attributeClasses: TAttributeClass[];
segments: TSegment[];
responseCount: number;
- membershipRole?: TMembershipRole;
+ membershipRole?: TOrganizationRole;
isUserTargetingAllowed?: boolean;
isFormbricksCloud: boolean;
locale: string;
+ productPermission: TTeamPermission | null;
}
export const SettingsView = ({
@@ -38,6 +40,7 @@ export const SettingsView = ({
isUserTargetingAllowed = false,
isFormbricksCloud,
locale,
+ productPermission,
}: SettingsViewProps) => {
const isAppSurvey = localSurvey.type === "app";
@@ -83,6 +86,7 @@ export const SettingsView = ({
environmentId={environment.id}
propActionClasses={actionClasses}
membershipRole={membershipRole}
+ productPermission={productPermission}
/>
{
const [activeView, setActiveView] = useState("questions");
const [activeQuestionId, setActiveQuestionId] = useState(null);
@@ -209,6 +212,7 @@ export const SurveyEditor = ({
isUserTargetingAllowed={isUserTargetingAllowed}
isFormbricksCloud={isFormbricksCloud}
locale={locale}
+ productPermission={productPermission}
/>
)}
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyMenuBar.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyMenuBar.tsx
index fe4bb9b485..04685a5ea2 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyMenuBar.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyMenuBar.tsx
@@ -1,14 +1,14 @@
"use client";
import { SurveyStatusDropdown } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SurveyStatusDropdown";
+import { getFormattedErrorMessage } from "@/lib/utils/helper";
+import { createSegmentAction } from "@/modules/ee/advanced-targeting/lib/actions";
import { isEqual } from "lodash";
import { AlertTriangleIcon, ArrowLeftIcon, SettingsIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import toast from "react-hot-toast";
-import { createSegmentAction } from "@formbricks/ee/advanced-targeting/lib/actions";
-import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { getLanguageLabel } from "@formbricks/lib/i18n/utils";
import { TEnvironment } from "@formbricks/types/environment";
import { TProduct } from "@formbricks/types/product";
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/TargetingCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/TargetingCard.tsx
index 3d326dc2f9..a2cce7e507 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/TargetingCard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/TargetingCard.tsx
@@ -1,5 +1,6 @@
"use client";
+import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import * as Collapsible from "@radix-ui/react-collapsible";
import { AlertCircle, CheckIcon, ChevronDownIcon, ChevronUpIcon, PencilIcon } from "lucide-react";
@@ -8,7 +9,6 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { toast } from "react-hot-toast";
-import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { cn } from "@formbricks/lib/cn";
import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
import { isAdvancedSegment } from "@formbricks/lib/segment/utils";
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 c81f5032c3..0a69c4944d 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,5 +1,7 @@
"use client";
+import { TTeamPermission } from "@/modules/ee/teams/product-teams/types/teams";
+import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import * as Collapsible from "@radix-ui/react-collapsible";
import {
@@ -14,7 +16,7 @@ import { useTranslations } from "next-intl";
import { useEffect, useMemo, useState } from "react";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { TActionClass } from "@formbricks/types/action-classes";
-import { TMembershipRole } from "@formbricks/types/memberships";
+import { TOrganizationRole } from "@formbricks/types/memberships";
import { TSurvey } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
@@ -26,7 +28,8 @@ interface WhenToSendCardProps {
setLocalSurvey: React.Dispatch>;
environmentId: string;
propActionClasses: TActionClass[];
- membershipRole?: TMembershipRole;
+ membershipRole?: TOrganizationRole;
+ productPermission: TTeamPermission | null;
}
export const WhenToSendCard = ({
@@ -35,6 +38,7 @@ export const WhenToSendCard = ({
setLocalSurvey,
propActionClasses,
membershipRole,
+ productPermission,
}: WhenToSendCardProps) => {
const t = useTranslations();
const [open, setOpen] = useState(localSurvey.type === "app" ? true : false);
@@ -42,7 +46,10 @@ export const WhenToSendCard = ({
const [actionClasses, setActionClasses] = useState(propActionClasses);
const [randomizerToggle, setRandomizerToggle] = useState(localSurvey.displayPercentage ? true : false);
- const { isViewer } = getAccessFlags(membershipRole);
+ const { isMember } = getAccessFlags(membershipRole);
+ const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+
+ const isReadOnly = isMember && hasReadAccess;
const autoClose = localSurvey.autoClose !== null;
const delay = localSurvey.delay !== 0;
@@ -367,7 +374,7 @@ export const WhenToSendCard = ({
setOpen={setAddActionModalOpen}
actionClasses={actionClasses}
setActionClasses={setActionClasses}
- isViewer={isViewer}
+ isReadOnly={isReadOnly}
localSurvey={localSurvey}
setLocalSurvey={setLocalSurvey}
/>
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 065835dd5e..f3f573d0eb 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
@@ -1,3 +1,5 @@
+import { getProductPermissionByUserId } 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";
import { getAdvancedTargetingPermission, getMultiLanguagePermission } from "@formbricks/ee/lib/service";
@@ -60,10 +62,20 @@ const Page = async ({ params, searchParams }) => {
throw new Error(t("common.organization_not_found"));
}
+ if (!product) {
+ throw new Error(t("common.product_not_found"));
+ }
+
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
- const { isViewer } = getAccessFlags(currentUserMembership?.role);
- const isSurveyCreationDeletionDisabled = isViewer;
+ const { isMember } = getAccessFlags(currentUserMembership?.role);
+
+ const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
+
+ const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+
+ const isSurveyCreationDeletionDisabled = isMember && hasReadAccess;
const locale = session.user.id ? await getUserLocale(session.user.id) : undefined;
+
const isUserTargetingAllowed = await getAdvancedTargetingPermission(organization);
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization);
@@ -89,6 +101,7 @@ const Page = async ({ params, searchParams }) => {
attributeClasses={attributeClasses}
responseCount={responseCount}
membershipRole={currentUserMembership?.role}
+ productPermission={productPermission}
colors={SURVEY_BG_COLORS}
segments={segments}
isUserTargetingAllowed={isUserTargetingAllowed}
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 2ddb015d13..bd4cb89f12 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
@@ -1,13 +1,13 @@
"use server";
+import { authenticatedActionClient } from "@/lib/utils/action-client";
+import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
+import { getOrganizationIdFromEnvironmentId, getProductIdFromEnvironmentId } from "@/lib/utils/helper";
import { createId } from "@paralleldrive/cuid2";
import { generateObject } from "ai";
import { z } from "zod";
-import { authenticatedActionClient } from "@formbricks/lib/actionClient";
-import { checkAuthorization } from "@formbricks/lib/actionClient/utils";
import { llmModel } from "@formbricks/lib/aiModels";
import { getOrganization } from "@formbricks/lib/organization/service";
-import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils";
import { createSurvey } from "@formbricks/lib/survey/service";
import { getIsAIEnabled } from "@formbricks/lib/utils/ai";
import { ZId, ZString } from "@formbricks/types/common";
@@ -23,10 +23,20 @@ export const createAISurveyAction = authenticatedActionClient
.action(async ({ ctx, parsedInput }) => {
const organizationId = await getOrganizationIdFromEnvironmentId(parsedInput.environmentId);
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId,
- rules: ["survey", "create"],
+ access: [
+ {
+ type: "organization",
+ roles: ["owner", "manager"],
+ },
+ {
+ type: "productTeam",
+ productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ minPermission: "readWrite",
+ },
+ ],
});
const organization = await getOrganization(organizationId);
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/FormbricksAICard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/FormbricksAICard.tsx
index 20fb860277..3e0747898a 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/FormbricksAICard.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/FormbricksAICard.tsx
@@ -1,12 +1,12 @@
"use client";
import { createAISurveyAction } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/actions";
+import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { Sparkles } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
-import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { Button } from "@formbricks/ui/components/Button";
import {
Card,
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 623b83f2a3..17d4118cca 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
@@ -2,6 +2,7 @@
import { FormbricksAICard } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/FormbricksAICard";
import { MenuBar } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/MenuBar";
+import { TemplateList } from "@/modules/surveys/components/TemplateList";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { getCustomSurveyTemplate } from "@formbricks/lib/templates";
@@ -12,7 +13,6 @@ import { TUser } from "@formbricks/types/user";
import { PreviewSurvey } from "@formbricks/ui/components/PreviewSurvey";
import { SearchBar } from "@formbricks/ui/components/SearchBar";
import { Separator } from "@formbricks/ui/components/Separator";
-import { TemplateList } from "@formbricks/ui/components/TemplateList";
import { getMinimalSurvey } from "../../lib/minimalSurvey";
type TemplateContainerWithPreviewProps = {
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 6b284ac13e..36759c0ad0 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,3 +1,5 @@
+import { getProductPermissionByUserId } 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";
import { redirect } from "next/navigation";
@@ -52,9 +54,13 @@ const Page = async ({ params, searchParams }: SurveyTemplateProps) => {
session?.user.id,
product.organizationId
);
- const { isViewer } = getAccessFlags(currentUserMembership?.role);
+ const { isMember } = getAccessFlags(currentUserMembership?.role);
- if (isViewer) {
+ const productPermission = await getProductPermissionByUserId(session.user.id, product.id);
+ const { hasReadAccess } = getTeamPermissionFlags(productPermission);
+
+ const isReadOnly = isMember && hasReadAccess;
+ if (isReadOnly) {
return redirect(`/environments/${environment.id}/surveys`);
}
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts
index 54ae660c1b..6c44944024 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts
@@ -1,9 +1,9 @@
"use server";
+import { authenticatedActionClient } from "@/lib/utils/action-client";
+import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
+import { getOrganizationIdFromEnvironmentId, getProductIdFromEnvironmentId } from "@/lib/utils/helper";
import { z } from "zod";
-import { authenticatedActionClient } from "@formbricks/lib/actionClient";
-import { checkAuthorization } from "@formbricks/lib/actionClient/utils";
-import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils";
import { getSegmentsByAttributeClassName } from "@formbricks/lib/segment/service";
import { ZAttributeClass } from "@formbricks/types/attribute-classes";
import { ZId } from "@formbricks/types/common";
@@ -16,10 +16,15 @@ const ZGetSegmentsByAttributeClassAction = z.object({
export const getSegmentsByAttributeClassAction = authenticatedActionClient
.schema(ZGetSegmentsByAttributeClassAction)
.action(async ({ ctx, parsedInput }) => {
- await checkAuthorization({
+ await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId),
- rules: ["environment", "read"],
+ access: [
+ {
+ type: "productTeam",
+ productId: await getProductIdFromEnvironmentId(parsedInput.environmentId),
+ },
+ ],
});
const segments = await getSegmentsByAttributeClassName(
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeActivityTab.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeActivityTab.tsx
index 4131c00053..d76e6be6a9 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeActivityTab.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeActivityTab.tsx
@@ -1,9 +1,9 @@
"use client";
+import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { TagIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
-import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { convertDateTimeStringShort } from "@formbricks/lib/time";
import { capitalizeFirstLetter } from "@formbricks/lib/utils/strings";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeClassesTable.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeClassesTable.tsx
index da979ff4f3..e1ff46aac3 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeClassesTable.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeClassesTable.tsx
@@ -11,9 +11,14 @@ import { AttributeClassDataRow } from "./AttributeRowData";
interface AttributeClassesTableProps {
attributeClasses: TAttributeClass[];
locale: TUserLocale;
+ isReadOnly: boolean;
}
-export const AttributeClassesTable = ({ attributeClasses, locale }: AttributeClassesTableProps) => {
+export const AttributeClassesTable = ({
+ attributeClasses,
+ locale,
+ isReadOnly,
+}: AttributeClassesTableProps) => {
const [isAttributeDetailModalOpen, setAttributeDetailModalOpen] = useState(false);
const [activeAttributeClass, setActiveAttributeClass] = useState(null);
const [showArchived, setShowArchived] = useState(false);
@@ -70,6 +75,7 @@ export const AttributeClassesTable = ({ attributeClasses, locale }: AttributeCla
open={isAttributeDetailModalOpen}
setOpen={setAttributeDetailModalOpen}
attributeClass={activeAttributeClass}
+ isReadOnly={isReadOnly}
/>
)}
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeDetailModal.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeDetailModal.tsx
index 1599b2fb53..450c4ad4f3 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeDetailModal.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeDetailModal.tsx
@@ -11,9 +11,15 @@ interface AttributeDetailModalProps {
open: boolean;
setOpen: (v: boolean) => void;
attributeClass: TAttributeClass;
+ isReadOnly: boolean;
}
-export const AttributeDetailModal = ({ open, setOpen, attributeClass }: AttributeDetailModalProps) => {
+export const AttributeDetailModal = ({
+ open,
+ setOpen,
+ attributeClass,
+ isReadOnly,
+}: AttributeDetailModalProps) => {
const t = useTranslations();
const tabs = [
{
@@ -22,7 +28,9 @@ export const AttributeDetailModal = ({ open, setOpen, attributeClass }: Attribut
},
{
title: t("common.settings"),
- children:
,
+ children: (
+
+ ),
},
];
diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeSettingsTab.tsx b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeSettingsTab.tsx
index bbb9e0a846..3e6d6eb02f 100644
--- a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeSettingsTab.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/components/AttributeSettingsTab.tsx
@@ -14,9 +14,14 @@ import { Label } from "@formbricks/ui/components/Label";
interface AttributeSettingsTabProps {
attributeClass: AttributeClass;
setOpen: (v: boolean) => void;
+ isReadOnly: boolean;
}
-export const AttributeSettingsTab = async ({ attributeClass, setOpen }: AttributeSettingsTabProps) => {
+export const AttributeSettingsTab = async ({
+ attributeClass,
+ setOpen,
+ isReadOnly,
+}: AttributeSettingsTabProps) => {
const router = useRouter();
const t = useTranslations();
const { register, handleSubmit } = useForm({
@@ -58,7 +63,7 @@ export const AttributeSettingsTab = async ({ attributeClass, setOpen }: Attribut
type="text"
placeholder={t("environments.attributes.ex_user_property")}
{...register("description", {
- disabled: attributeClass.type === "automatic" ? true : false,
+ disabled: attributeClass.type === "automatic" || isReadOnly ? true : false,
})}
/>
@@ -85,24 +90,22 @@ export const AttributeSettingsTab = async ({ attributeClass, setOpen }: Attribut
{t("common.read_docs")}
{attributeClass.type !== "automatic" && (
-