mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 10:19:51 -06:00
Merge branch 'formbricks:main' into gitpod-doc
This commit is contained in:
@@ -49,7 +49,7 @@ Server actions are used to perform server actions in client components. For exam
|
||||
|
||||
## Use service abstraction instead of direct database calls
|
||||
|
||||
We utilize [prisma](https://www.prisma.io/) as our Object-Relational Mapping (ORM) tool to interact with the database. This implies that when you need to fetch or modify data in the database, you will be utilizing prisma. All prisma calls should be written in the services folder `packages/lib/services`, and before creating a new service, please ensure that one does not already exist.
|
||||
We utilize [prisma](https://www.prisma.io/) as our Object-Relational Mapping (ORM) tool to interact with the database. This implies that when you need to fetch or modify data in the database, you will be utilizing prisma. All prisma calls should be written in the services folder `packages/lib`, and before creating a new service, please ensure that one does not already exist.
|
||||
|
||||
## Handle authentication and CORS in management APIs
|
||||
|
||||
|
||||
@@ -157,8 +157,8 @@ To remove the integration with Google Account,
|
||||
For the above, we ask for:
|
||||
|
||||
1. **User Email**: To identify you (that's it, nothing else, we're opensource, see this in our codebase [here](https://github.com/formbricks/formbricks/blob/main/apps/web/app/api/google-sheet/callback/route.ts#L47C17-L47C25))
|
||||
1. **Google Drive API**: To list all your google sheets (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/services/googleSheet.ts#L13))
|
||||
1. **Google Spreadsheet API**: To write to the spreadsheet you select (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/services/googleSheet.ts#L70))
|
||||
1. **Google Drive API**: To list all your google sheets (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/googleSheet/service.ts#L13))
|
||||
1. **Google Spreadsheet API**: To write to the spreadsheet you select (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/googleSheet/service.ts#L70))
|
||||
|
||||
<Note>
|
||||
We do not store any other information of yours! We value Privacy more than you and rest assured you're safe
|
||||
|
||||
@@ -2,9 +2,9 @@ export const revalidate = REVALIDATION_INTERVAL;
|
||||
|
||||
import Navigation from "@/app/(app)/environments/[environmentId]/Navigation";
|
||||
import { IS_FORMBRICKS_CLOUD, REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment, getEnvironments } from "@formbricks/lib/services/environment";
|
||||
import { getProducts } from "@formbricks/lib/services/product";
|
||||
import { getTeamByEnvironmentId, getTeamsByUserId } from "@formbricks/lib/services/team";
|
||||
import { getEnvironment, getEnvironments } from "@formbricks/lib/environment/service";
|
||||
import { getProducts } from "@formbricks/lib/product/service";
|
||||
import { getTeamByEnvironmentId, getTeamsByUserId } from "@formbricks/lib/team/service";
|
||||
import { ErrorComponent } from "@formbricks/ui";
|
||||
import type { Session } from "next-auth";
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
import { createMembership } from "@formbricks/lib/services/membership";
|
||||
import { createProduct } from "@formbricks/lib/services/product";
|
||||
import { createTeam, getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { createMembership } from "@formbricks/lib/membership/service";
|
||||
import { createProduct } from "@formbricks/lib/product/service";
|
||||
import { createTeam, getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { canUserAccessSurvey } from "@formbricks/lib/survey/auth";
|
||||
import { deleteSurvey, getSurvey } from "@formbricks/lib/survey/service";
|
||||
import { AuthorizationError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
@@ -79,6 +79,9 @@ export async function duplicateSurveyAction(environmentId: string, surveyId: str
|
||||
singleUse: existingSurvey.singleUse
|
||||
? JSON.parse(JSON.stringify(existingSurvey.singleUse))
|
||||
: prismaClient.JsonNull,
|
||||
productOverwrites: existingSurvey.productOverwrites
|
||||
? JSON.parse(JSON.stringify(existingSurvey.productOverwrites))
|
||||
: prismaClient.JsonNull,
|
||||
verifyEmail: existingSurvey.verifyEmail
|
||||
? JSON.parse(JSON.stringify(existingSurvey.verifyEmail))
|
||||
: prismaClient.JsonNull,
|
||||
@@ -228,6 +231,7 @@ export async function copyToOtherEnvironmentAction(
|
||||
},
|
||||
surveyClosedMessage: existingSurvey.surveyClosedMessage ?? prismaClient.JsonNull,
|
||||
singleUse: existingSurvey.singleUse ?? prismaClient.JsonNull,
|
||||
productOverwrites: existingSurvey.productOverwrites ?? prismaClient.JsonNull,
|
||||
verifyEmail: existingSurvey.verifyEmail ?? prismaClient.JsonNull,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { getSpreadSheets } from "@formbricks/lib/services/googleSheet";
|
||||
import { createOrUpdateIntegration, deleteIntegration } from "@formbricks/lib/services/integrations";
|
||||
import { getSpreadSheets } from "@formbricks/lib/googleSheet/service";
|
||||
import { createOrUpdateIntegration, deleteIntegration } from "@formbricks/lib/integration/service";
|
||||
import { TGoogleSheetIntegration } from "@formbricks/types/v1/integrations";
|
||||
|
||||
export async function upsertIntegrationAction(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import GoogleSheetWrapper from "@/app/(app)/environments/[environmentId]/integrations/google-sheets/GoogleSheetWrapper";
|
||||
import GoBackButton from "@/components/shared/GoBackButton";
|
||||
import { getSpreadSheets } from "@formbricks/lib/services/googleSheet";
|
||||
import { getIntegrations } from "@formbricks/lib/services/integrations";
|
||||
import { getSpreadSheets } from "@formbricks/lib/googleSheet/service";
|
||||
import { getIntegrations } from "@formbricks/lib/integration/service";
|
||||
import { getSurveys } from "@formbricks/lib/survey/service";
|
||||
import { TGoogleSheetIntegration, TGoogleSpreadsheet } from "@formbricks/types/v1/integrations";
|
||||
import {
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
GOOGLE_SHEETS_CLIENT_SECRET,
|
||||
GOOGLE_SHEETS_REDIRECT_URL,
|
||||
} from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function GoogleSheet({ params }) {
|
||||
const enabled = !!(GOOGLE_SHEETS_CLIENT_ID && GOOGLE_SHEETS_CLIENT_SECRET && GOOGLE_SHEETS_REDIRECT_URL);
|
||||
|
||||
@@ -6,9 +6,9 @@ import n8nLogo from "@/images/n8n.png";
|
||||
import MakeLogo from "@/images/make-small.png";
|
||||
import { Card } from "@formbricks/ui";
|
||||
import Image from "next/image";
|
||||
import { getCountOfWebhooksBasedOnSource } from "@formbricks/lib/services/webhook";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getIntegrations } from "@formbricks/lib/services/integrations";
|
||||
import { getCountOfWebhooksBasedOnSource } from "@formbricks/lib/webhook/service";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getIntegrations } from "@formbricks/lib/integration/service";
|
||||
|
||||
export default async function IntegrationsPage({ params }) {
|
||||
const environmentId = params.environmentId;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { createWebhook, deleteWebhook, updateWebhook } from "@formbricks/lib/services/webhook";
|
||||
import { createWebhook, deleteWebhook, updateWebhook } from "@formbricks/lib/webhook/service";
|
||||
import { TWebhook, TWebhookInput } from "@formbricks/types/v1/webhooks";
|
||||
|
||||
export const createWebhookAction = async (
|
||||
|
||||
@@ -5,9 +5,9 @@ import WebhookTable from "@/app/(app)/environments/[environmentId]/integrations/
|
||||
import WebhookTableHeading from "@/app/(app)/environments/[environmentId]/integrations/webhooks/WebhookTableHeading";
|
||||
import GoBackButton from "@/components/shared/GoBackButton";
|
||||
import { getSurveys } from "@formbricks/lib/survey/service";
|
||||
import { getWebhooks } from "@formbricks/lib/services/webhook";
|
||||
import { getWebhooks } from "@formbricks/lib/webhook/service";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function CustomWebhookPage({ params }) {
|
||||
const [webhooksUnsorted, surveys, environment] = await Promise.all([
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ActivityTimeline from "@/app/(app)/environments/[environmentId]/people/[personId]/(activitySection)/ActivityTimeline";
|
||||
import { getActivityTimeline } from "@formbricks/lib/services/activity";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getActivityTimeline } from "@formbricks/lib/activity/service";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function ActivitySection({
|
||||
environmentId,
|
||||
|
||||
@@ -3,9 +3,9 @@ export const revalidate = REVALIDATION_INTERVAL;
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
|
||||
import { capitalizeFirstLetter } from "@/lib/utils";
|
||||
import { getPerson } from "@formbricks/lib/services/person";
|
||||
import { getPerson } from "@formbricks/lib/person/service";
|
||||
import { getResponsesByPersonId } from "@formbricks/lib/response/service";
|
||||
import { getSessionCount } from "@formbricks/lib/services/session";
|
||||
import { getSessionCount } from "@formbricks/lib/session/service";
|
||||
|
||||
export default async function AttributesSection({ personId }: { personId: string }) {
|
||||
const person = await getPerson(personId);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import GoBackButton from "@/components/shared/GoBackButton";
|
||||
import { DeletePersonButton } from "./DeletePersonButton";
|
||||
import { getPerson } from "@formbricks/lib/services/person";
|
||||
import { getPerson } from "@formbricks/lib/person/service";
|
||||
|
||||
interface HeadingSectionProps {
|
||||
environmentId: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { deletePerson } from "@formbricks/lib/services/person";
|
||||
import { deletePerson } from "@formbricks/lib/person/service";
|
||||
|
||||
export const deletePersonAction = async (personId: string) => {
|
||||
await deletePerson(personId);
|
||||
|
||||
@@ -5,7 +5,7 @@ import AttributesSection from "@/app/(app)/environments/[environmentId]/people/[
|
||||
import ResponseSection from "@/app/(app)/environments/[environmentId]/people/[personId]/(responseSection)/ResponseSection";
|
||||
import HeadingSection from "@/app/(app)/environments/[environmentId]/people/[personId]/HeadingSection";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function PersonPage({ params }) {
|
||||
const environment = await getEnvironment(params.environmentId);
|
||||
|
||||
@@ -3,8 +3,8 @@ export const revalidate = REVALIDATION_INTERVAL;
|
||||
import EmptySpaceFiller from "@/components/shared/EmptySpaceFiller";
|
||||
import { truncateMiddle } from "@/lib/utils";
|
||||
import { PEOPLE_PER_PAGE, REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getPeople, getPeopleCount } from "@formbricks/lib/services/person";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getPeople, getPeopleCount } from "@formbricks/lib/person/service";
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
import { Pagination, PersonAvatar } from "@formbricks/ui";
|
||||
import Link from "next/link";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import EditApiKeys from "./EditApiKeys";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getApiKeys } from "@formbricks/lib/apiKey/service";
|
||||
import { getEnvironments } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironments } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function ApiKeyList({
|
||||
environmentId,
|
||||
|
||||
@@ -5,7 +5,7 @@ import SettingsCard from "../SettingsCard";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
import ApiKeyList from "./ApiKeyList";
|
||||
import EnvironmentNotice from "@/components/shared/EnvironmentNotice";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function ProfileSettingsPage({ params }) {
|
||||
const environment = await getEnvironment(params.environmentId);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { notFound } from "next/navigation";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Metadata } from "next";
|
||||
import SettingsNavbar from "./SettingsNavbar";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Settings",
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { Button, Label, RadioGroup, RadioGroupItem } from "@formbricks/ui";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { getPlacementStyle } from "@/lib/preview";
|
||||
import { PlacementType } from "@formbricks/types/js";
|
||||
import { TProduct, TProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { updateProductAction } from "./actions";
|
||||
|
||||
const placements = [
|
||||
@@ -35,7 +35,9 @@ export function EditPlacement({ product }: EditPlacementProps) {
|
||||
darkOverlay: overlay === "darkOverlay",
|
||||
clickOutsideClose: clickOutside === "allow",
|
||||
};
|
||||
|
||||
await updateProductAction(product.id, inputProduct);
|
||||
|
||||
toast.success("Placement updated successfully.");
|
||||
} catch (error) {
|
||||
toast.error(`Error: ${error.message}`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { updateProduct } from "@formbricks/lib/services/product";
|
||||
import { updateProduct } from "@formbricks/lib/product/service";
|
||||
import { TProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
|
||||
export async function updateProductAction(productId: string, inputProduct: Partial<TProductUpdateInput>) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const revalidate = REVALIDATION_INTERVAL;
|
||||
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import SettingsCard from "../SettingsCard";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
@@ -15,6 +15,7 @@ export default async function ProfileSettingsPage({ params }: { params: { enviro
|
||||
if (!product) {
|
||||
throw new Error("Product not found");
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SettingsTitle title="Look & Feel" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { TTeam } from "@formbricks/types/v1/teams";
|
||||
import React from "react";
|
||||
import MembersInfo from "@/app/(app)/environments/[environmentId]/settings/members/EditMemberships/MembersInfo";
|
||||
import { getMembersByTeamId } from "@formbricks/lib/services/membership";
|
||||
import { getInvitesByTeamId } from "@formbricks/lib/services/invite";
|
||||
import { getMembersByTeamId } from "@formbricks/lib/membership/service";
|
||||
import { getInvitesByTeamId } from "@formbricks/lib/invite/service";
|
||||
import { TMembership } from "@formbricks/types/v1/memberships";
|
||||
|
||||
type EditMembershipsProps = {
|
||||
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
inviteUser,
|
||||
resendInvite,
|
||||
updateInvite,
|
||||
} from "@formbricks/lib/services/invite";
|
||||
} from "@formbricks/lib/invite/service";
|
||||
import {
|
||||
deleteMembership,
|
||||
getMembershipsByUserId,
|
||||
getMembershipByUserIdTeamId,
|
||||
transferOwnership,
|
||||
updateMembership,
|
||||
} from "@formbricks/lib/services/membership";
|
||||
import { deleteTeam, updateTeam } from "@formbricks/lib/services/team";
|
||||
} from "@formbricks/lib/membership/service";
|
||||
import { deleteTeam, updateTeam } from "@formbricks/lib/team/service";
|
||||
import { TInviteUpdateInput } from "@formbricks/types/v1/invites";
|
||||
import { TMembershipRole, TMembershipUpdateInput } from "@formbricks/types/v1/memberships";
|
||||
import { getServerSession } from "next-auth";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import TeamActions from "@/app/(app)/environments/[environmentId]/settings/members/EditMemberships/TeamActions";
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { getMembershipsByUserId, getMembershipByUserIdTeamId } from "@formbricks/lib/services/membership";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { getMembershipsByUserId, getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { Skeleton } from "@formbricks/ui";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { Suspense } from "react";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { getProducts } from "@formbricks/lib/services/product";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { getProducts } from "@formbricks/lib/product/service";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { TProduct } from "@formbricks/types/v1/product";
|
||||
import DeleteProductRender from "@/app/(app)/environments/[environmentId]/settings/product/DeleteProductRender";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/services/membership";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
|
||||
type DeleteProductProps = {
|
||||
environmentId: string;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use server";
|
||||
|
||||
import { deleteProduct, getProducts, updateProduct } from "@formbricks/lib/services/product";
|
||||
import { deleteProduct, getProducts, updateProduct } from "@formbricks/lib/product/service";
|
||||
import { TProduct, TProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { AuthenticationError, AuthorizationError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { TEnvironment } from "@formbricks/types/v1/environment";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/services/membership";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
|
||||
export const updateProductAction = async (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
|
||||
import SettingsCard from "../SettingsCard";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
@@ -6,7 +6,7 @@ import SettingsTitle from "../SettingsTitle";
|
||||
import EditProductName from "./EditProductName";
|
||||
import EditWaitingTime from "./EditWaitingTime";
|
||||
import DeleteProduct from "./DeleteProduct";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export default async function ProfileSettingsPage({ params }: { params: { environmentId: string } }) {
|
||||
const [, product] = await Promise.all([
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { updateProfile, deleteProfile } from "@formbricks/lib/services/profile";
|
||||
import { updateProfile, deleteProfile } from "@formbricks/lib/profile/service";
|
||||
import { TProfileUpdateInput } from "@formbricks/types/v1/profile";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { AuthorizationError } from "@formbricks/types/v1/errors";
|
||||
|
||||
@@ -8,7 +8,7 @@ import SettingsTitle from "../SettingsTitle";
|
||||
import { DeleteAccount } from "./DeleteAccount";
|
||||
import { EditName } from "./EditName";
|
||||
import { EditAvatar } from "./EditAvatar";
|
||||
import { getProfile } from "@formbricks/lib/services/profile";
|
||||
import { getProfile } from "@formbricks/lib/profile/service";
|
||||
|
||||
export default async function ProfileSettingsPage() {
|
||||
const session = await getServerSession(authOptions);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { updateEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { updateEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { TEnvironment, TEnvironmentUpdateInput } from "@formbricks/types/v1/environment";
|
||||
|
||||
export async function updateEnvironmentAction(
|
||||
|
||||
@@ -5,7 +5,7 @@ import EnvironmentNotice from "@/components/shared/EnvironmentNotice";
|
||||
import WidgetStatusIndicator from "@/components/shared/WidgetStatusIndicator";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getActionsByEnvironmentId } from "@formbricks/lib/action/service";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { ErrorComponent } from "@formbricks/ui";
|
||||
import SettingsCard from "../SettingsCard";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import EditTagsWrapper from "./EditTagsWrapper";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
|
||||
import { getTagsOnResponsesCount } from "@formbricks/lib/services/tagOnResponse";
|
||||
import { getTagsOnResponsesCount } from "@formbricks/lib/tagOnResponse/service";
|
||||
|
||||
export default async function MembersSettingsPage({ params }) {
|
||||
const environment = await getEnvironment(params.environmentId);
|
||||
|
||||
@@ -11,20 +11,23 @@ import { ArrowPathRoundedSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { ComputerDesktopIcon, DevicePhoneMobileIcon } from "@heroicons/react/24/solid";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
type TPreviewType = "modal" | "fullwidth" | "email";
|
||||
|
||||
interface PreviewSurveyProps {
|
||||
survey: TSurvey | Survey;
|
||||
setActiveQuestionId: (id: string | null) => void;
|
||||
activeQuestionId?: string | null;
|
||||
previewType?: "modal" | "fullwidth" | "email";
|
||||
previewType?: TPreviewType;
|
||||
product: TProduct;
|
||||
environment: TEnvironment;
|
||||
}
|
||||
|
||||
let surveyNameTemp;
|
||||
|
||||
export default function PreviewSurvey({
|
||||
survey,
|
||||
setActiveQuestionId,
|
||||
activeQuestionId,
|
||||
survey,
|
||||
previewType,
|
||||
product,
|
||||
environment,
|
||||
@@ -34,6 +37,18 @@ export default function PreviewSurvey({
|
||||
const [previewMode, setPreviewMode] = useState("desktop");
|
||||
const ContentRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const { productOverwrites } = survey || {};
|
||||
|
||||
const {
|
||||
brandColor: surveyBrandColor,
|
||||
highlightBorderColor: surveyHighlightBorderColor,
|
||||
placement: surveyPlacement,
|
||||
} = productOverwrites || {};
|
||||
|
||||
const brandColor = surveyBrandColor || product.brandColor;
|
||||
const placement = surveyPlacement || product.placement;
|
||||
const highlightBorderColor = surveyHighlightBorderColor || product.highlightBorderColor;
|
||||
|
||||
useEffect(() => {
|
||||
// close modal if there are no questions left
|
||||
if (survey.type === "web" && !survey.thankYouCard.enabled) {
|
||||
@@ -52,6 +67,8 @@ export default function PreviewSurvey({
|
||||
resetQuestionProgress();
|
||||
surveyNameTemp = survey.name;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [survey]);
|
||||
|
||||
function resetQuestionProgress() {
|
||||
@@ -94,12 +111,12 @@ export default function PreviewSurvey({
|
||||
{previewType === "modal" ? (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
placement={product.placement}
|
||||
highlightBorderColor={product.highlightBorderColor}
|
||||
placement={placement}
|
||||
highlightBorderColor={highlightBorderColor}
|
||||
previewMode="mobile">
|
||||
<SurveyInline
|
||||
survey={survey}
|
||||
brandColor={product.brandColor}
|
||||
brandColor={brandColor}
|
||||
activeQuestionId={activeQuestionId || undefined}
|
||||
formbricksSignature={product.formbricksSignature}
|
||||
onActiveQuestionChange={setActiveQuestionId}
|
||||
@@ -114,7 +131,7 @@ export default function PreviewSurvey({
|
||||
<div className="w-full max-w-md px-4">
|
||||
<SurveyInline
|
||||
survey={survey}
|
||||
brandColor={product.brandColor}
|
||||
brandColor={brandColor}
|
||||
activeQuestionId={activeQuestionId || undefined}
|
||||
formbricksSignature={product.formbricksSignature}
|
||||
onActiveQuestionChange={setActiveQuestionId}
|
||||
@@ -143,12 +160,12 @@ export default function PreviewSurvey({
|
||||
{previewType === "modal" ? (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
placement={product.placement}
|
||||
highlightBorderColor={product.highlightBorderColor}
|
||||
placement={placement}
|
||||
highlightBorderColor={highlightBorderColor}
|
||||
previewMode="desktop">
|
||||
<SurveyInline
|
||||
survey={survey}
|
||||
brandColor={product.brandColor}
|
||||
brandColor={brandColor}
|
||||
activeQuestionId={activeQuestionId || undefined}
|
||||
formbricksSignature={product.formbricksSignature}
|
||||
onActiveQuestionChange={setActiveQuestionId}
|
||||
@@ -161,7 +178,7 @@ export default function PreviewSurvey({
|
||||
<div className="w-full max-w-md">
|
||||
<SurveyInline
|
||||
survey={survey}
|
||||
brandColor={product.brandColor}
|
||||
brandColor={brandColor}
|
||||
activeQuestionId={activeQuestionId || undefined}
|
||||
formbricksSignature={product.formbricksSignature}
|
||||
onActiveQuestionChange={setActiveQuestionId}
|
||||
|
||||
@@ -3,8 +3,8 @@ import SurveyDropDownMenu from "@/app/(app)/environments/[environmentId]/surveys
|
||||
import SurveyStarter from "@/app/(app)/environments/[environmentId]/surveys/SurveyStarter";
|
||||
import SurveyStatusIndicator from "@/components/shared/SurveyStatusIndicator";
|
||||
import { SURVEY_BASE_URL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment, getEnvironments } from "@formbricks/lib/services/environment";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getEnvironment, getEnvironments } from "@formbricks/lib/environment/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getSurveys } from "@formbricks/lib/survey/service";
|
||||
import type { TEnvironment } from "@formbricks/types/v1/environment";
|
||||
import { Badge } from "@formbricks/ui";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { RESPONSES_LIMIT_FREE } from "@formbricks/lib/constants";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getSurveyWithAnalytics } from "@formbricks/lib/survey/service";
|
||||
import { getSurveyResponses } from "@formbricks/lib/response/service";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/services/team";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
|
||||
export const getAnalysisData = async (surveyId: string, environmentId: string) => {
|
||||
const [survey, team, allResponses] = await Promise.all([
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use server";
|
||||
|
||||
import { deleteResponse } from "@formbricks/lib/response/service";
|
||||
import { updateResponseNote, resolveResponseNote } from "@formbricks/lib/services/responseNote";
|
||||
import { updateResponseNote, resolveResponseNote } from "@formbricks/lib/responseNote/service";
|
||||
import { createTag } from "@formbricks/lib/tag/service";
|
||||
import { addTagToRespone, deleteTagOnResponse } from "@formbricks/lib/services/tagOnResponse";
|
||||
import { addTagToRespone, deleteTagOnResponse } from "@formbricks/lib/tagOnResponse/service";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { getServerSession } from "next-auth";
|
||||
|
||||
@@ -6,8 +6,8 @@ import { getAnalysisData } from "@/app/(app)/environments/[environmentId]/survey
|
||||
import { getServerSession } from "next-auth";
|
||||
import { REVALIDATION_INTERVAL, SURVEY_BASE_URL } from "@formbricks/lib/constants";
|
||||
import ResponsesLimitReachedBanner from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/ResponsesLimitReachedBanner";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
|
||||
|
||||
export default async function Page({ params }) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import { getAnalysisData } from "@/app/(app)/environments/[environmentId]/survey
|
||||
import SummaryPage from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage";
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { REVALIDATION_INTERVAL, SURVEY_BASE_URL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { generateSurveySingleUseId } from "@/lib/singleUseSurveys";
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { Label, RadioGroup, RadioGroupItem } from "@formbricks/ui";
|
||||
import { getPlacementStyle } from "@/lib/preview";
|
||||
import { PlacementType } from "@formbricks/types/js";
|
||||
const placements = [
|
||||
{ name: "Bottom Right", value: "bottomRight", disabled: false },
|
||||
{ name: "Top Right", value: "topRight", disabled: false },
|
||||
{ name: "Top Left", value: "topLeft", disabled: false },
|
||||
{ name: "Bottom Left", value: "bottomLeft", disabled: false },
|
||||
{ name: "Centered Modal", value: "center", disabled: false },
|
||||
];
|
||||
|
||||
type TPlacementProps = {
|
||||
currentPlacement: PlacementType;
|
||||
setCurrentPlacement: (placement: PlacementType) => void;
|
||||
setOverlay: (overlay: string) => void;
|
||||
overlay: string;
|
||||
setClickOutside: (clickOutside: boolean) => void;
|
||||
clickOutside: boolean;
|
||||
};
|
||||
|
||||
export default function Placement({
|
||||
setCurrentPlacement,
|
||||
currentPlacement,
|
||||
setOverlay,
|
||||
overlay,
|
||||
setClickOutside,
|
||||
clickOutside,
|
||||
}: TPlacementProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex">
|
||||
<RadioGroup onValueChange={(e) => setCurrentPlacement(e as PlacementType)} value={currentPlacement}>
|
||||
{placements.map((placement) => (
|
||||
<div key={placement.value} className="flex items-center space-x-2 whitespace-nowrap">
|
||||
<RadioGroupItem id={placement.value} value={placement.value} disabled={placement.disabled} />
|
||||
<Label
|
||||
htmlFor={placement.value}
|
||||
className={cn(placement.disabled ? "cursor-not-allowed text-slate-500" : "text-slate-900")}>
|
||||
{placement.name}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<div className="relative ml-8 h-40 w-full rounded bg-slate-200">
|
||||
<div
|
||||
className={cn(
|
||||
"absolute h-16 w-16 rounded bg-slate-700",
|
||||
getPlacementStyle(currentPlacement)
|
||||
)}></div>
|
||||
</div>
|
||||
</div>
|
||||
{currentPlacement === "center" && (
|
||||
<>
|
||||
<div className="mt-6 space-y-2">
|
||||
<Label className="font-semibold">Centered modal overlay color</Label>
|
||||
<RadioGroup
|
||||
onValueChange={(overlay) => setOverlay(overlay)}
|
||||
value={overlay}
|
||||
className="flex space-x-4">
|
||||
<div className="flex items-center space-x-2 whitespace-nowrap">
|
||||
<RadioGroupItem id="lightOverlay" value="light" />
|
||||
<Label htmlFor="lightOverlay" className="text-slate-900">
|
||||
Light Overlay
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 whitespace-nowrap">
|
||||
<RadioGroupItem id="darkOverlay" value="dark" />
|
||||
<Label htmlFor="darkOverlay" className="text-slate-900">
|
||||
Dark Overlay
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className="mt-6 space-y-2">
|
||||
<Label className="font-semibold">Allow users to exit by clicking outside the study</Label>
|
||||
<RadioGroup
|
||||
onValueChange={(value) => setClickOutside(value === "allow")}
|
||||
value={clickOutside ? "allow" : "disallow"}
|
||||
className="flex space-x-4">
|
||||
<div className="flex items-center space-x-2 whitespace-nowrap">
|
||||
<RadioGroupItem id="disallow" value="disallow" />
|
||||
<Label htmlFor="disallow" className="text-slate-900">
|
||||
Don't Allow
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 whitespace-nowrap">
|
||||
<RadioGroupItem id="allow" value="allow" />
|
||||
<Label htmlFor="allow" className="text-slate-900">
|
||||
Allow
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import RecontactOptionsCard from "./RecontactOptionsCard";
|
||||
import ResponseOptionsCard from "./ResponseOptionsCard";
|
||||
import WhenToSendCard from "./WhenToSendCard";
|
||||
import WhoToSendCard from "./WhoToSendCard";
|
||||
import StylingCard from "./StylingCard";
|
||||
import { TSurveyWithAnalytics } from "@formbricks/types/v1/surveys";
|
||||
import { TEnvironment } from "@formbricks/types/v1/environment";
|
||||
import { TActionClass } from "@formbricks/types/v1/actionClasses";
|
||||
@@ -54,6 +55,8 @@ export default function SettingsView({
|
||||
setLocalSurvey={setLocalSurvey}
|
||||
environmentId={environment.id}
|
||||
/>
|
||||
|
||||
<StylingCard localSurvey={localSurvey} setLocalSurvey={setLocalSurvey} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
"use client";
|
||||
|
||||
import Placement from "./Placement";
|
||||
import { PlacementType } from "@formbricks/types/js";
|
||||
import { TSurveyWithAnalytics } from "@formbricks/types/v1/surveys";
|
||||
import { ColorPicker, Label, Switch } from "@formbricks/ui";
|
||||
import { CheckCircleIcon } from "@heroicons/react/24/solid";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useState } from "react";
|
||||
|
||||
interface StylingCardProps {
|
||||
localSurvey: TSurveyWithAnalytics;
|
||||
setLocalSurvey: React.Dispatch<React.SetStateAction<TSurveyWithAnalytics>>;
|
||||
}
|
||||
|
||||
export default function StylingCard({ localSurvey, setLocalSurvey }: StylingCardProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { type, productOverwrites } = localSurvey;
|
||||
const { brandColor, clickOutside, darkOverlay, placement, highlightBorderColor } = productOverwrites ?? {};
|
||||
|
||||
const togglePlacement = () => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
placement: !!placement ? null : "bottomRight",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const toggleBrandColor = () => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
brandColor: !!brandColor ? null : "#64748b",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const toggleHighlightBorderColor = () => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
highlightBorderColor: !!highlightBorderColor ? null : "#64748b",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleColorChange = (color: string) => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
brandColor: color,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleBorderColorChange = (color: string) => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
highlightBorderColor: color,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handlePlacementChange = (placement: PlacementType) => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
placement,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleOverlay = (overlayType: string) => {
|
||||
const darkOverlay = overlayType === "dark";
|
||||
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
darkOverlay,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleClickOutside = (clickOutside: boolean) => {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
productOverwrites: {
|
||||
...localSurvey.productOverwrites,
|
||||
clickOutside,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapsible.Root
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
className="w-full rounded-lg border border-slate-300 bg-white">
|
||||
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
|
||||
<div className="inline-flex px-4 py-4">
|
||||
<div className="flex items-center pl-2 pr-5">
|
||||
<CheckCircleIcon className="h-8 w-8 text-green-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-800">Styling</p>
|
||||
<p className="mt-1 truncate text-sm text-slate-500">Overwrite global styling settings</p>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent>
|
||||
<hr className="py-1 text-slate-600" />
|
||||
<div className="p-3">
|
||||
{/* Brand Color */}
|
||||
<div className="p-3">
|
||||
<div className="ml-2 flex items-center space-x-1">
|
||||
<Switch id="autoComplete" checked={!!brandColor} onCheckedChange={toggleBrandColor} />
|
||||
<Label htmlFor="autoComplete" className="cursor-pointer">
|
||||
<div className="ml-2">
|
||||
<h3 className="text-sm font-semibold text-slate-700">Overwrite Brand Color</h3>
|
||||
<p className="text-xs font-normal text-slate-500">Change the main color for this survey.</p>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
{brandColor && (
|
||||
<div className="ml-2 mt-4 rounded-lg border bg-slate-50 p-4">
|
||||
<div className="w-full max-w-xs">
|
||||
<Label htmlFor="brandcolor">Color (HEX)</Label>
|
||||
<ColorPicker color={brandColor} onChange={handleColorChange} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* positioning */}
|
||||
{type !== "link" && (
|
||||
<div className="p-3 ">
|
||||
<div className="ml-2 flex items-center space-x-1">
|
||||
<Switch id="surveyDeadline" checked={!!placement} onCheckedChange={togglePlacement} />
|
||||
<Label htmlFor="surveyDeadline" className="cursor-pointer">
|
||||
<div className="ml-2">
|
||||
<h3 className="text-sm font-semibold text-slate-700">Overwrite Placement</h3>
|
||||
<p className="text-xs font-normal text-slate-500">Change the placement of this survey.</p>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
{placement && (
|
||||
<div className="ml-2 mt-4 flex items-center space-x-1 pb-4">
|
||||
<div className="flex w-full cursor-pointer items-center rounded-lg border bg-slate-50 p-4">
|
||||
<div className="w-full items-center">
|
||||
<Placement
|
||||
currentPlacement={placement}
|
||||
setCurrentPlacement={handlePlacementChange}
|
||||
setOverlay={handleOverlay}
|
||||
overlay={darkOverlay ? "dark" : "light"}
|
||||
setClickOutside={handleClickOutside}
|
||||
clickOutside={!!clickOutside}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Highlight border */}
|
||||
{type !== "link" && (
|
||||
<div className="p-3 ">
|
||||
<div className="ml-2 flex items-center space-x-1">
|
||||
<Switch
|
||||
id="autoComplete"
|
||||
checked={!!highlightBorderColor}
|
||||
onCheckedChange={toggleHighlightBorderColor}
|
||||
/>
|
||||
<Label htmlFor="autoComplete" className="cursor-pointer">
|
||||
<div className="ml-2">
|
||||
<h3 className="text-sm font-semibold text-slate-700">Overwrite border highlight</h3>
|
||||
<p className="text-xs font-normal text-slate-500">
|
||||
Change the border highlight for this survey.
|
||||
</p>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
{!!highlightBorderColor && (
|
||||
<div className="ml-2 mt-4 rounded-lg border bg-slate-50 p-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="highlightBorder"
|
||||
checked={!!highlightBorderColor}
|
||||
onCheckedChange={toggleHighlightBorderColor}
|
||||
/>
|
||||
<h2 className="text-sm font-medium text-slate-800">Show highlight border</h2>
|
||||
</div>
|
||||
{!!highlightBorderColor && (
|
||||
<div className="mt-6 w-full max-w-xs">
|
||||
<Label htmlFor="brandcolor">Color (HEX)</Label>
|
||||
<ColorPicker color={highlightBorderColor || ""} onChange={handleBorderColorChange} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.Root>
|
||||
);
|
||||
}
|
||||
@@ -51,6 +51,8 @@ export default function SurveyEditor({
|
||||
if (localSurvey?.questions?.length && localSurvey.questions.length > 0) {
|
||||
setActiveQuestionId(localSurvey.questions[0].id);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [localSurvey?.type]);
|
||||
|
||||
if (!localSurvey) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import React from "react";
|
||||
import { FORMBRICKS_ENCRYPTION_KEY, REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import SurveyEditor from "./SurveyEditor";
|
||||
import { getSurveyWithAnalytics } from "@formbricks/lib/survey/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getActionClasses } from "@formbricks/lib/actionClass/service";
|
||||
import { getAttributeClasses } from "@formbricks/lib/attributeClass/service";
|
||||
import { ErrorComponent } from "@formbricks/ui";
|
||||
|
||||
@@ -5,7 +5,7 @@ import ContentWrapper from "@/components/shared/ContentWrapper";
|
||||
import WidgetStatusIndicator from "@/components/shared/WidgetStatusIndicator";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getActionsByEnvironmentId } from "@formbricks/lib/action/service";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { Metadata } from "next";
|
||||
import SurveysList from "./SurveyList";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import TemplateContainerWithPreview from "./TemplateContainer";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
|
||||
export default async function SurveyTemplatesPage({ params }) {
|
||||
const environmentId = params.environmentId;
|
||||
|
||||
@@ -2062,5 +2062,6 @@ export const minimalSurvey: TSurvey = {
|
||||
surveyClosedMessage: {
|
||||
enabled: false,
|
||||
},
|
||||
productOverwrites: null,
|
||||
singleUse: null,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use server";
|
||||
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { updateProduct } from "@formbricks/lib/services/product";
|
||||
import { updateProfile } from "@formbricks/lib/services/profile";
|
||||
import { updateProduct } from "@formbricks/lib/product/service";
|
||||
import { updateProfile } from "@formbricks/lib/profile/service";
|
||||
import { TProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
import { TProfileUpdateInput } from "@formbricks/types/v1/profile";
|
||||
import { getServerSession } from "next-auth";
|
||||
|
||||
@@ -2,9 +2,9 @@ export const revalidate = REVALIDATION_INTERVAL;
|
||||
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getFirstEnvironmentByUserId } from "@formbricks/lib/services/environment";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getProfile } from "@formbricks/lib/services/profile";
|
||||
import { getFirstEnvironmentByUserId } from "@formbricks/lib/environment/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getProfile } from "@formbricks/lib/profile/service";
|
||||
import { getServerSession } from "next-auth";
|
||||
import Onboarding from "./components/Onboarding";
|
||||
|
||||
@@ -13,8 +13,14 @@ export default async function OnboardingPage() {
|
||||
if (!session) {
|
||||
throw new Error("No session found");
|
||||
}
|
||||
const environment = await getFirstEnvironmentByUserId(session?.user.id);
|
||||
const profile = await getProfile(session?.user.id!);
|
||||
const userId = session?.user.id;
|
||||
const environment = await getFirstEnvironmentByUserId(userId);
|
||||
|
||||
if (!environment) {
|
||||
throw new Error("No environment found for user");
|
||||
}
|
||||
|
||||
const profile = await getProfile(userId);
|
||||
const product = await getProductByEnvironmentId(environment?.id!);
|
||||
|
||||
if (!environment || !profile || !product) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { hasTeamAccess } from "@/lib/api/apiHelper";
|
||||
import { getEnvironments } from "@formbricks/lib/services/environment";
|
||||
import { getProduct } from "@formbricks/lib/services/product";
|
||||
import { getEnvironments } from "@formbricks/lib/environment/service";
|
||||
import { getProduct } from "@formbricks/lib/product/service";
|
||||
import { AuthenticationError, AuthorizationError } from "@formbricks/types/v1/errors";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { hasTeamAccess } from "@/lib/api/apiHelper";
|
||||
import { getEnvironments } from "@formbricks/lib/services/environment";
|
||||
import { getProducts } from "@formbricks/lib/services/product";
|
||||
import { getEnvironments } from "@formbricks/lib/environment/service";
|
||||
import { getProducts } from "@formbricks/lib/product/service";
|
||||
import { AuthenticationError, AuthorizationError } from "@formbricks/types/v1/errors";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { verifyPassword } from "@/lib/auth";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { EMAIL_VERIFICATION_DISABLED, INTERNAL_SECRET, WEBAPP_URL } from "@formbricks/lib/constants";
|
||||
import { verifyToken } from "@formbricks/lib/jwt";
|
||||
import { getProfileByEmail } from "@formbricks/lib/services/profile";
|
||||
import { getProfileByEmail } from "@formbricks/lib/profile/service";
|
||||
import type { IdentityProvider } from "@prisma/client";
|
||||
import type { NextAuthOptions } from "next-auth";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { writeData } from "@formbricks/lib/services/googleSheet";
|
||||
import { writeData } from "@formbricks/lib/googleSheet/service";
|
||||
import { getSurvey } from "@formbricks/lib/survey/service";
|
||||
import { TGoogleSheetIntegration, TIntegration } from "@formbricks/types/v1/integrations";
|
||||
import { TPipelineInput } from "@formbricks/types/v1/pipelines";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { InvalidInputError } from "@formbricks/types/v1/errors";
|
||||
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
|
||||
import { createDisplay } from "@formbricks/lib/display/service";
|
||||
import { getSurvey } from "@formbricks/lib/survey/service";
|
||||
import { getTeamDetails } from "@formbricks/lib/services/teamDetails";
|
||||
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
|
||||
import { TDisplay, ZDisplayInput } from "@formbricks/types/v1/displays";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { InvalidInputError } from "@formbricks/types/v1/errors";
|
||||
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
|
||||
import { getSurvey } from "@formbricks/lib/survey/service";
|
||||
import { createResponse } from "@formbricks/lib/response/service";
|
||||
import { getTeamDetails } from "@formbricks/lib/services/teamDetails";
|
||||
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
|
||||
import { TResponse, TResponseInput, ZResponseInput } from "@formbricks/types/v1/responses";
|
||||
import { NextResponse } from "next/server";
|
||||
import { UAParser } from "ua-parser-js";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getUpdatedState } from "@/app/api/v1/js/sync/lib/sync";
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/lib/api/validator";
|
||||
import { createAttributeClass, getAttributeClassByNameCached } from "@formbricks/lib/attributeClass/service";
|
||||
import { getPersonCached, updatePersonAttribute } from "@formbricks/lib/services/person";
|
||||
import { getPersonCached, updatePersonAttribute } from "@formbricks/lib/person/service";
|
||||
import { ZJsPeopleAttributeInput } from "@formbricks/types/v1/js";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getUpdatedState } from "@/app/api/v1/js/sync/lib/sync";
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/lib/api/validator";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { deletePerson, selectPerson, transformPrismaPerson } from "@formbricks/lib/services/person";
|
||||
import { deletePerson, selectPerson, transformPrismaPerson } from "@formbricks/lib/person/service";
|
||||
import { ZJsPeopleUserIdInput } from "@formbricks/types/v1/js";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { getSurveysCached } from "@/app/api/v1/js/surveys";
|
||||
import { MAU_LIMIT } from "@formbricks/lib/constants";
|
||||
import { getActionClasses } from "@formbricks/lib/actionClass/service";
|
||||
import { getEnvironment } from "@formbricks/lib/services/environment";
|
||||
import { createPerson, getMonthlyActivePeopleCount, getPersonCached } from "@formbricks/lib/services/person";
|
||||
import { getProductByEnvironmentIdCached } from "@formbricks/lib/services/product";
|
||||
import { createSession, extendSession, getSessionCached } from "@formbricks/lib/services/session";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { createPerson, getMonthlyActivePeopleCount, getPersonCached } from "@formbricks/lib/person/service";
|
||||
import { getProductByEnvironmentIdCached } from "@formbricks/lib/product/service";
|
||||
import { createSession, extendSession, getSessionCached } from "@formbricks/lib/session/service";
|
||||
import { captureTelemetry } from "@formbricks/lib/telemetry";
|
||||
import { TEnvironment } from "@formbricks/types/v1/environment";
|
||||
import { TJsState } from "@formbricks/types/v1/js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth";
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { deletePerson, getPerson } from "@formbricks/lib/services/person";
|
||||
import { deletePerson, getPerson } from "@formbricks/lib/person/service";
|
||||
import { TAuthenticationApiKey } from "@formbricks/types/v1/auth";
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { authenticateRequest } from "@/app/api/v1/auth";
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { getPeople } from "@formbricks/lib/services/person";
|
||||
import { getPeople } from "@formbricks/lib/person/service";
|
||||
import { DatabaseError } from "@formbricks/types/v1/errors";
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { INTERNAL_SECRET } from "@formbricks/lib/constants";
|
||||
import { createDemoProduct } from "@formbricks/lib/services/team";
|
||||
import { createDemoProduct } from "@formbricks/lib/team/service";
|
||||
import { NextResponse } from "next/server";
|
||||
import { headers } from "next/headers";
|
||||
import { responses } from "@/lib/api/response";
|
||||
|
||||
@@ -2,11 +2,11 @@ import { sendInviteAcceptedEmail, sendVerificationEmail } from "@/lib/email";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { EMAIL_VERIFICATION_DISABLED, INVITE_DISABLED, SIGNUP_ENABLED } from "@formbricks/lib/constants";
|
||||
import { verifyInviteToken } from "@formbricks/lib/jwt";
|
||||
import { deleteInvite } from "@formbricks/lib/services/invite";
|
||||
import { createMembership } from "@formbricks/lib/services/membership";
|
||||
import { createProduct } from "@formbricks/lib/services/product";
|
||||
import { createProfile } from "@formbricks/lib/services/profile";
|
||||
import { createTeam } from "@formbricks/lib/services/team";
|
||||
import { deleteInvite } from "@formbricks/lib/invite/service";
|
||||
import { createMembership } from "@formbricks/lib/membership/service";
|
||||
import { createProduct } from "@formbricks/lib/product/service";
|
||||
import { createProfile } from "@formbricks/lib/profile/service";
|
||||
import { createTeam } from "@formbricks/lib/team/service";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { getApiKeyFromKey } from "@formbricks/lib/apiKey/service";
|
||||
import { deleteWebhook, getWebhook } from "@formbricks/lib/services/webhook";
|
||||
import { deleteWebhook, getWebhook } from "@formbricks/lib/webhook/service";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
export async function GET(_: Request, { params }: { params: { webhookId: string } }) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { responses } from "@/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/lib/api/validator";
|
||||
import { getApiKeyFromKey } from "@formbricks/lib/apiKey/service";
|
||||
import { DatabaseError, InvalidInputError } from "@formbricks/types/v1/errors";
|
||||
import { createWebhook, getWebhooks } from "@formbricks/lib/services/webhook";
|
||||
import { createWebhook, getWebhooks } from "@formbricks/lib/webhook/service";
|
||||
import { ZWebhookInput } from "@formbricks/types/v1/webhooks";
|
||||
import { headers } from "next/headers";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ClientLogout from "@/app/ClientLogout";
|
||||
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { getFirstEnvironmentByUserId } from "@formbricks/lib/services/environment";
|
||||
import { getFirstEnvironmentByUserId } from "@formbricks/lib/environment/service";
|
||||
import type { Session } from "next-auth";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
@@ -46,6 +46,8 @@ export default function LinkSurvey({
|
||||
? getPrefillResponseData(survey.questions[0], survey, prefillAnswer)
|
||||
: undefined;
|
||||
|
||||
const brandColor = survey.productOverwrites?.brandColor || product.brandColor;
|
||||
|
||||
const responseQueue = useMemo(
|
||||
() =>
|
||||
new ResponseQueue(
|
||||
@@ -110,7 +112,7 @@ export default function LinkSurvey({
|
||||
)}
|
||||
<SurveyInline
|
||||
survey={survey}
|
||||
brandColor={product.brandColor}
|
||||
brandColor={brandColor}
|
||||
formbricksSignature={product.formbricksSignature}
|
||||
onDisplay={async () => {
|
||||
if (!isPreview) {
|
||||
|
||||
@@ -3,8 +3,8 @@ export const revalidate = REVALIDATION_INTERVAL;
|
||||
import LinkSurvey from "@/app/s/[surveyId]/LinkSurvey";
|
||||
import SurveyInactive from "@/app/s/[surveyId]/SurveyInactive";
|
||||
import { REVALIDATION_INTERVAL, WEBAPP_URL } from "@formbricks/lib/constants";
|
||||
import { getOrCreatePersonByUserId } from "@formbricks/lib/services/person";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getOrCreatePersonByUserId } from "@formbricks/lib/person/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getSurvey } from "@formbricks/lib/survey/service";
|
||||
import { getEmailVerificationStatus } from "./helpers";
|
||||
import { checkValidity } from "@/app/s/[surveyId]/prefilling";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import SurveyNavBarName from "@/components/shared/SurveyNavBarName";
|
||||
import Link from "next/link";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/services/product";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getSurvey } from "@formbricks/lib/survey/service";
|
||||
|
||||
interface SecondNavbarProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getEnvironments } from "@formbricks/lib/services/environment";
|
||||
import { getEnvironments } from "@formbricks/lib/environment/service";
|
||||
import { TEnvironment } from "@formbricks/types/v1/environment";
|
||||
import { LightBulbIcon } from "@heroicons/react/24/outline";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getSessionUser, hasEnvironmentAccess } from "@/lib/api/apiHelper";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { createProduct } from "@formbricks/lib/services/product";
|
||||
import { createProduct } from "@formbricks/lib/product/service";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
@@ -147,6 +147,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
surveyClosedMessage: existingSurvey.surveyClosedMessage ?? prismaClient.JsonNull,
|
||||
singleUse: existingSurvey.singleUse ?? prismaClient.JsonNull,
|
||||
productOverwrites: existingSurvey.productOverwrites ?? prismaClient.JsonNull,
|
||||
verifyEmail: existingSurvey.verifyEmail ?? prismaClient.JsonNull,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -67,6 +67,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
surveyClosedMessage: existingSurvey.surveyClosedMessage ?? prismaClient.JsonNull,
|
||||
singleUse: existingSurvey.singleUse ?? prismaClient.JsonNull,
|
||||
productOverwrites: existingSurvey.productOverwrites ?? prismaClient.JsonNull,
|
||||
verifyEmail: existingSurvey.verifyEmail ?? prismaClient.JsonNull,
|
||||
},
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
BIN
apps/web/uploads/cln2o8ist000g19nh5zqpze7e/public/cpTree.png
Normal file
BIN
apps/web/uploads/cln2o8ist000g19nh5zqpze7e/public/cpTree.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -3,6 +3,7 @@ import { TIntegrationConfig } from "@formbricks/types/v1/integrations";
|
||||
import { TResponsePersonAttributes, TResponseData, TResponseMeta } from "@formbricks/types/v1/responses";
|
||||
import {
|
||||
TSurveyClosedMessage,
|
||||
TSurveyProductOverwrites,
|
||||
TSurveyQuestions,
|
||||
TSurveySingleUse,
|
||||
TSurveyThankYouCard,
|
||||
@@ -20,6 +21,7 @@ declare global {
|
||||
export type ResponsePersonAttributes = TResponsePersonAttributes;
|
||||
export type SurveyQuestions = TSurveyQuestions;
|
||||
export type SurveyThankYouCard = TSurveyThankYouCard;
|
||||
export type SurveyProductOverwrites = TSurveyProductOverwrites;
|
||||
export type SurveyClosedMessage = TSurveyClosedMessage;
|
||||
export type SurveySingleUse = TSurveySingleUse;
|
||||
export type SurveyVerifyEmail = TSurveyVerifyEmail;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Survey" ADD COLUMN "productOverwrites" JSONB;
|
||||
@@ -251,10 +251,16 @@ model Survey {
|
||||
surveyClosedMessage Json?
|
||||
/// @zod.custom(imports.ZSurveySingleUse)
|
||||
/// [SurveySingleUse]
|
||||
singleUse Json? @default("{\"enabled\": false, \"isEncrypted\": true}")
|
||||
|
||||
/// @zod.custom(imports.ZSurveyProductOverwrites)
|
||||
/// [SurveyProductOverwrites]
|
||||
productOverwrites Json?
|
||||
/// @zod.custom(imports.ZSurveySingleUse)
|
||||
/// [SurveySingleUse]
|
||||
singleUse Json? @default("{\"enabled\": false, \"isEncrypted\": true}")
|
||||
/// @zod.custom(imports.ZSurveyVerifyEmail)
|
||||
/// [SurveyVerifyEmail]
|
||||
verifyEmail Json?
|
||||
verifyEmail Json?
|
||||
}
|
||||
|
||||
model Event {
|
||||
|
||||
@@ -10,6 +10,7 @@ export {
|
||||
ZSurveyQuestions,
|
||||
ZSurveyThankYouCard,
|
||||
ZSurveyClosedMessage,
|
||||
ZSurveyProductOverwrites,
|
||||
ZSurveyVerifyEmail,
|
||||
ZSurveySingleUse,
|
||||
} from "@formbricks/types/v1/surveys";
|
||||
|
||||
@@ -42,15 +42,22 @@ export const renderWidget = (survey: TSurvey) => {
|
||||
surveyState
|
||||
);
|
||||
|
||||
const productOverwrites = survey.productOverwrites ?? {};
|
||||
const brandColor = productOverwrites.brandColor ?? product.brandColor;
|
||||
const highlightBorderColor = productOverwrites.highlightBorderColor ?? product.highlightBorderColor;
|
||||
const clickOutside = productOverwrites.clickOutside ?? product.clickOutsideClose;
|
||||
const darkOverlay = productOverwrites.darkOverlay ?? product.darkOverlay;
|
||||
const placement = productOverwrites.placement ?? product.placement;
|
||||
|
||||
setTimeout(() => {
|
||||
renderSurveyModal({
|
||||
survey: survey,
|
||||
brandColor: product.brandColor,
|
||||
brandColor,
|
||||
formbricksSignature: product.formbricksSignature,
|
||||
clickOutside: product.clickOutsideClose,
|
||||
darkOverlay: product.darkOverlay,
|
||||
highlightBorderColor: product.highlightBorderColor,
|
||||
placement: product.placement,
|
||||
clickOutside,
|
||||
darkOverlay,
|
||||
highlightBorderColor,
|
||||
placement,
|
||||
onDisplay: async () => {
|
||||
const { id } = await createDisplay(
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ import { TJsActionInput } from "@formbricks/types/v1/js";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { EventType } from "@prisma/client";
|
||||
import { getActionClassCacheTag, getActionClassCached } from "../actionClass/service";
|
||||
import { getSessionCached } from "../services/session";
|
||||
import { getSessionCached } from "../session/service";
|
||||
|
||||
export const getActionsByEnvironmentId = cache(
|
||||
async (environmentId: string, limit?: number): Promise<TAction[]> => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Prisma } from "@prisma/client";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { transformPrismaPerson } from "../services/person";
|
||||
import { transformPrismaPerson } from "../person/service";
|
||||
|
||||
const selectDisplay = {
|
||||
id: true,
|
||||
|
||||
@@ -136,9 +136,8 @@ export const updateEnvironment = async (
|
||||
|
||||
export const getFirstEnvironmentByUserId = async (userId: string): Promise<TEnvironment | null> => {
|
||||
validateInputs([userId, ZId]);
|
||||
let environmentPrisma;
|
||||
try {
|
||||
environmentPrisma = await prisma.environment.findFirst({
|
||||
return await prisma.environment.findFirst({
|
||||
where: {
|
||||
type: "production",
|
||||
product: {
|
||||
@@ -159,16 +158,6 @@ export const getFirstEnvironmentByUserId = async (userId: string): Promise<TEnvi
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
const environment = ZEnvironment.parse(environmentPrisma);
|
||||
return environment;
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
console.error(JSON.stringify(error.errors, null, 2));
|
||||
}
|
||||
throw new ValidationError("Data validation of environment failed");
|
||||
}
|
||||
};
|
||||
|
||||
export const createEnvironment = async (
|
||||
@@ -11,7 +11,7 @@ import { cache } from "react";
|
||||
import { z } from "zod";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { createEnvironment, getEnvironmentCacheTag, getEnvironmentsCacheTag } from "./environment";
|
||||
import { createEnvironment, getEnvironmentCacheTag, getEnvironmentsCacheTag } from "../environment/service";
|
||||
|
||||
export const getProductsCacheTag = (teamId: string): string => `teams-${teamId}-products`;
|
||||
const getProductCacheTag = (environmentId: string): string => `environments-${environmentId}-product`;
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { MembershipRole, Prisma } from "@prisma/client";
|
||||
import { unstable_cache, revalidateTag } from "next/cache";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { deleteTeam } from "./team";
|
||||
import { deleteTeam } from "../team/service";
|
||||
import { z } from "zod";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
|
||||
@@ -14,7 +14,7 @@ import { TTag } from "@formbricks/types/v1/tags";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
import { cache } from "react";
|
||||
import { getPerson, transformPrismaPerson } from "../services/person";
|
||||
import { getPerson, transformPrismaPerson } from "../person/service";
|
||||
import { captureTelemetry } from "../telemetry";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
@@ -42,6 +42,7 @@ export const selectSurvey = {
|
||||
autoComplete: true,
|
||||
verifyEmail: true,
|
||||
redirectUrl: true,
|
||||
productOverwrites: true,
|
||||
surveyClosedMessage: true,
|
||||
singleUse: true,
|
||||
triggers: {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { unstable_cache } from "next/cache";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { canUserAccessResponse } from "../response/auth";
|
||||
import { canUserAccessTag } from "../tag/auth";
|
||||
import { getTagOnResponseCacheTag } from "../services/tagOnResponse";
|
||||
import { getTagOnResponseCacheTag } from "./service";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
|
||||
export const canUserAccessTagOnResponse = async (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PlacementType } from "./js";
|
||||
import { Question } from "./questions";
|
||||
|
||||
export interface ThankYouCard {
|
||||
@@ -22,6 +23,14 @@ export interface VerifyEmail {
|
||||
subheading?: string;
|
||||
}
|
||||
|
||||
export interface SurveyProductOverwrites {
|
||||
brandColor: string;
|
||||
highlightBorderColor: string | null;
|
||||
placement: PlacementType;
|
||||
clickOutside: boolean;
|
||||
darkOverlay: boolean;
|
||||
}
|
||||
|
||||
export interface Survey {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
@@ -47,6 +56,7 @@ export interface Survey {
|
||||
closeOnDate: Date | null;
|
||||
singleUse: SurveySingleUse | null;
|
||||
_count: { responses: number | null } | null;
|
||||
productOverwrites: SurveyProductOverwrites | null;
|
||||
}
|
||||
|
||||
export interface AttributeFilter {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user