mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-12 16:59:35 -05:00
Formbricks Branding Signature (#305)
* Add Formbricks Signature Branding (can be deactivated in Look & Feel Settings) --------- Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -18,8 +18,8 @@ export default function SettingsCard({
|
||||
<div className="my-4 w-full bg-white shadow sm:rounded-lg">
|
||||
<div className="rounded-t-lg border-b border-slate-200 bg-slate-100 px-6 py-5">
|
||||
<div className="flex">
|
||||
<h3 className="text-lg font-medium leading-6 text-slate-900">{title}</h3>
|
||||
{soon && <Badge text="coming soon" size="normal" type="warning" />}
|
||||
<h3 className="mr-2 text-lg font-medium leading-6 text-slate-900">{title}</h3>
|
||||
{soon && <Badge text="coming soon" size="normal" type="success" />}
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-slate-500">{description}</p>
|
||||
</div>
|
||||
|
||||
+47
-17
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { useEnvironment } from "@/lib/environments/environments";
|
||||
import { useProductMutation } from "@/lib/products/mutateProducts";
|
||||
@@ -18,7 +19,6 @@ import toast from "react-hot-toast";
|
||||
|
||||
export function EditBrandColor({ environmentId }) {
|
||||
const { product, isLoadingProduct, isErrorProduct } = useProduct(environmentId);
|
||||
|
||||
const { triggerProductMutate, isMutatingProduct } = useProductMutation(environmentId);
|
||||
|
||||
const [color, setColor] = useState("");
|
||||
@@ -54,7 +54,6 @@ export function EditBrandColor({ environmentId }) {
|
||||
}}>
|
||||
Save
|
||||
</Button>
|
||||
{/* <div className="whitespace-pre-wrap">{JSON.stringify(environment, null, 2)}</div>; */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -89,7 +88,11 @@ export function EditPlacement({ environmentId }) {
|
||||
checked={placement.default}
|
||||
disabled={placement.disabled}
|
||||
/>
|
||||
<Label htmlFor={placement.value}>{placement.name}</Label>
|
||||
<Label
|
||||
htmlFor={placement.value}
|
||||
className={cn(placement.disabled ? "cursor-not-allowed text-slate-500" : "text-slate-900")}>
|
||||
{placement.name}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
@@ -100,31 +103,58 @@ export function EditPlacement({ environmentId }) {
|
||||
<Button type="submit" variant="darkCTA" className="mt-4" disabled>
|
||||
Save
|
||||
</Button>
|
||||
{/* <div className="whitespace-pre-wrap">{JSON.stringify(environment, null, 2)}</div>; */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function EditFormbricksSignature({ environmentId }) {
|
||||
const { isLoadingEnvironment, isErrorEnvironment } = useEnvironment(environmentId);
|
||||
const { product, isLoadingProduct, isErrorProduct } = useProduct(environmentId);
|
||||
const { triggerProductMutate, isMutatingProduct } = useProductMutation(environmentId);
|
||||
|
||||
if (isLoadingEnvironment) {
|
||||
const [formbricksSignature, setFormbricksSignature] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (product) {
|
||||
setFormbricksSignature(product.formbricksSignature);
|
||||
}
|
||||
}, [product]);
|
||||
|
||||
const toggleSignature = () => {
|
||||
const newSignatureState = !formbricksSignature;
|
||||
setFormbricksSignature(newSignatureState);
|
||||
triggerProductMutate({ formbricksSignature: newSignatureState })
|
||||
.then(() => {
|
||||
toast.success(newSignatureState ? "Formbricks signature shown." : "Formbricks signature hidden.");
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error(`Error: ${error.message}`);
|
||||
});
|
||||
};
|
||||
|
||||
if (isLoadingEnvironment || isLoadingProduct) {
|
||||
return <LoadingSpinner />;
|
||||
}
|
||||
if (isErrorEnvironment) {
|
||||
|
||||
if (isErrorEnvironment || isErrorProduct) {
|
||||
return <ErrorComponent />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full items-center">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch disabled id="signature" />
|
||||
<Label htmlFor="signature">Show Formbricks Signature</Label>
|
||||
if (formbricksSignature !== null) {
|
||||
return (
|
||||
<div className="w-full items-center">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="signature"
|
||||
checked={formbricksSignature}
|
||||
onCheckedChange={toggleSignature}
|
||||
disabled={isMutatingProduct}
|
||||
/>
|
||||
<Label htmlFor="signature">Show 'Powered by Formbricks' Signature</Label>
|
||||
</div>
|
||||
</div>
|
||||
<Button type="submit" variant="darkCTA" className="mt-4" disabled>
|
||||
Save
|
||||
</Button>
|
||||
{/* <div className="whitespace-pre-wrap">{JSON.stringify(environment, null, 2)}</div>; */}
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -11,14 +11,13 @@ export default function ProfileSettingsPage({ params }: { params: { environmentI
|
||||
</SettingsCard>
|
||||
<SettingsCard
|
||||
soon
|
||||
title="Survey Placement"
|
||||
description="Change where surveys will be shown in your product.">
|
||||
title="In-app Survey Placement"
|
||||
description="Change where surveys will be shown in your web app.">
|
||||
<EditPlacement environmentId={params.environmentId} />
|
||||
</SettingsCard>
|
||||
<SettingsCard
|
||||
soon
|
||||
title="Formbricks Signature"
|
||||
description="As of now, there is no Formbricks branding on your surveys.">
|
||||
description="We love your support but understand if you toggle it off.">
|
||||
<EditFormbricksSignature environmentId={params.environmentId} />
|
||||
</SettingsCard>
|
||||
</div>
|
||||
|
||||
@@ -104,7 +104,7 @@ export default function EditAlerts({ memberships, user, environmentId }: EditAle
|
||||
</div>
|
||||
<p className="pb-3 pl-4 text-xs text-slate-400">
|
||||
Want to loop in team mates?{" "}
|
||||
<Link className="font-semibold" href={`/environments/${environmentId}/settings/notifications`}>
|
||||
<Link className="font-semibold" href={`/environments/${environmentId}/settings/members`}>
|
||||
Invite them.
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -3,9 +3,11 @@ import Progress from "@/components/preview/Progress";
|
||||
import QuestionConditional from "@/components/preview/QuestionConditional";
|
||||
import ThankYouCard from "@/components/preview/ThankYouCard";
|
||||
import { useEnvironment } from "@/lib/environments/environments";
|
||||
import { useProduct } from "@/lib/products/products";
|
||||
import type { Logic, Question } from "@formbricks/types/questions";
|
||||
import { Survey } from "@formbricks/types/surveys";
|
||||
import { useEffect, useState } from "react";
|
||||
import FormbricksSignature from "@/components/preview/FormbricksSignature";
|
||||
|
||||
interface PreviewSurveyProps {
|
||||
setActiveQuestionId: (id: string | null) => void;
|
||||
@@ -28,11 +30,20 @@ export default function PreviewSurvey({
|
||||
thankYouCard,
|
||||
previewType,
|
||||
}: PreviewSurveyProps) {
|
||||
const { environment } = useEnvironment(environmentId);
|
||||
const { product } = useProduct(environmentId);
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(true);
|
||||
const [progress, setProgress] = useState(0); // [0, 1]
|
||||
const [widgetSetupCompleted, setWidgetSetupCompleted] = useState(false);
|
||||
const { environment } = useEnvironment(environmentId);
|
||||
const [lastActiveQuestionId, setLastActiveQuestionId] = useState("");
|
||||
const [showFormbricksSignature, setShowFormbricksSignature] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (product) {
|
||||
setShowFormbricksSignature(product.formbricksSignature);
|
||||
}
|
||||
}, [product]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeQuestionId) {
|
||||
@@ -155,15 +166,6 @@ export default function PreviewSurvey({
|
||||
}
|
||||
};
|
||||
|
||||
/* const resetPreview = () => {
|
||||
setIsModalOpen(false);
|
||||
setTimeout(() => {
|
||||
setActiveQuestionId(questions[0].id);
|
||||
setIsModalOpen(true);
|
||||
}, 500);
|
||||
};
|
||||
*/
|
||||
|
||||
useEffect(() => {
|
||||
if (environment && environment.widgetSetupCompleted) {
|
||||
setWidgetSetupCompleted(true);
|
||||
@@ -217,6 +219,7 @@ export default function PreviewSurvey({
|
||||
) : null
|
||||
)
|
||||
)}
|
||||
{showFormbricksSignature && <FormbricksSignature />}
|
||||
</div>
|
||||
<Progress progress={progress} brandColor={brandColor} />
|
||||
</Modal>
|
||||
@@ -246,8 +249,9 @@ export default function PreviewSurvey({
|
||||
</div>
|
||||
</div>
|
||||
<div className="z-10 w-full rounded-b-lg bg-white">
|
||||
<div className="mx-auto max-w-md p-6 pt-4">
|
||||
<div className="mx-auto max-w-md space-y-6 p-6 pt-4">
|
||||
<Progress progress={progress} brandColor={brandColor} />
|
||||
{showFormbricksSignature && <FormbricksSignature />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import FormbricksSignature from "@/components/preview/FormbricksSignature";
|
||||
import Progress from "@/components/preview/Progress";
|
||||
import QuestionConditional from "@/components/preview/QuestionConditional";
|
||||
import ThankYouCard from "@/components/preview/ThankYouCard";
|
||||
@@ -16,6 +17,7 @@ import { useEffect, useState } from "react";
|
||||
|
||||
type EnhancedSurvey = Survey & {
|
||||
brandColor: string;
|
||||
formbricksSignature: boolean;
|
||||
};
|
||||
|
||||
interface LinkSurveyProps {
|
||||
@@ -235,8 +237,9 @@ export default function LinkSurvey({ survey }: LinkSurveyProps) {
|
||||
</ContentWrapper>
|
||||
</div>
|
||||
<div className="top-0 z-10 w-full border-b bg-white">
|
||||
<div className="mx-auto max-w-md p-6">
|
||||
<div className="mx-auto max-w-md space-y-6 p-6">
|
||||
<Progress progress={progress} brandColor={survey.brandColor} />
|
||||
{survey.formbricksSignature && <FormbricksSignature />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default function FormbricksSignature() {
|
||||
return (
|
||||
<a
|
||||
href="https://formbricks.com?utm_source=survey_branding"
|
||||
target="_blank"
|
||||
className="mt-4 flex justify-center">
|
||||
<p className=" text-xs text-slate-400">
|
||||
Powered by{" "}
|
||||
<b>
|
||||
<span className="text-slate-500 hover:text-slate-700">Formbricks</span>
|
||||
</b>
|
||||
</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -32,21 +32,6 @@ export default function ThankYouCard({ headline, subheader, brandColor }: ThankY
|
||||
<Headline headline={headline} questionId="thankYouCard" />
|
||||
<Subheader subheader={subheader} questionId="thankYouCard" />
|
||||
</div>
|
||||
|
||||
{/* <span
|
||||
className="mb-[10px] mt-[35px] inline-block h-[2px] w-4/5 rounded-full opacity-25"
|
||||
style={{ backgroundColor: brandColor }}></span>
|
||||
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">
|
||||
Powered by{" "}
|
||||
<b>
|
||||
<a href="https://formbricks.com" target="_blank" className="hover:text-slate-700">
|
||||
Formbricks
|
||||
</a>
|
||||
</b>
|
||||
</p>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,12 +202,14 @@ export const getSettings = async (environmentId: string, personId: string): Prom
|
||||
product: {
|
||||
select: {
|
||||
brandColor: true,
|
||||
formbricksSignature: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formbricksSignature = environmentProdut?.product.formbricksSignature;
|
||||
const brandColor = environmentProdut?.product.brandColor;
|
||||
|
||||
return { surveys, noCodeEvents, brandColor };
|
||||
return { surveys, noCodeEvents, brandColor, formbricksSignature };
|
||||
};
|
||||
|
||||
@@ -46,17 +46,27 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
select: {
|
||||
brandColor: true,
|
||||
formbricksSignature: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (survey.status !== "inProgress") {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ message: "Survey not running", reason: survey.status, brandColor: product?.brandColor });
|
||||
return res.status(403).json({
|
||||
message: "Survey not running",
|
||||
reason: survey.status,
|
||||
brandColor: product?.brandColor,
|
||||
formbricksSignature: product?.formbricksSignature,
|
||||
});
|
||||
}
|
||||
|
||||
// if survey exists, return survey
|
||||
return res.status(200).json({ ...survey, brandColor: product?.brandColor });
|
||||
return res
|
||||
.status(200)
|
||||
.json({
|
||||
...survey,
|
||||
brandColor: product?.brandColor,
|
||||
formbricksSignature: product?.formbricksSignature,
|
||||
});
|
||||
}
|
||||
|
||||
// Unknown HTTP Method
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Product" ADD COLUMN "formbricksSignature" BOOLEAN NOT NULL DEFAULT false;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Product" ALTER COLUMN "formbricksSignature" SET DEFAULT true;
|
||||
@@ -238,15 +238,16 @@ model Environment {
|
||||
}
|
||||
|
||||
model Product {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
teamId String
|
||||
environments Environment[]
|
||||
brandColor String @default("#64748b")
|
||||
recontactDays Int @default(7)
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
teamId String
|
||||
environments Environment[]
|
||||
brandColor String @default("#64748b")
|
||||
recontactDays Int @default(7)
|
||||
formbricksSignature Boolean @default(true)
|
||||
}
|
||||
|
||||
enum Plan {
|
||||
|
||||
@@ -25,13 +25,7 @@ export default function App({ config, survey, closeSurvey, errorHandler }: AppPr
|
||||
return (
|
||||
<div id="fbjs">
|
||||
<Modal isOpen={isOpen} close={close}>
|
||||
<SurveyView
|
||||
config={config}
|
||||
survey={survey}
|
||||
close={close}
|
||||
brandColor={config.settings?.brandColor}
|
||||
errorHandler={errorHandler}
|
||||
/>
|
||||
<SurveyView config={config} survey={survey} close={close} errorHandler={errorHandler} />
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { h } from "preact";
|
||||
|
||||
export default function FormbricksSignature() {
|
||||
return (
|
||||
<a
|
||||
href="https://formbricks.com?utm_source=survey_branding"
|
||||
target="_blank"
|
||||
className="fb-mb-5 fb-mt-2 fb-flex fb-justify-center">
|
||||
<p className="fb-text-xs fb-text-slate-400">
|
||||
Powered by{" "}
|
||||
<b>
|
||||
<span className="fb-text-slate-500 fb-hover:text-slate-700">Formbricks</span>
|
||||
</b>
|
||||
</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -10,16 +10,16 @@ import { cn } from "../lib/utils";
|
||||
import Progress from "./Progress";
|
||||
import QuestionConditional from "./QuestionConditional";
|
||||
import ThankYouCard from "./ThankYouCard";
|
||||
import FormbricksSignature from "./FormbricksSignature";
|
||||
|
||||
interface SurveyViewProps {
|
||||
config: JsConfig;
|
||||
survey: Survey;
|
||||
close: () => void;
|
||||
brandColor: string;
|
||||
errorHandler: IErrorHandler;
|
||||
}
|
||||
|
||||
export default function SurveyView({ config, survey, close, brandColor, errorHandler }: SurveyViewProps) {
|
||||
export default function SurveyView({ config, survey, close, errorHandler }: SurveyViewProps) {
|
||||
const [activeQuestionId, setActiveQuestionId] = useState(survey.questions[0].id);
|
||||
const [progress, setProgress] = useState(0); // [0, 1]
|
||||
const [responseId, setResponseId] = useState<string | null>(null);
|
||||
@@ -185,7 +185,7 @@ export default function SurveyView({ config, survey, close, brandColor, errorHan
|
||||
activeQuestionId === question.id && (
|
||||
<QuestionConditional
|
||||
key={question.id}
|
||||
brandColor={brandColor}
|
||||
brandColor={config.settings?.brandColor}
|
||||
lastQuestion={idx === survey.questions.length - 1}
|
||||
onSubmit={submitResponse}
|
||||
question={question}
|
||||
@@ -194,7 +194,8 @@ export default function SurveyView({ config, survey, close, brandColor, errorHan
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<Progress progress={progress} brandColor={brandColor} />
|
||||
{config.settings?.formbricksSignature && <FormbricksSignature />}
|
||||
<Progress progress={progress} brandColor={config.settings?.brandColor} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,21 +33,6 @@ export default function ThankYouCard({ headline, subheader, brandColor }: ThankY
|
||||
<Headline headline={headline} questionId="thankYouCard" style={{ "margin-right": 0 }} />
|
||||
<Subheader subheader={subheader} questionId="thankYouCard" />
|
||||
</div>
|
||||
|
||||
{/* <span
|
||||
className="fb-inline-block fb-w-4/5 fb-h-[2px] fb-mt-[35px] fb-mb-[10px]"
|
||||
style={{ backgroundColor: brandColor }}></span>
|
||||
|
||||
<div>
|
||||
<p className="fb-text-xs fb-text-slate-500">
|
||||
Powered by{" "}
|
||||
<b>
|
||||
<a href="https://formbricks.com" target="_blank" className="hover:text-slate-700">
|
||||
Formbricks
|
||||
</a>
|
||||
</b>
|
||||
</p>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface Settings {
|
||||
surveys?: Survey[];
|
||||
noCodeEvents?: any[];
|
||||
brandColor?: string;
|
||||
formbricksSignature?: boolean;
|
||||
}
|
||||
|
||||
export interface JsConfig {
|
||||
|
||||
Reference in New Issue
Block a user