mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-21 10:08:34 -06:00
chore: Freeze client api + api docs (#4373)
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
This commit is contained in:
committed by
GitHub
parent
97a66168c0
commit
dfe025ab8e
66
apps/docs/app/api-docs/components/api-docs.tsx
Normal file
66
apps/docs/app/api-docs/components/api-docs.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/Button";
|
||||
import { LoadingSpinner } from "@/components/icons/LoadingSpinner";
|
||||
import { useState } from "react";
|
||||
import { RedocStandalone } from "redoc";
|
||||
import "./style.css";
|
||||
|
||||
const redocTheme = {
|
||||
hideDownloadButton: true,
|
||||
hideLoading: true,
|
||||
nativeScrollbars: true,
|
||||
theme: {
|
||||
sidebar: {
|
||||
backgroundColor: "transparent",
|
||||
textColor: "rgb(203, 213, 225)",
|
||||
activeTextColor: "#2dd4bf",
|
||||
},
|
||||
rightPanel: {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
colors: {
|
||||
primary: { main: "#2dd4bf" },
|
||||
text: {
|
||||
primary: "rgb(203, 213, 225)",
|
||||
secondary: "rgb(203, 213, 225)",
|
||||
},
|
||||
responses: {
|
||||
success: { color: "#22c55e" },
|
||||
error: { color: "#ef4444" },
|
||||
info: { color: "#3b82f6" },
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontSize: "16px",
|
||||
lineHeight: "2rem",
|
||||
fontFamily: "Jost, system-ui, -apple-system, sans-serif",
|
||||
headings: {
|
||||
fontFamily: "Jost, system-ui, -apple-system, sans-serif",
|
||||
fontWeight: "600",
|
||||
},
|
||||
code: {
|
||||
fontSize: "16px",
|
||||
fontFamily: "ui-monospace, monospace",
|
||||
},
|
||||
},
|
||||
codeBlock: {
|
||||
backgroundColor: "rgb(24, 35, 58)",
|
||||
},
|
||||
spacing: { unit: 5 },
|
||||
},
|
||||
};
|
||||
|
||||
export const ApiDocs = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="px-4">
|
||||
<Button href="/developer-docs/rest-api" arrow="left" className="mb-4 mt-8">
|
||||
Back to docs
|
||||
</Button>
|
||||
<RedocStandalone specUrl="/docs/openapi.yaml" onLoaded={() => setLoading(false)} options={redocTheme} />
|
||||
{loading && <LoadingSpinner />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
74
apps/docs/app/api-docs/components/style.css
Normal file
74
apps/docs/app/api-docs/components/style.css
Normal file
@@ -0,0 +1,74 @@
|
||||
h5,
|
||||
.sc-dhCplO,
|
||||
.sc-dpBQxM {
|
||||
color: rgb(203, 213, 225) !important;
|
||||
}
|
||||
|
||||
.tab-success,
|
||||
.react-tabs__tab,
|
||||
.tab-error {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid rgb(203, 213, 225) !important;
|
||||
margin: 10px 5px !important;
|
||||
}
|
||||
|
||||
.sc-dwGkES,
|
||||
.sc-ePpfBx {
|
||||
background-color: rgb(24, 24, 27) !important;
|
||||
border: 1px solid rgb(203, 213, 225) !important;
|
||||
}
|
||||
.sc-ePpfBx,
|
||||
.corVrN {
|
||||
background-color: rgb(24, 24, 27) !important;
|
||||
border: none !important;
|
||||
}
|
||||
.cqdCbT {
|
||||
display: none !important;
|
||||
}
|
||||
.kiMaJz, .iZNUDY {
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.react-tabs__tab-panel > div {
|
||||
padding: 0 !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.sc-Rjrgp {
|
||||
background-color: rgb(15, 23, 42) !important;
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
flex-direction: column !important;
|
||||
padding: 8px 16px !important;
|
||||
}
|
||||
.cugBNu {
|
||||
position: static !important;
|
||||
}
|
||||
.daIHdK {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.kqHNPM {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
.sc-iwXfZk,
|
||||
.redoc-json {
|
||||
padding: 1rem !important;
|
||||
background-color: rgb(24, 35, 58) !important;
|
||||
}
|
||||
.sc-uYFMi {
|
||||
background-color: rgb(24, 35, 58) !important;
|
||||
padding: 0.5rem 0 !important;
|
||||
color: #2dd4bf !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
.token {
|
||||
color: #2dd4bf !important;
|
||||
}
|
||||
|
||||
.property {
|
||||
color: rgb(203, 213, 225) !important;
|
||||
}
|
||||
.sc-iPHsxv {
|
||||
background-color: rgb(15, 23, 42) !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
5
apps/docs/app/api-docs/page.tsx
Normal file
5
apps/docs/app/api-docs/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { ApiDocs } from "./components/api-docs";
|
||||
|
||||
export default function ApiDocsPage() {
|
||||
return <ApiDocs />;
|
||||
}
|
||||
@@ -20,31 +20,31 @@ export const metadata = {
|
||||
|
||||
Formbricks offers two types of APIs: the **Public Client API** and the **Management API**. Each API serves a different purpose, has different authentication requirements, and provides access to different data and settings.
|
||||
|
||||
View our [API Documentation](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh) in more than 30 frameworks and languages.
|
||||
View our [API Documentation](/api-docs) in more than 30 frameworks and languages.
|
||||
|
||||
## Public Client API
|
||||
|
||||
The [Public Client API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#5c981d9e-5e7d-455d-9795-b9c45bc2f930) is designed for our SDKs and **does not require authentication**. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
The [Public Client API](/api-docs#tag/Client-API) is designed for our SDKs and **does not require authentication**. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
We currently have the following Client API methods exposed and below is their documentation attached in Postman:
|
||||
|
||||
- [Displays API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#949272bf-daec-4d72-9b52-47af3d74a62c) - Mark Survey as Displayed or Update an existing Display by linking it with a Response for a Person
|
||||
- [People API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#ee3d2188-4253-4bca-9238-6b76455805a9) - Create & Update a Person (e.g. attributes, email, userId, etc)
|
||||
- [Responses API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#8c773032-536c-483c-a237-c7697347946e) - Create & Update a Response for a Survey
|
||||
- [Displays API](/api-docs#tag/Public-Client-API-greater-Displays) - Mark Survey as Displayed or Update an existing Display by linking it with a Response for a Person
|
||||
- [People API](/api-docs#tag/Public-Client-API-greater-People) - Create & Update a Person (e.g. attributes, email, userId, etc)
|
||||
- [Responses API](/api-docs#tag/Public-Client-API-greater-Responses) - Create & Update a Response for a Survey
|
||||
|
||||
## Management API
|
||||
|
||||
The [Management API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#98fce5a1-1365-4125-8de1-acdb28206766) provides access to all data and settings that your account has access to in the Formbricks app. This API **requires a personal API Key** for authentication, which can be generated in the Settings section of the Formbricks app. Checkout the [API Key Setup](#how-to-generate-an-api-key) below to generate & manage API Keys.
|
||||
The [Management API](/api-docs#tag/Management-API) provides access to all data and settings that your account has access to in the Formbricks app. This API **requires a personal API Key** for authentication, which can be generated in the Settings section of the Formbricks app. Checkout the [API Key Setup](#how-to-generate-an-api-key) below to generate & manage API Keys.
|
||||
|
||||
We currently have the following Management API methods exposed and below is their documentation attached in Postman:
|
||||
|
||||
- [Action Class API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#81947f69-99fc-41c9-a184-f3260e02be48) - Create, List, and Delete Action Classes
|
||||
- [Attribute Class API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#31089010-d468-4a7c-943e-8ebe71b9a36e) - Create, List, and Delete Attribute Classes
|
||||
- [Me API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#79e08365-641d-4b2d-aea2-9a855e0438ec) - Retrieve Account Information
|
||||
- [People API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#cffc27a6-dafb-428f-8ea7-5165bedb911e) - List and Delete People
|
||||
- [Response API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#e544ec0d-8b30-4e33-8d35-2441cb40d676) - List, List by Survey, Update, and Delete Responses
|
||||
- [Survey API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#953189b2-37b5-4429-a7bd-f4d01ceae242) - List, Create, Update, generate multiple suId and Delete Surveys
|
||||
- [Webhook API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#62e6ec65-021b-42a4-ac93-d1434b393c6c) - List, Create, and Delete Webhooks
|
||||
- [Action Class API](/api-docs#tag/Management-API-greater-Action-Class) - Create, List, and Delete Action Classes
|
||||
- [Attribute Class API](/api-docs#tag/Management-API-greater-Attribute-Class) - Create, List, and Delete Attribute Classes
|
||||
- [Me API](/api-docs#tag/Management-API-greater-Me) - Retrieve Account Information
|
||||
- [People API](/api-docs#tag/Management-API-greater-People) - List and Delete People
|
||||
- [Response API](/api-docs#tag/Management-API-greater-Response) - List, List by Survey, Update, and Delete Responses
|
||||
- [Survey API](/api-docs#tag/Management-API-greater-Survey) - List, Create, Update, generate multiple suId and Delete Surveys
|
||||
- [Webhook API](/api-docs#tag/Management-API-greater-Webhook) - List, Create, and Delete Webhooks
|
||||
|
||||
## How to Generate an API key
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@ export const Layout = ({
|
||||
}) => {
|
||||
const pathname = usePathname();
|
||||
|
||||
const fullWidthRoutes = ["/api-docs"];
|
||||
const isFullWidth = fullWidthRoutes.includes(pathname || "");
|
||||
|
||||
// If it's a full-width route, return just the children
|
||||
if (isFullWidth) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionProvider sections={allSections[pathname || ""] ?? []}>
|
||||
<div className="h-full lg:ml-72 xl:ml-80">
|
||||
|
||||
7
apps/docs/components/icons/LoadingSpinner.tsx
Normal file
7
apps/docs/components/icons/LoadingSpinner.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
export const LoadingSpinner = () => (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="border-primary h-12 w-12 animate-spin rounded-full border-4 border-t-transparent" />
|
||||
</div>
|
||||
);
|
||||
@@ -51,6 +51,7 @@
|
||||
"react-highlight-words": "0.20.0",
|
||||
"react-markdown": "9.0.1",
|
||||
"react-responsive-embed": "2.1.0",
|
||||
"redoc": "2.2.0",
|
||||
"remark": "15.0.1",
|
||||
"remark-gfm": "4.0.0",
|
||||
"remark-mdx": "3.0.1",
|
||||
@@ -65,8 +66,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"@types/dompurify": "3.0.5",
|
||||
"@types/react-highlight-words": "0.20.0",
|
||||
"@formbricks/eslint-config": "workspace:*"
|
||||
"@types/react-highlight-words": "0.20.0"
|
||||
}
|
||||
}
|
||||
|
||||
4832
apps/docs/public/openapi.yaml
Normal file
4832
apps/docs/public/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -187,8 +187,8 @@ const Page = async (props) => {
|
||||
)}
|
||||
</p>
|
||||
<Button asChild>
|
||||
<Link
|
||||
href="https://app.formbricks.com/s/clvupq3y205i5yrm3sm9v1xt5"
|
||||
<Link
|
||||
href="https://app.formbricks.com/s/clvupq3y205i5yrm3sm9v1xt5"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{t("environments.settings.enterprise.request_30_day_trial_license")}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from "@formbricks/lib/posthogServer";
|
||||
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
|
||||
import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
|
||||
import { TJsAppStateSync, ZJsPeopleUserIdInput } from "@formbricks/types/js";
|
||||
import { TJsRNStateSync, ZJsPeopleUserIdInput } from "@formbricks/types/js";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const OPTIONS = async (): Promise<Response> => {
|
||||
@@ -174,7 +174,7 @@ export const GET = async (
|
||||
let transformedSurveys: TSurvey[] = surveys;
|
||||
|
||||
// creating state object
|
||||
let state: TJsAppStateSync = {
|
||||
let state: TJsRNStateSync = {
|
||||
surveys: !isAppSurveyResponseLimitReached
|
||||
? transformedSurveys.map((survey) => replaceAttributeRecall(survey, contactAttributes))
|
||||
: [],
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { actionClassCache } from "@formbricks/lib/actionClass/cache";
|
||||
import { cache } from "@formbricks/lib/cache";
|
||||
import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { TJsEnvironmentStateActionClass } from "@formbricks/types/js";
|
||||
|
||||
export const getActionClassesForEnvironmentState = reactCache(
|
||||
async (environmentId: string): Promise<TJsEnvironmentStateActionClass[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
return await prisma.actionClass.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
name: true,
|
||||
key: true,
|
||||
noCodeConfig: true,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching actions for environment ${environmentId}`);
|
||||
}
|
||||
},
|
||||
[`getActionClassesForEnvironmentState-${environmentId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
@@ -1,6 +1,5 @@
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { actionClassCache } from "@formbricks/lib/actionClass/cache";
|
||||
import { getActionClasses } from "@formbricks/lib/actionClass/service";
|
||||
import { cache } from "@formbricks/lib/cache";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { environmentCache } from "@formbricks/lib/environment/cache";
|
||||
@@ -15,11 +14,12 @@ import {
|
||||
sendPlanLimitsReachedEventToPosthogWeekly,
|
||||
} from "@formbricks/lib/posthogServer";
|
||||
import { projectCache } from "@formbricks/lib/project/cache";
|
||||
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
|
||||
import { surveyCache } from "@formbricks/lib/survey/cache";
|
||||
import { getSurveys } from "@formbricks/lib/survey/service";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { TJsEnvironmentState } from "@formbricks/types/js";
|
||||
import { getActionClassesForEnvironmentState } from "./actionClass";
|
||||
import { getProjectForEnvironmentState } from "./project";
|
||||
import { getSurveysForEnvironmentState } from "./survey";
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -36,7 +36,7 @@ export const getEnvironmentState = async (
|
||||
const [environment, organization, project] = await Promise.all([
|
||||
getEnvironment(environmentId),
|
||||
getOrganizationByEnvironmentId(environmentId),
|
||||
getProjectByEnvironmentId(environmentId),
|
||||
getProjectForEnvironmentState(environmentId),
|
||||
]);
|
||||
|
||||
if (!environment) {
|
||||
@@ -94,8 +94,8 @@ export const getEnvironmentState = async (
|
||||
}
|
||||
|
||||
const [surveys, actionClasses] = await Promise.all([
|
||||
getSurveys(environmentId),
|
||||
getActionClasses(environmentId),
|
||||
getSurveysForEnvironmentState(environmentId),
|
||||
getActionClassesForEnvironmentState(environmentId),
|
||||
]);
|
||||
|
||||
const filteredSurveys = surveys.filter(
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { cache } from "@formbricks/lib/cache";
|
||||
import { projectCache } from "@formbricks/lib/project/cache";
|
||||
import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { TJsEnvironmentStateProject } from "@formbricks/types/js";
|
||||
|
||||
export const getProjectForEnvironmentState = reactCache(
|
||||
async (environmentId: string): Promise<TJsEnvironmentStateProject | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
return await prisma.project.findFirst({
|
||||
where: {
|
||||
environments: {
|
||||
some: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
recontactDays: true,
|
||||
clickOutsideClose: true,
|
||||
darkOverlay: true,
|
||||
placement: true,
|
||||
inAppSurveyBranding: true,
|
||||
styling: true,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getProjectForEnvironmentState-${environmentId}`],
|
||||
{
|
||||
tags: [projectCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { cache } from "@formbricks/lib/cache";
|
||||
import { surveyCache } from "@formbricks/lib/survey/cache";
|
||||
import { transformPrismaSurvey } from "@formbricks/lib/survey/utils";
|
||||
import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
|
||||
export const getSurveysForEnvironmentState = reactCache(
|
||||
async (environmentId: string): Promise<TJsEnvironmentStateSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const surveysPrisma = await prisma.survey.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
welcomeCard: true,
|
||||
name: true,
|
||||
questions: true,
|
||||
variables: true,
|
||||
type: true,
|
||||
showLanguageSwitch: true,
|
||||
languages: true,
|
||||
endings: true,
|
||||
autoClose: true,
|
||||
styling: true,
|
||||
status: true,
|
||||
segment: {
|
||||
include: {
|
||||
surveys: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
recontactDays: true,
|
||||
displayLimit: true,
|
||||
displayOption: true,
|
||||
hiddenFields: true,
|
||||
triggers: {
|
||||
select: {
|
||||
actionClass: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
displayPercentage: true,
|
||||
delay: true,
|
||||
},
|
||||
});
|
||||
|
||||
return surveysPrisma.map((survey) => transformPrismaSurvey<TJsEnvironmentStateSurvey>(survey));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getSurveysForEnvironmentState-${environmentId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
@@ -24,9 +24,16 @@ export const OPTIONS = async (): Promise<Response> => {
|
||||
export const POST = async (request: Request, context: Context): Promise<Response> => {
|
||||
const params = await context.params;
|
||||
const requestHeaders = await headers();
|
||||
let responseInput;
|
||||
try {
|
||||
responseInput = await request.json();
|
||||
} catch (error) {
|
||||
return responses.badRequestResponse("Invalid JSON in request body", { error: error.message }, true);
|
||||
}
|
||||
|
||||
const { environmentId } = params;
|
||||
const environmentIdValidation = ZId.safeParse(environmentId);
|
||||
const responseInputValidation = ZResponseInput.safeParse({ ...responseInput, environmentId });
|
||||
|
||||
if (!environmentIdValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
@@ -36,7 +43,13 @@ export const POST = async (request: Request, context: Context): Promise<Response
|
||||
);
|
||||
}
|
||||
|
||||
const responseInput = await request.json();
|
||||
if (!responseInputValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
"Fields are missing or incorrectly formatted",
|
||||
transformErrorToDetails(responseInputValidation.error),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
const agent = UAParser(request.headers.get("user-agent"));
|
||||
const country =
|
||||
@@ -44,17 +57,10 @@ export const POST = async (request: Request, context: Context): Promise<Response
|
||||
requestHeaders.get("X-Vercel-IP-Country") ||
|
||||
requestHeaders.get("CloudFront-Viewer-Country") ||
|
||||
undefined;
|
||||
const inputValidation = ZResponseInput.safeParse({ ...responseInput, environmentId });
|
||||
|
||||
if (!inputValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
"Fields are missing or incorrectly formatted",
|
||||
transformErrorToDetails(inputValidation.error),
|
||||
true
|
||||
);
|
||||
}
|
||||
const responseInputData = responseInputValidation.data;
|
||||
|
||||
if (inputValidation.data.userId) {
|
||||
if (responseInputData.userId) {
|
||||
const isContactsEnabled = await getIsContactsEnabled();
|
||||
if (!isContactsEnabled) {
|
||||
return responses.forbiddenResponse("User identification is only available for enterprise users.", true);
|
||||
@@ -62,9 +68,9 @@ export const POST = async (request: Request, context: Context): Promise<Response
|
||||
}
|
||||
|
||||
// get and check survey
|
||||
const survey = await getSurvey(responseInput.surveyId);
|
||||
const survey = await getSurvey(responseInputData.surveyId);
|
||||
if (!survey) {
|
||||
return responses.notFoundResponse("Survey", responseInput.surveyId, true);
|
||||
return responses.notFoundResponse("Survey", responseInputData.surveyId, true);
|
||||
}
|
||||
if (survey.environmentId !== environmentId) {
|
||||
return responses.badRequestResponse(
|
||||
@@ -80,19 +86,19 @@ export const POST = async (request: Request, context: Context): Promise<Response
|
||||
let response: TResponse;
|
||||
try {
|
||||
const meta: TResponseInput["meta"] = {
|
||||
source: responseInput?.meta?.source,
|
||||
url: responseInput?.meta?.url,
|
||||
source: responseInputData?.meta?.source,
|
||||
url: responseInputData?.meta?.url,
|
||||
userAgent: {
|
||||
browser: agent?.browser.name,
|
||||
device: agent?.device.type || "desktop",
|
||||
os: agent?.os.name,
|
||||
},
|
||||
country: country,
|
||||
action: responseInput?.meta?.action,
|
||||
action: responseInputData?.meta?.action,
|
||||
};
|
||||
|
||||
response = await createResponse({
|
||||
...inputValidation.data,
|
||||
...responseInputData,
|
||||
meta,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { TActionClass } from "@formbricks/types/action-classes";
|
||||
import { TJsEnvironmentStateActionClass } from "@formbricks/types/js";
|
||||
import { trackNoCodeAction } from "./actions";
|
||||
import { Config } from "./config";
|
||||
import { ErrorHandler, NetworkError, Result, err, match, okVoid } from "./errors";
|
||||
@@ -65,7 +65,7 @@ const checkClickMatch = (event: MouseEvent) => {
|
||||
|
||||
const targetElement = event.target as HTMLElement;
|
||||
|
||||
noCodeClickActionClasses.forEach((action: TActionClass) => {
|
||||
noCodeClickActionClasses.forEach((action: TJsEnvironmentStateActionClass) => {
|
||||
if (evaluateNoCodeConfigClick(targetElement, action)) {
|
||||
trackNoCodeAction(action.name).then((res) => {
|
||||
match(
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { diffInDays } from "@formbricks/lib/utils/datetime";
|
||||
import {
|
||||
TActionClass,
|
||||
TActionClassNoCodeConfig,
|
||||
TActionClassPageUrlRule,
|
||||
} from "@formbricks/types/action-classes";
|
||||
import { TActionClassNoCodeConfig, TActionClassPageUrlRule } from "@formbricks/types/action-classes";
|
||||
import { TAttributes } from "@formbricks/types/attributes";
|
||||
import { TJsEnvironmentState, TJsPersonState, TJsTrackProperties } from "@formbricks/types/js";
|
||||
import {
|
||||
TJsEnvironmentState,
|
||||
TJsEnvironmentStateActionClass,
|
||||
TJsEnvironmentStateSurvey,
|
||||
TJsPersonState,
|
||||
TJsTrackProperties,
|
||||
} from "@formbricks/types/js";
|
||||
import { TResponseHiddenFieldValue } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Logger } from "./logger";
|
||||
|
||||
const logger = Logger.getInstance();
|
||||
@@ -50,7 +51,10 @@ export const handleUrlFilters = (urlFilters: TActionClassNoCodeConfig["urlFilter
|
||||
return isMatch;
|
||||
};
|
||||
|
||||
export const evaluateNoCodeConfigClick = (targetElement: HTMLElement, action: TActionClass): boolean => {
|
||||
export const evaluateNoCodeConfigClick = (
|
||||
targetElement: HTMLElement,
|
||||
action: TJsEnvironmentStateActionClass
|
||||
): boolean => {
|
||||
if (action.noCodeConfig?.type !== "click") return false;
|
||||
|
||||
const innerHtml = action.noCodeConfig.elementSelector.innerHtml;
|
||||
@@ -79,7 +83,7 @@ export const evaluateNoCodeConfigClick = (targetElement: HTMLElement, action: TA
|
||||
};
|
||||
|
||||
export const handleHiddenFields = (
|
||||
hiddenFieldsConfig: TSurvey["hiddenFields"],
|
||||
hiddenFieldsConfig: TJsEnvironmentStateSurvey["hiddenFields"],
|
||||
hiddenFields: TJsTrackProperties["hiddenFields"]
|
||||
): TResponseHiddenFieldValue => {
|
||||
const { enabled: enabledHiddenFields, fieldIds: hiddenFieldIds } = hiddenFieldsConfig || {};
|
||||
@@ -114,7 +118,10 @@ export const shouldDisplayBasedOnPercentage = (displayPercentage: number) => {
|
||||
return randomNum <= displayPercentage;
|
||||
};
|
||||
|
||||
export const getLanguageCode = (survey: TSurvey, attributes: TAttributes): string | undefined => {
|
||||
export const getLanguageCode = (
|
||||
survey: TJsEnvironmentStateSurvey,
|
||||
attributes: TAttributes
|
||||
): string | undefined => {
|
||||
const language = attributes.language;
|
||||
const availableLanguageCodes = survey.languages.map((surveyLanguage) => surveyLanguage.language.code);
|
||||
if (!language) return "default";
|
||||
@@ -139,7 +146,7 @@ export const getLanguageCode = (survey: TSurvey, attributes: TAttributes): strin
|
||||
}
|
||||
};
|
||||
|
||||
export const getDefaultLanguageCode = (survey: TSurvey) => {
|
||||
export const getDefaultLanguageCode = (survey: TJsEnvironmentStateSurvey) => {
|
||||
const defaultSurveyLanguage = survey.languages?.find((surveyLanguage) => {
|
||||
return surveyLanguage.default === true;
|
||||
});
|
||||
@@ -159,7 +166,7 @@ export const getIsDebug = () => window.location.search.includes("formbricksDebug
|
||||
export const filterSurveys = (
|
||||
environmentState: TJsEnvironmentState,
|
||||
personState: TJsPersonState
|
||||
): TSurvey[] => {
|
||||
): TJsEnvironmentStateSurvey[] => {
|
||||
const { project, surveys } = environmentState.data;
|
||||
const { displays, responses, lastDisplayAt, segments, userId } = personState.data;
|
||||
|
||||
@@ -168,7 +175,7 @@ export const filterSurveys = (
|
||||
}
|
||||
|
||||
// Function to filter surveys based on displayOption criteria
|
||||
let filteredSurveys = surveys.filter((survey: TSurvey) => {
|
||||
let filteredSurveys = surveys.filter((survey: TJsEnvironmentStateSurvey) => {
|
||||
switch (survey.displayOption) {
|
||||
case "respondMultiple":
|
||||
return true;
|
||||
|
||||
@@ -2,10 +2,14 @@ import { FormbricksAPI } from "@formbricks/api";
|
||||
import { ResponseQueue } from "@formbricks/lib/responseQueue";
|
||||
import { SurveyState } from "@formbricks/lib/surveyState";
|
||||
import { getStyling } from "@formbricks/lib/utils/styling";
|
||||
import { TJsFileUploadParams, TJsPersonState, TJsTrackProperties } from "@formbricks/types/js";
|
||||
import {
|
||||
TJsEnvironmentStateSurvey,
|
||||
TJsFileUploadParams,
|
||||
TJsPersonState,
|
||||
TJsTrackProperties,
|
||||
} from "@formbricks/types/js";
|
||||
import { TResponseHiddenFieldValue, TResponseUpdate } from "@formbricks/types/responses";
|
||||
import { TUploadFileConfig } from "@formbricks/types/storage";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Config } from "./config";
|
||||
import { CONTAINER_ID } from "./constants";
|
||||
import { Logger } from "./logger";
|
||||
@@ -28,7 +32,7 @@ export const setIsSurveyRunning = (value: boolean) => {
|
||||
};
|
||||
|
||||
export const triggerSurvey = async (
|
||||
survey: TSurvey,
|
||||
survey: TJsEnvironmentStateSurvey,
|
||||
action?: string,
|
||||
properties?: TJsTrackProperties
|
||||
): Promise<void> => {
|
||||
@@ -50,7 +54,7 @@ export const triggerSurvey = async (
|
||||
};
|
||||
|
||||
const renderWidget = async (
|
||||
survey: TSurvey,
|
||||
survey: TJsEnvironmentStateSurvey,
|
||||
action?: string,
|
||||
hiddenFields: TResponseHiddenFieldValue = {}
|
||||
) => {
|
||||
|
||||
@@ -224,7 +224,7 @@ export const getSurvey = reactCache(
|
||||
return null;
|
||||
}
|
||||
|
||||
return transformPrismaSurvey(surveyPrisma);
|
||||
return transformPrismaSurvey<TSurvey>(surveyPrisma);
|
||||
},
|
||||
[`getSurvey-${surveyId}`],
|
||||
{
|
||||
@@ -267,7 +267,7 @@ export const getSurveysByActionClassId = reactCache(
|
||||
const surveys: TSurvey[] = [];
|
||||
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
const transformedSurvey = transformPrismaSurvey<TSurvey>(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ export const getSurveys = reactCache(
|
||||
skip: offset,
|
||||
});
|
||||
|
||||
return surveysPrisma.map(transformPrismaSurvey);
|
||||
return surveysPrisma.map((surveyPrisma) => transformPrismaSurvey<TSurvey>(surveyPrisma));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
@@ -1366,7 +1366,7 @@ export const getSurveysBySegmentId = reactCache(
|
||||
const surveys: TSurvey[] = [];
|
||||
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
const transformedSurvey = transformPrismaSurvey<TSurvey>(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { generateObject } from "ai";
|
||||
import { z } from "zod";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import {
|
||||
TSurvey,
|
||||
@@ -11,7 +12,9 @@ import {
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { llmModel } from "../aiModels";
|
||||
|
||||
export const transformPrismaSurvey = (surveyPrisma: any): TSurvey => {
|
||||
export const transformPrismaSurvey = <T extends TSurvey | TJsEnvironmentStateSurvey>(
|
||||
surveyPrisma: any
|
||||
): T => {
|
||||
let segment: TSegment | null = null;
|
||||
|
||||
if (surveyPrisma.segment) {
|
||||
@@ -21,11 +24,11 @@ export const transformPrismaSurvey = (surveyPrisma: any): TSurvey => {
|
||||
};
|
||||
}
|
||||
|
||||
const transformedSurvey: TSurvey = {
|
||||
const transformedSurvey = {
|
||||
...surveyPrisma,
|
||||
displayPercentage: Number(surveyPrisma.displayPercentage) || null,
|
||||
segment,
|
||||
};
|
||||
} as T;
|
||||
|
||||
return transformedSurvey;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { TResponseData, TResponseVariables } from "@formbricks/types/responses";
|
||||
import {
|
||||
TActionCalculate,
|
||||
TActionObjective,
|
||||
TConditionGroup,
|
||||
TSingleCondition,
|
||||
TSurvey,
|
||||
TSurveyLogic,
|
||||
TSurveyLogicAction,
|
||||
TSurveyQuestion,
|
||||
@@ -212,7 +212,7 @@ export const getUpdatedActionBody = (
|
||||
};
|
||||
|
||||
export const evaluateLogic = (
|
||||
localSurvey: TSurvey,
|
||||
localSurvey: TJsEnvironmentStateSurvey,
|
||||
data: TResponseData,
|
||||
variablesData: TResponseVariables,
|
||||
conditions: TConditionGroup,
|
||||
@@ -234,7 +234,7 @@ export const evaluateLogic = (
|
||||
};
|
||||
|
||||
const evaluateSingleCondition = (
|
||||
localSurvey: TSurvey,
|
||||
localSurvey: TJsEnvironmentStateSurvey,
|
||||
data: TResponseData,
|
||||
variablesData: TResponseVariables,
|
||||
condition: TSingleCondition,
|
||||
@@ -476,7 +476,7 @@ const getVariableValue = (
|
||||
};
|
||||
|
||||
const getLeftOperandValue = (
|
||||
localSurvey: TSurvey,
|
||||
localSurvey: TJsEnvironmentStateSurvey,
|
||||
data: TResponseData,
|
||||
variablesData: TResponseVariables,
|
||||
leftOperand: TSingleCondition["leftOperand"],
|
||||
@@ -541,7 +541,7 @@ const getLeftOperandValue = (
|
||||
};
|
||||
|
||||
const getRightOperandValue = (
|
||||
localSurvey: TSurvey,
|
||||
localSurvey: TJsEnvironmentStateSurvey,
|
||||
data: TResponseData,
|
||||
variablesData: TResponseVariables,
|
||||
rightOperand: TSingleCondition["rightOperand"]
|
||||
@@ -564,7 +564,7 @@ const getRightOperandValue = (
|
||||
};
|
||||
|
||||
export const performActions = (
|
||||
survey: TSurvey,
|
||||
survey: TJsEnvironmentStateSurvey,
|
||||
actions: TSurveyLogicAction[],
|
||||
data: TResponseData,
|
||||
calculationResults: TResponseVariables
|
||||
@@ -598,7 +598,7 @@ export const performActions = (
|
||||
};
|
||||
|
||||
const performCalculation = (
|
||||
survey: TSurvey,
|
||||
survey: TJsEnvironmentStateSurvey,
|
||||
action: TActionCalculate,
|
||||
data: TResponseData,
|
||||
calculations: Record<string, number | string>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TJsEnvironmentStateProject, TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
|
||||
export const getStyling = (project: TProject, survey: TSurvey) => {
|
||||
export const getStyling = (project: TJsEnvironmentStateProject, survey: TJsEnvironmentStateSurvey) => {
|
||||
// allow style overwrite is disabled from the project
|
||||
if (!project.styling.allowStyleOverwrite) {
|
||||
return project.styling;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useCallback, useEffect, useSyncExternalStore } from "react";
|
||||
import { type TJsReactNativeConfigInput } from "@formbricks/types/js";
|
||||
import { type TJsRNConfigInput } from "@formbricks/types/js";
|
||||
import { Logger } from "../../js-core/src/lib/logger";
|
||||
import { init } from "./lib";
|
||||
import { SurveyStore } from "./lib/survey-store";
|
||||
import { SurveyWebView } from "./survey-web-view";
|
||||
|
||||
interface FormbricksProps {
|
||||
initConfig: TJsReactNativeConfigInput;
|
||||
initConfig: TJsRNConfigInput;
|
||||
}
|
||||
const surveyStore = SurveyStore.getInstance();
|
||||
const logger = Logger.getInstance();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import {
|
||||
type InvalidCodeError,
|
||||
type NetworkError,
|
||||
@@ -14,7 +14,7 @@ import { SurveyStore } from "./survey-store";
|
||||
const logger = Logger.getInstance();
|
||||
const surveyStore = SurveyStore.getInstance();
|
||||
|
||||
export const triggerSurvey = (survey: TSurvey): void => {
|
||||
export const triggerSurvey = (survey: TJsEnvironmentStateSurvey): void => {
|
||||
// Check if the survey should be displayed based on displayPercentage
|
||||
if (survey.displayPercentage) {
|
||||
const shouldDisplaySurvey = shouldDisplayBasedOnPercentage(survey.displayPercentage);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-console -- Required for error logging */
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { type Result, err, ok, wrapThrowsAsync } from "@formbricks/types/error-handlers";
|
||||
import type { TJsAppConfigUpdateInput, TJsRNConfig } from "@formbricks/types/js";
|
||||
import type { TJsRNConfig, TJsRNConfigUpdateInput } from "@formbricks/types/js";
|
||||
import { RN_ASYNC_STORAGE_KEY } from "../../../js-core/src/lib/constants";
|
||||
|
||||
export class RNConfig {
|
||||
@@ -27,7 +27,7 @@ export class RNConfig {
|
||||
return RNConfig.instance;
|
||||
}
|
||||
|
||||
public update(newConfig: TJsAppConfigUpdateInput): void {
|
||||
public update(newConfig: TJsRNConfigUpdateInput): void {
|
||||
this.config = {
|
||||
...this.config,
|
||||
...newConfig,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type TJsReactNativeConfigInput } from "@formbricks/types/js";
|
||||
import { type TJsRNConfigInput } from "@formbricks/types/js";
|
||||
import { ErrorHandler } from "../../../js-core/src/lib/errors";
|
||||
import { Logger } from "../../../js-core/src/lib/logger";
|
||||
import { trackCodeAction } from "./actions";
|
||||
@@ -9,7 +9,7 @@ const logger = Logger.getInstance();
|
||||
logger.debug("Create command queue");
|
||||
const queue = new CommandQueue();
|
||||
|
||||
export const init = async (initConfig: TJsReactNativeConfigInput): Promise<void> => {
|
||||
export const init = async (initConfig: TJsRNConfigInput): Promise<void> => {
|
||||
ErrorHandler.init(initConfig.errorHandler);
|
||||
queue.add(initialize, false, initConfig);
|
||||
await queue.wait();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type TAttributes } from "@formbricks/types/attributes";
|
||||
import { type TJsRNConfig, type TJsReactNativeConfigInput } from "@formbricks/types/js";
|
||||
import { type TJsRNConfig, type TJsRNConfigInput } from "@formbricks/types/js";
|
||||
import {
|
||||
ErrorHandler,
|
||||
type MissingFieldError,
|
||||
@@ -24,7 +24,7 @@ export const setIsInitialize = (state: boolean): void => {
|
||||
};
|
||||
|
||||
export const initialize = async (
|
||||
c: TJsReactNativeConfigInput
|
||||
c: TJsRNConfigInput
|
||||
): Promise<Result<void, MissingFieldError | NetworkError | MissingPersonError>> => {
|
||||
if (isInitialized) {
|
||||
logger.debug("Already initialized, skipping initialization.");
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { type TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
|
||||
type Listener = (state: TSurvey | null, prevSurvey: TSurvey | null) => void;
|
||||
type Listener = (
|
||||
state: TJsEnvironmentStateSurvey | null,
|
||||
prevSurvey: TJsEnvironmentStateSurvey | null
|
||||
) => void;
|
||||
|
||||
export class SurveyStore {
|
||||
private static instance: SurveyStore | undefined;
|
||||
private survey: TSurvey | null = null;
|
||||
private survey: TJsEnvironmentStateSurvey | null = null;
|
||||
private listeners = new Set<Listener>();
|
||||
|
||||
static getInstance(): SurveyStore {
|
||||
@@ -14,11 +17,11 @@ export class SurveyStore {
|
||||
return SurveyStore.instance;
|
||||
}
|
||||
|
||||
public getSurvey(): TSurvey | null {
|
||||
public getSurvey(): TJsEnvironmentStateSurvey | null {
|
||||
return this.survey;
|
||||
}
|
||||
|
||||
public setSurvey(survey: TSurvey): void {
|
||||
public setSurvey(survey: TJsEnvironmentStateSurvey): void {
|
||||
const prevSurvey = this.survey;
|
||||
if (prevSurvey !== survey) {
|
||||
this.survey = survey;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import type { TAttributes } from "@formbricks/types/attributes";
|
||||
import { type Result, err, ok } from "@formbricks/types/error-handlers";
|
||||
import type { NetworkError } from "@formbricks/types/errors";
|
||||
import type { TJsAppState, TJsAppStateSync, TJsRNSyncParams } from "@formbricks/types/js";
|
||||
import type { TJsRNState, TJsRNStateSync, TJsRNSyncParams } from "@formbricks/types/js";
|
||||
import { Logger } from "../../../js-core/src/lib/logger";
|
||||
import type { RNConfig } from "./config";
|
||||
|
||||
@@ -15,7 +15,7 @@ let syncIntervalId: number | null = null;
|
||||
const syncWithBackend = async (
|
||||
{ apiHost, environmentId, userId }: TJsRNSyncParams,
|
||||
noCache: boolean
|
||||
): Promise<Result<TJsAppStateSync, NetworkError>> => {
|
||||
): Promise<Result<TJsRNStateSync, NetworkError>> => {
|
||||
try {
|
||||
const fetchOptions: RequestInit = {};
|
||||
|
||||
@@ -37,10 +37,10 @@ const syncWithBackend = async (
|
||||
message: "Error syncing with backend",
|
||||
url,
|
||||
responseMessage: jsonRes.message,
|
||||
}) as Result<TJsAppStateSync, NetworkError>;
|
||||
}) as Result<TJsRNStateSync, NetworkError>;
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { data: TJsAppStateSync };
|
||||
const data = (await response.json()) as { data: TJsRNStateSync };
|
||||
const { data: state } = data;
|
||||
|
||||
return ok(state);
|
||||
@@ -63,7 +63,7 @@ export const sync = async (params: TJsRNSyncParams, appConfig: RNConfig, noCache
|
||||
attributes.language = syncResult.data.language;
|
||||
}
|
||||
|
||||
const state: TJsAppState = {
|
||||
const state: TJsRNState = {
|
||||
surveys: syncResult.data.surveys,
|
||||
actionClasses: syncResult.data.actionClasses,
|
||||
project: syncResult.data.project,
|
||||
|
||||
@@ -10,10 +10,9 @@ import { SurveyState } from "@formbricks/lib/surveyState";
|
||||
import { getStyling } from "@formbricks/lib/utils/styling";
|
||||
import type { SurveyInlineProps } from "@formbricks/types/formbricks-surveys";
|
||||
import { ZJsRNWebViewOnMessageData } from "@formbricks/types/js";
|
||||
import type { TJsFileUploadParams } from "@formbricks/types/js";
|
||||
import type { TJsEnvironmentStateSurvey, TJsFileUploadParams } from "@formbricks/types/js";
|
||||
import type { TResponseUpdate } from "@formbricks/types/responses";
|
||||
import type { TUploadFileConfig } from "@formbricks/types/storage";
|
||||
import type { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Logger } from "../../js-core/src/lib/logger";
|
||||
import { getDefaultLanguageCode, getLanguageCode } from "../../js-core/src/lib/utils";
|
||||
import { appConfig } from "./lib/config";
|
||||
@@ -27,7 +26,7 @@ logger.configure({ logLevel: "debug" });
|
||||
const surveyStore = SurveyStore.getInstance();
|
||||
|
||||
interface SurveyWebViewProps {
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
}
|
||||
|
||||
export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | undefined {
|
||||
|
||||
@@ -7,11 +7,12 @@ import { ScrollableContainer } from "@/components/wrappers/ScrollableContainer";
|
||||
import { replaceRecallInfo } from "@/lib/recall";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { TResponseData, TResponseVariables } from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyEndScreenCard, TSurveyRedirectUrlCard } from "@formbricks/types/surveys/types";
|
||||
import { TSurveyEndScreenCard, TSurveyRedirectUrlCard } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface EndingCardProps {
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
endingCard: TSurveyEndScreenCard | TSurveyRedirectUrlCard;
|
||||
isRedirectDisabled: boolean;
|
||||
isResponseSendingFinished: boolean;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { calculateElementIdx } from "@/lib/utils";
|
||||
import { useCallback, useMemo } from "preact/hooks";
|
||||
import { TSurvey, TSurveyQuestionId } from "@formbricks/types/surveys/types";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { TSurveyQuestionId } from "@formbricks/types/surveys/types";
|
||||
import { Progress } from "./Progress";
|
||||
|
||||
interface ProgressBarProps {
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
questionId: TSurveyQuestionId;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,14 @@ import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import type { JSX } from "react";
|
||||
import { evaluateLogic, performActions } from "@formbricks/lib/surveyLogic/utils";
|
||||
import { SurveyBaseProps } from "@formbricks/types/formbricks-surveys";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import type {
|
||||
TResponseData,
|
||||
TResponseDataValue,
|
||||
TResponseTtc,
|
||||
TResponseVariables,
|
||||
} from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyQuestionId } from "@formbricks/types/surveys/types";
|
||||
import { TSurveyQuestionId } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface VariableStackEntry {
|
||||
questionId: TSurveyQuestionId;
|
||||
@@ -53,7 +54,7 @@ export const Survey = ({
|
||||
fullSizeCards = false,
|
||||
autoFocus,
|
||||
}: SurveyBaseProps) => {
|
||||
const [localSurvey, setlocalSurvey] = useState<TSurvey>(survey);
|
||||
const [localSurvey, setlocalSurvey] = useState<TJsEnvironmentStateSurvey>(survey);
|
||||
|
||||
// Update localSurvey when the survey prop changes (it changes in case of survey editor)
|
||||
useEffect(() => {
|
||||
|
||||
@@ -4,8 +4,9 @@ import { replaceRecallInfo } from "@/lib/recall";
|
||||
import { calculateElementIdx } from "@/lib/utils";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { TResponseData, TResponseTtc, TResponseVariables } from "@formbricks/types/responses";
|
||||
import { TI18nString, TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TI18nString } from "@formbricks/types/surveys/types";
|
||||
import { Headline } from "./Headline";
|
||||
import { HtmlBody } from "./HtmlBody";
|
||||
|
||||
@@ -15,7 +16,7 @@ interface WelcomeCardProps {
|
||||
fileUrl?: string;
|
||||
buttonLabel?: TI18nString;
|
||||
onSubmit: (data: TResponseData, ttc: TResponseTtc) => void;
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
languageCode: string;
|
||||
responseCount?: number;
|
||||
autoFocusEnabled: boolean;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AutoCloseProgressBar } from "@/components/general/AutoCloseProgressBar";
|
||||
import React from "preact/compat";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
|
||||
interface AutoCloseProps {
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
onClose: () => void;
|
||||
offset: number;
|
||||
children: React.ReactNode;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import type { JSX } from "react";
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { TProjectStyling } from "@formbricks/types/project";
|
||||
import { TCardArrangementOptions } from "@formbricks/types/styling";
|
||||
import { TSurvey, TSurveyQuestionId, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { TSurveyQuestionId, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
|
||||
// offset = 0 -> Current question card
|
||||
// offset < 0 -> Question cards that are already answered
|
||||
@@ -11,7 +12,7 @@ import { TSurvey, TSurveyQuestionId, TSurveyStyling } from "@formbricks/types/su
|
||||
interface StackedCardsContainerProps {
|
||||
cardArrangement: TCardArrangementOptions;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
getCardContent: (questionIdxTemp: number, offset: number) => JSX.Element | undefined;
|
||||
styling: TProjectStyling | TSurveyStyling;
|
||||
setQuestionId: (questionId: TSurveyQuestionId) => void;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import {
|
||||
TShuffleOption,
|
||||
TSurvey,
|
||||
TSurveyLogic,
|
||||
TSurveyLogicAction,
|
||||
TSurveyQuestion,
|
||||
@@ -61,7 +61,7 @@ export const getShuffledChoicesIds = (
|
||||
return shuffledChoices.map((choice) => choice.id);
|
||||
};
|
||||
|
||||
export const calculateElementIdx = (survey: TSurvey, currentQustionIdx: number): number => {
|
||||
export const calculateElementIdx = (survey: TJsEnvironmentStateSurvey, currentQustionIdx: number): number => {
|
||||
const currentQuestion = survey.questions[currentQustionIdx];
|
||||
const surveyLength = survey.questions.length;
|
||||
const middleIdx = Math.floor(surveyLength / 2);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { TJsFileUploadParams } from "./js";
|
||||
import type { TJsEnvironmentStateSurvey, TJsFileUploadParams } from "./js";
|
||||
import type { TProjectStyling } from "./project";
|
||||
import type { TResponseData, TResponseUpdate } from "./responses";
|
||||
import type { TUploadFileConfig } from "./storage";
|
||||
import type { TSurvey, TSurveyStyling } from "./surveys/types";
|
||||
import type { TSurveyStyling } from "./surveys/types";
|
||||
|
||||
export interface SurveyBaseProps {
|
||||
survey: TSurvey;
|
||||
survey: TJsEnvironmentStateSurvey;
|
||||
styling: TSurveyStyling | TProjectStyling;
|
||||
isBrandingEnabled: boolean;
|
||||
getSetIsError?: (getSetError: (value: boolean) => void) => void;
|
||||
|
||||
@@ -12,71 +12,76 @@ export const ZJsPerson = z.object({
|
||||
userId: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TJsPerson = z.infer<typeof ZJsPerson>;
|
||||
|
||||
// ZSurvey is a refinement, so to extend it to ZSurveyWithTriggers, we need to extend the innerType and then apply the same refinements.
|
||||
const ZSurveyWithTriggers = ZSurvey.innerType()
|
||||
.extend({
|
||||
triggers: z.array(ZActionClass).or(z.array(z.string())),
|
||||
export const ZJsEnvironmentStateSurvey = ZSurvey.innerType()
|
||||
.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
welcomeCard: true,
|
||||
questions: true,
|
||||
variables: true,
|
||||
type: true,
|
||||
showLanguageSwitch: true,
|
||||
languages: true,
|
||||
endings: true,
|
||||
autoClose: true,
|
||||
styling: true,
|
||||
status: true,
|
||||
segment: true,
|
||||
recontactDays: true,
|
||||
displayLimit: true,
|
||||
displayOption: true,
|
||||
hiddenFields: true,
|
||||
triggers: true,
|
||||
displayPercentage: true,
|
||||
delay: true,
|
||||
projectOverwrites: true,
|
||||
})
|
||||
.superRefine(ZSurvey._def.effect.type === "refinement" ? ZSurvey._def.effect.refinement : () => null);
|
||||
|
||||
export type TSurveyWithTriggers = z.infer<typeof ZSurveyWithTriggers>;
|
||||
export type TJsEnvironmentStateSurvey = z.infer<typeof ZJsEnvironmentStateSurvey>;
|
||||
|
||||
export const ZJSWebsiteStateDisplay = z.object({
|
||||
createdAt: z.date(),
|
||||
surveyId: z.string().cuid2(),
|
||||
responded: z.boolean(),
|
||||
});
|
||||
|
||||
export type TJSWebsiteStateDisplay = z.infer<typeof ZJSWebsiteStateDisplay>;
|
||||
|
||||
export const ZJsAppStateSync = z.object({
|
||||
export const ZJsRNStateSync = z.object({
|
||||
person: ZJsPerson.nullish(),
|
||||
userId: z.string().optional(),
|
||||
surveys: z.array(ZSurvey),
|
||||
surveys: z.array(ZJsEnvironmentStateSurvey),
|
||||
actionClasses: z.array(ZActionClass),
|
||||
project: ZProject,
|
||||
language: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TJsAppStateSync = z.infer<typeof ZJsAppStateSync>;
|
||||
export type TJsRNStateSync = z.infer<typeof ZJsRNStateSync>;
|
||||
|
||||
export const ZJsAppState = z.object({
|
||||
export const ZJsRNState = z.object({
|
||||
attributes: ZAttributes,
|
||||
surveys: z.array(ZSurvey),
|
||||
surveys: z.array(ZJsEnvironmentStateSurvey),
|
||||
actionClasses: z.array(ZActionClass),
|
||||
project: ZProject,
|
||||
});
|
||||
|
||||
export type TJsAppState = z.infer<typeof ZJsAppState>;
|
||||
export type TJsRNState = z.infer<typeof ZJsRNState>;
|
||||
|
||||
export const ZJsAppConfigUpdateInput = z.object({
|
||||
export const ZJsRNConfigUpdateInput = z.object({
|
||||
environmentId: z.string().cuid2(),
|
||||
apiHost: z.string(),
|
||||
userId: z.string(),
|
||||
state: ZJsAppState,
|
||||
state: ZJsRNState,
|
||||
expiresAt: z.date(),
|
||||
status: z.enum(["success", "error"]).optional(),
|
||||
});
|
||||
|
||||
export type TJsAppConfigUpdateInput = z.infer<typeof ZJsAppConfigUpdateInput>;
|
||||
export type TJsRNConfigUpdateInput = z.infer<typeof ZJsRNConfigUpdateInput>;
|
||||
|
||||
export const ZJsRNConfig = z.object({
|
||||
environmentId: z.string().cuid(),
|
||||
apiHost: z.string(),
|
||||
userId: z.string(),
|
||||
state: ZJsAppState,
|
||||
state: ZJsRNState,
|
||||
expiresAt: z.date(),
|
||||
status: z.enum(["success", "error"]).optional(),
|
||||
});
|
||||
|
||||
export type TJsRNConfig = z.infer<typeof ZJsRNConfig>;
|
||||
|
||||
export const ZJsWebsiteStateSync = ZJsAppStateSync.omit({ person: true });
|
||||
|
||||
export type TJsWebsiteStateSync = z.infer<typeof ZJsWebsiteStateSync>;
|
||||
|
||||
export const ZJsRNSyncParams = z.object({
|
||||
environmentId: z.string().cuid(),
|
||||
apiHost: z.string(),
|
||||
@@ -85,23 +90,34 @@ export const ZJsRNSyncParams = z.object({
|
||||
});
|
||||
|
||||
export type TJsRNSyncParams = z.infer<typeof ZJsRNSyncParams>;
|
||||
|
||||
export const ZJsWebsiteState = z.object({
|
||||
surveys: z.array(ZSurvey),
|
||||
actionClasses: z.array(ZActionClass),
|
||||
project: ZProject,
|
||||
displays: z.array(ZJSWebsiteStateDisplay),
|
||||
attributes: ZAttributes.optional(),
|
||||
export const ZJsEnvironmentStateActionClass = ZActionClass.pick({
|
||||
id: true,
|
||||
key: true,
|
||||
type: true,
|
||||
name: true,
|
||||
noCodeConfig: true,
|
||||
});
|
||||
|
||||
export type TJsWebsiteState = z.infer<typeof ZJsWebsiteState>;
|
||||
export type TJsEnvironmentStateActionClass = z.infer<typeof ZJsEnvironmentStateActionClass>;
|
||||
|
||||
export const ZJsEnvironmentStateProject = ZProject.pick({
|
||||
id: true,
|
||||
recontactDays: true,
|
||||
clickOutsideClose: true,
|
||||
darkOverlay: true,
|
||||
placement: true,
|
||||
inAppSurveyBranding: true,
|
||||
styling: true,
|
||||
});
|
||||
|
||||
export type TJsEnvironmentStateProject = z.infer<typeof ZJsEnvironmentStateProject>;
|
||||
|
||||
export const ZJsEnvironmentState = z.object({
|
||||
expiresAt: z.date(),
|
||||
data: z.object({
|
||||
surveys: z.array(ZSurvey),
|
||||
actionClasses: z.array(ZActionClass),
|
||||
project: ZProject,
|
||||
surveys: z.array(ZJsEnvironmentStateSurvey),
|
||||
actionClasses: z.array(ZJsEnvironmentStateActionClass),
|
||||
project: ZJsEnvironmentStateProject,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -143,7 +159,7 @@ export const ZJsConfig = z.object({
|
||||
apiHost: z.string(),
|
||||
environmentState: ZJsEnvironmentState,
|
||||
personState: ZJsPersonState,
|
||||
filteredSurveys: z.array(ZSurvey).default([]),
|
||||
filteredSurveys: z.array(ZJsEnvironmentStateSurvey).default([]),
|
||||
attributes: z.record(z.string()),
|
||||
status: z.object({
|
||||
value: z.enum(["success", "error"]),
|
||||
@@ -164,15 +180,6 @@ export const ZJsConfigUpdateInput = ZJsConfig.omit({ status: true }).extend({
|
||||
|
||||
export type TJsConfigUpdateInput = z.infer<typeof ZJsConfigUpdateInput>;
|
||||
|
||||
export const ZJsWebsiteConfigInput = z.object({
|
||||
environmentId: z.string().cuid2(),
|
||||
apiHost: z.string(),
|
||||
errorHandler: z.function().args(z.any()).returns(z.void()).optional(),
|
||||
attributes: z.record(z.string()).optional(),
|
||||
});
|
||||
|
||||
export type TJsWebsiteConfigInput = z.infer<typeof ZJsWebsiteConfigInput>;
|
||||
|
||||
export const ZJsConfigInput = z.object({
|
||||
environmentId: z.string().cuid2(),
|
||||
apiHost: z.string(),
|
||||
@@ -183,8 +190,8 @@ export const ZJsConfigInput = z.object({
|
||||
|
||||
export type TJsConfigInput = z.infer<typeof ZJsConfigInput>;
|
||||
|
||||
export const ZJsReactNativeConfigInput = ZJsConfigInput.omit({ userId: true }).extend({ userId: z.string() });
|
||||
export type TJsReactNativeConfigInput = z.infer<typeof ZJsReactNativeConfigInput>;
|
||||
export const ZJsRNConfigInput = ZJsConfigInput.omit({ userId: true }).extend({ userId: z.string() });
|
||||
export type TJsRNConfigInput = z.infer<typeof ZJsRNConfigInput>;
|
||||
|
||||
export const ZJsPeopleUserIdInput = z.object({
|
||||
environmentId: z.string().cuid2(),
|
||||
|
||||
827
pnpm-lock.yaml
generated
827
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user