diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/components/EditBranding.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/components/EditBranding.tsx new file mode 100644 index 0000000000..a7615e5bb0 --- /dev/null +++ b/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/components/EditBranding.tsx @@ -0,0 +1,83 @@ +"use client"; + +import { TProduct, TProductUpdateInput } from "@formbricks/types/product"; +import { Alert, AlertDescription } from "@formbricks/ui/Alert"; +import { Label } from "@formbricks/ui/Label"; +import { Switch } from "@formbricks/ui/Switch"; +import Link from "next/link"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { updateProductAction } from "../actions"; + +interface EditFormbricksBrandingProps { + type: "linkSurvey" | "inAppSurvey"; + product: TProduct; + canRemoveBranding: boolean; + environmentId: string; +} + +export function EditFormbricksBranding({ + type, + product, + canRemoveBranding, + environmentId, +}: EditFormbricksBrandingProps) { + const [isBrandingEnabled, setIsBrandingEnabled] = useState( + type === "linkSurvey" ? product.linkSurveyBranding : product.inAppSurveyBranding + ); + const [updatingBranding, setUpdatingBranding] = useState(false); + + const toggleBranding = async () => { + try { + setUpdatingBranding(true); + const newBrandingState = !isBrandingEnabled; + setIsBrandingEnabled(newBrandingState); + let inputProduct: Partial = { + [type === "linkSurvey" ? "linkSurveyBranding" : "inAppSurveyBranding"]: newBrandingState, + }; + await updateProductAction(product.id, inputProduct); + toast.success( + newBrandingState ? "Formbricks branding will be shown." : "Formbricks branding will now be hidden." + ); + } catch (error) { + toast.error(`Error: ${error.message}`); + } finally { + setUpdatingBranding(false); + } + }; + + return ( +
+ {!canRemoveBranding && ( +
+ + + To remove the Formbricks branding from the {type} surveys + , please{" "} + {type === "linkSurvey" ? ( + + upgrade your plan. + + ) : ( + + add your creditcard. + + )} + + +
+ )} +
+ + +
+
+ ); +} diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/components/EditSignature.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/components/EditSignature.tsx deleted file mode 100644 index aadcdd78a2..0000000000 --- a/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/components/EditSignature.tsx +++ /dev/null @@ -1,67 +0,0 @@ -"use client"; - -import { Alert, AlertDescription } from "@formbricks/ui/Alert"; -import { updateProductAction } from "../actions"; -import { TProduct, TProductUpdateInput } from "@formbricks/types/product"; -import { Label } from "@formbricks/ui/Label"; -import { Switch } from "@formbricks/ui/Switch"; -import { useState } from "react"; -import toast from "react-hot-toast"; -import Link from "next/link"; - -interface EditSignatureProps { - product: TProduct; - canRemoveSignature: boolean; - environmentId: string; -} - -export function EditFormbricksSignature({ product, canRemoveSignature, environmentId }: EditSignatureProps) { - const [formbricksSignature, setFormbricksSignature] = useState(product.formbricksSignature); - const [updatingSignature, setUpdatingSignature] = useState(false); - - const toggleSignature = async () => { - try { - setUpdatingSignature(true); - const newSignatureState = !formbricksSignature; - setFormbricksSignature(newSignatureState); - let inputProduct: Partial = { - formbricksSignature: newSignatureState, - }; - await updateProductAction(product.id, inputProduct); - toast.success( - newSignatureState ? "Formbricks signature will be shown." : "Formbricks signature will now be hidden." - ); - } catch (error) { - toast.error(`Error: ${error.message}`); - } finally { - setUpdatingSignature(false); - } - }; - - return ( -
- {!canRemoveSignature && ( -
- - - To remove the Formbricks branding from the link surveys, please{" "} - - upgrade - {" "} - your plan. - - -
- )} -
- - -
-
- ); -} diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/page.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/page.tsx index 4cb46b599a..f764d0e541 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/page.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/settings/lookandfeel/page.tsx @@ -1,20 +1,19 @@ export const revalidate = REVALIDATION_INTERVAL; -import { getProductByEnvironmentId } from "@formbricks/lib/product/service"; -import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants"; -import SettingsCard from "../components/SettingsCard"; -import SettingsTitle from "../components/SettingsTitle"; -import { EditFormbricksSignature } from "./components/EditSignature"; -import { EditBrandColor } from "./components/EditBrandColor"; -import { EditPlacement } from "./components/EditPlacement"; -import { EditHighlightBorder } from "./components/EditHighlightBorder"; -import { DEFAULT_BRAND_COLOR } from "@formbricks/lib/constants"; import { authOptions } from "@formbricks/lib/authOptions"; -import { getServerSession } from "next-auth"; -import { getTeamByEnvironmentId } from "@formbricks/lib/team/service"; +import { DEFAULT_BRAND_COLOR, REVALIDATION_INTERVAL } from "@formbricks/lib/constants"; import { getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service"; import { getAccessFlags } from "@formbricks/lib/membership/utils"; +import { getProductByEnvironmentId } from "@formbricks/lib/product/service"; +import { getTeamByEnvironmentId } from "@formbricks/lib/team/service"; import { ErrorComponent } from "@formbricks/ui/ErrorComponent"; +import { getServerSession } from "next-auth"; +import SettingsCard from "../components/SettingsCard"; +import SettingsTitle from "../components/SettingsTitle"; +import { EditBrandColor } from "./components/EditBrandColor"; +import { EditHighlightBorder } from "./components/EditHighlightBorder"; +import { EditPlacement } from "./components/EditPlacement"; +import { EditFormbricksBranding } from "./components/EditBranding"; export default async function ProfileSettingsPage({ params }: { params: { environmentId: string } }) { const [session, team, product] = await Promise.all([ @@ -33,8 +32,10 @@ export default async function ProfileSettingsPage({ params }: { params: { enviro throw new Error("Team not found"); } + const canRemoveLinkBranding = team.billing.features.linkSurvey.status !== "inactive"; + const canRemoveInAppBranding = team.billing.features.inAppSurvey.status !== "inactive"; + const currentUserMembership = await getMembershipByUserIdTeamId(session?.user.id, team.id); - const canRemoveSignature = team.billing.features.linkSurvey.status !== "inactive"; const { isDeveloper, isViewer } = getAccessFlags(currentUserMembership?.role); const isBrandColorEditDisabled = isDeveloper ? true : isViewer; @@ -68,11 +69,18 @@ export default async function ProfileSettingsPage({ params }: { params: { enviro /> - + diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/LinkTab.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/LinkTab.tsx index d2eabca2db..091bfc5512 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/LinkTab.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/LinkTab.tsx @@ -56,7 +56,7 @@ export default function LinkTab({ surveyUrl, survey, brandColor }: EmailTabProps @@ -219,7 +219,7 @@ export default function PreviewSurvey({ survey={survey} brandColor={brandColor} activeQuestionId={activeQuestionId || undefined} - formbricksSignature={product.formbricksSignature} + isBrandingEnabled={product.linkSurveyBranding} onActiveQuestionChange={setActiveQuestionId} /> @@ -274,7 +274,7 @@ export default function PreviewSurvey({ survey={survey} brandColor={brandColor} activeQuestionId={activeQuestionId || undefined} - formbricksSignature={product.formbricksSignature} + isBrandingEnabled={product.linkSurveyBranding} onActiveQuestionChange={setActiveQuestionId} isRedirectDisabled={true} /> @@ -287,7 +287,7 @@ export default function PreviewSurvey({ survey={survey} brandColor={brandColor} activeQuestionId={activeQuestionId || undefined} - formbricksSignature={product.formbricksSignature} + isBrandingEnabled={product.linkSurveyBranding} onActiveQuestionChange={setActiveQuestionId} isRedirectDisabled={true} /> diff --git a/apps/web/app/lib/api/clientSettings.ts b/apps/web/app/lib/api/clientSettings.ts index ffd84cd9de..a1d2e4d3e9 100644 --- a/apps/web/app/lib/api/clientSettings.ts +++ b/apps/web/app/lib/api/clientSettings.ts @@ -208,7 +208,7 @@ export const getSettings = async (environmentId: string, personId: string): Prom product: { select: { brandColor: true, - formbricksSignature: true, + linkSurveyBranding: true, placement: true, darkOverlay: true, clickOutsideClose: true, @@ -217,7 +217,7 @@ export const getSettings = async (environmentId: string, personId: string): Prom }, }); - const formbricksSignature = environmentProdut?.product.formbricksSignature; + const formbricksSignature = environmentProdut?.product.linkSurveyBranding; const brandColor = environmentProdut?.product.brandColor; const placement = environmentProdut?.product.placement; const darkOverlay = environmentProdut?.product.darkOverlay; diff --git a/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx b/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx index 92b906cbd2..a1c42e326b 100644 --- a/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx +++ b/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx @@ -136,7 +136,7 @@ export default function LinkSurvey({ { if (!isPreview) { const api = new FormbricksAPI({ diff --git a/apps/web/pages/api/v1/client/surveys/[surveyId]/index.ts b/apps/web/pages/api/v1/client/surveys/[surveyId]/index.ts index 01269d2ac7..984e6c07ec 100644 --- a/apps/web/pages/api/v1/client/surveys/[surveyId]/index.ts +++ b/apps/web/pages/api/v1/client/surveys/[surveyId]/index.ts @@ -48,7 +48,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) }, select: { brandColor: true, - formbricksSignature: true, + linkSurveyBranding: true, }, }); @@ -57,7 +57,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) message: "Survey not running", reason: survey.status, brandColor: product?.brandColor, - formbricksSignature: product?.formbricksSignature, + formbricksSignature: product?.linkSurveyBranding, surveyClosedMessage: survey?.surveyClosedMessage, }); } @@ -66,7 +66,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) return res.status(200).json({ ...survey, brandColor: product?.brandColor, - formbricksSignature: product?.formbricksSignature, + formbricksSignature: product?.linkSurveyBranding, }); } diff --git a/packages/database/migrations/20231114143459_rename_branding/migration.sql b/packages/database/migrations/20231114143459_rename_branding/migration.sql new file mode 100644 index 0000000000..c70bb6cdf5 --- /dev/null +++ b/packages/database/migrations/20231114143459_rename_branding/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `formbricksSignature` on the `Product` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Product" RENAME COLUMN "formbricksSignature" TO "linkSurveyBranding"; +ALTER TABLE "Product" ADD COLUMN "inAppSurveyBranding" BOOLEAN NOT NULL DEFAULT true; diff --git a/packages/database/schema.prisma b/packages/database/schema.prisma index 210defb3d4..7f7199bd61 100644 --- a/packages/database/schema.prisma +++ b/packages/database/schema.prisma @@ -397,7 +397,8 @@ model Product { brandColor String @default("#64748b") highlightBorderColor String? recontactDays Int @default(7) - formbricksSignature Boolean @default(true) + linkSurveyBranding Boolean @default(true) // Determines if the survey branding should be displayed in link surveys + inAppSurveyBranding Boolean @default(true) // Determines if the survey branding should be displayed in in-app surveys placement WidgetPlacement @default(bottomRight) clickOutsideClose Boolean @default(true) darkOverlay Boolean @default(false) diff --git a/packages/js/package.json b/packages/js/package.json index 4db7ddb6de..6dc9b9fb32 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -1,7 +1,7 @@ { "name": "@formbricks/js", "license": "MIT", - "version": "1.2.0", + "version": "1.2.1", "description": "Formbricks-js allows you to connect your app to Formbricks, display surveys and trigger events.", "keywords": [ "Formbricks", diff --git a/packages/js/src/lib/widget.ts b/packages/js/src/lib/widget.ts index 8540943cba..67a1ae179a 100644 --- a/packages/js/src/lib/widget.ts +++ b/packages/js/src/lib/widget.ts @@ -48,12 +48,13 @@ export const renderWidget = (survey: TSurveyWithTriggers) => { const clickOutside = productOverwrites.clickOutside ?? product.clickOutsideClose; const darkOverlay = productOverwrites.darkOverlay ?? product.darkOverlay; const placement = productOverwrites.placement ?? product.placement; + const isBrandingEnabled = product.inAppSurveyBranding; setTimeout(() => { renderSurveyModal({ survey: survey, brandColor, - formbricksSignature: true, + isBrandingEnabled: isBrandingEnabled, clickOutside, darkOverlay, highlightBorderColor, diff --git a/packages/js/tests/__mocks__/apiMock.ts b/packages/js/tests/__mocks__/apiMock.ts index ddbd1ab833..e833bb8f81 100644 --- a/packages/js/tests/__mocks__/apiMock.ts +++ b/packages/js/tests/__mocks__/apiMock.ts @@ -79,7 +79,8 @@ export const mockInitResponse = () => { product: { noCodeEvents: [], brandColor: "#20b398", - formbricksSignature: true, + linkSurveyBranding: true, + inAppBranding: true, placement: "bottomRight", darkOverlay: false, clickOutsideClose: true, diff --git a/packages/lib/product/service.ts b/packages/lib/product/service.ts index 7eeb1bd848..753b1b7b7a 100644 --- a/packages/lib/product/service.ts +++ b/packages/lib/product/service.ts @@ -25,7 +25,8 @@ const selectProduct = { brandColor: true, highlightBorderColor: true, recontactDays: true, - formbricksSignature: true, + linkSurveyBranding: true, + inAppSurveyBranding: true, placement: true, clickOutsideClose: true, darkOverlay: true, diff --git a/packages/surveys/src/components/FormbricksSignature.tsx b/packages/surveys/src/components/FormbricksBranding.tsx similarity index 88% rename from packages/surveys/src/components/FormbricksSignature.tsx rename to packages/surveys/src/components/FormbricksBranding.tsx index 5323ec2d50..fd8564dd13 100644 --- a/packages/surveys/src/components/FormbricksSignature.tsx +++ b/packages/surveys/src/components/FormbricksBranding.tsx @@ -1,4 +1,4 @@ -export default function FormbricksSignature() { +export default function FormbricksBranding() { return ( {}, onActiveQuestionChange = () => {}, @@ -180,7 +180,7 @@ export function Survey({ )}
- {formbricksSignature && } + {isBrandingEnabled && }
diff --git a/packages/surveys/src/components/SurveyInline.tsx b/packages/surveys/src/components/SurveyInline.tsx index e814889eaf..22ba535f3a 100644 --- a/packages/surveys/src/components/SurveyInline.tsx +++ b/packages/surveys/src/components/SurveyInline.tsx @@ -4,7 +4,7 @@ import { Survey } from "./Survey"; export function SurveyInline({ survey, brandColor, - formbricksSignature, + isBrandingEnabled, activeQuestionId, onDisplay = () => {}, onActiveQuestionChange = () => {}, @@ -18,7 +18,7 @@ export function SurveyInline({ void; onResponse?: (response: TResponseUpdate) => void; diff --git a/packages/types/product.ts b/packages/types/product.ts index fe7cd9ff9e..d5aac85ea1 100644 --- a/packages/types/product.ts +++ b/packages/types/product.ts @@ -11,7 +11,8 @@ export const ZProduct = z.object({ brandColor: ZColor, highlightBorderColor: ZColor.nullable(), recontactDays: z.number().int(), - formbricksSignature: z.boolean(), + inAppSurveyBranding: z.boolean(), + linkSurveyBranding: z.boolean(), placement: ZPlacement, clickOutsideClose: z.boolean(), darkOverlay: z.boolean(), diff --git a/packages/ui/Survey/index.tsx b/packages/ui/Survey/index.tsx index b5bf58579b..0b80c6eb0a 100644 --- a/packages/ui/Survey/index.tsx +++ b/packages/ui/Survey/index.tsx @@ -8,7 +8,7 @@ const createContainerId = () => `formbricks-survey-container`; interface SurveyProps { survey: TSurvey; brandColor: string; - formbricksSignature: boolean; + isBrandingEnabled: boolean; activeQuestionId?: string; onDisplay?: () => void; onResponse?: (response: TResponseUpdate) => void; @@ -30,7 +30,7 @@ interface SurveyModalProps extends SurveyProps { export const SurveyInline = ({ survey, brandColor, - formbricksSignature, + isBrandingEnabled, activeQuestionId, onDisplay = () => {}, onResponse = () => {}, @@ -45,7 +45,7 @@ export const SurveyInline = ({ renderSurveyInline({ survey, brandColor, - formbricksSignature, + isBrandingEnabled, containerId, onDisplay, onResponse, @@ -60,7 +60,7 @@ export const SurveyInline = ({ activeQuestionId, brandColor, containerId, - formbricksSignature, + isBrandingEnabled, onActiveQuestionChange, onClose, onDisplay, @@ -76,7 +76,7 @@ export const SurveyInline = ({ export const SurveyModal = ({ survey, brandColor, - formbricksSignature, + isBrandingEnabled, activeQuestionId, placement = "bottomRight", clickOutside = false, @@ -93,7 +93,7 @@ export const SurveyModal = ({ renderSurveyModal({ survey, brandColor, - formbricksSignature, + isBrandingEnabled, placement, clickOutside, darkOverlay, @@ -111,7 +111,7 @@ export const SurveyModal = ({ brandColor, clickOutside, darkOverlay, - formbricksSignature, + isBrandingEnabled, highlightBorderColor, onActiveQuestionChange, onClose,