Compare commits
18 Commits
tolgee-bui
...
feat/heic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa7f52a72b | ||
|
|
7d4409e2b4 | ||
|
|
64a385b835 | ||
|
|
ee2573d128 | ||
|
|
d082e7c44d | ||
|
|
5306aecec4 | ||
|
|
cdc332d44e | ||
|
|
2e58f337b0 | ||
|
|
1390b071ea | ||
|
|
d5a4a4736b | ||
|
|
57725783e8 | ||
|
|
8f5527ac34 | ||
|
|
3632c9ec94 | ||
|
|
1acf334ad3 | ||
|
|
a526bb08be | ||
|
|
787cf2d56f | ||
|
|
12b7dbd567 | ||
|
|
423466d30a |
@@ -138,7 +138,7 @@ export const EditLogo = ({ project, environmentId, isReadOnly }: EditLogoProps)
|
||||
) : (
|
||||
<FileInput
|
||||
id="logo-input"
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
|
||||
environmentId={environmentId}
|
||||
onFileUpload={(files: string[]) => {
|
||||
setLogoUrl(files[0]);
|
||||
@@ -151,7 +151,7 @@ export const EditLogo = ({ project, environmentId, isReadOnly }: EditLogoProps)
|
||||
<Input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/jpeg, image/png, image/webp"
|
||||
accept="image/jpeg, image/png, image/webp, image/heic"
|
||||
className="hidden"
|
||||
disabled={isReadOnly}
|
||||
onChange={handleFileChange}
|
||||
|
||||
@@ -308,7 +308,7 @@ export const QuestionFormInput = ({
|
||||
{showImageUploader && id === "headline" && (
|
||||
<FileInput
|
||||
id="question-image"
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
|
||||
environmentId={localSurvey.environmentId}
|
||||
onFileUpload={(url: string[] | undefined, fileType: "image" | "video") => {
|
||||
if (url) {
|
||||
|
||||
@@ -114,7 +114,7 @@ export const EditWelcomeCard = ({
|
||||
<div className="mt-3 flex w-full items-center justify-center">
|
||||
<FileInput
|
||||
id="welcome-card-image"
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
|
||||
environmentId={environmentId}
|
||||
onFileUpload={(url: string[]) => {
|
||||
updateSurvey({ fileUrl: url[0] });
|
||||
|
||||
@@ -16,7 +16,7 @@ export const UploadImageSurveyBg = ({
|
||||
<div className="flex w-full items-center justify-center">
|
||||
<FileInput
|
||||
id="survey-bg-file-input"
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
|
||||
environmentId={environmentId}
|
||||
onFileUpload={(url: string[]) => {
|
||||
if (url.length > 0) {
|
||||
|
||||
@@ -133,7 +133,7 @@ export const PictureSelectionForm = ({
|
||||
<div className="mt-3 flex w-full items-center justify-center">
|
||||
<FileInput
|
||||
id="choices-file-input"
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
|
||||
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
|
||||
environmentId={environmentId}
|
||||
onFileUpload={handleFileInputChanges}
|
||||
fileUrl={question?.choices?.map((choice) => choice.imageUrl)}
|
||||
|
||||
@@ -68,7 +68,7 @@ export const FileInput = ({
|
||||
toast.error(t("common.only_one_file_allowed"));
|
||||
}
|
||||
|
||||
const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
|
||||
const allowedFiles = await getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
|
||||
|
||||
if (allowedFiles.length === 0) {
|
||||
return;
|
||||
@@ -137,7 +137,7 @@ export const FileInput = ({
|
||||
};
|
||||
|
||||
const handleUploadMore = async (files: File[]) => {
|
||||
const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
|
||||
const allowedFiles = await getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
|
||||
if (allowedFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
29
apps/web/modules/ui/components/file-input/lib/actions.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
"use server";
|
||||
|
||||
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
||||
import { z } from "zod";
|
||||
|
||||
const ZConvertHeicToJpegInput = z.object({
|
||||
file: z.instanceof(File),
|
||||
});
|
||||
|
||||
export const convertHeicToJpegAction = authenticatedActionClient
|
||||
.schema(ZConvertHeicToJpegInput)
|
||||
.action(async ({ parsedInput }) => {
|
||||
if (!parsedInput.file || !parsedInput.file.name.endsWith(".heic")) return parsedInput.file;
|
||||
|
||||
const convert = (await import("heic-convert")).default;
|
||||
|
||||
const arrayBuffer = await parsedInput.file.arrayBuffer();
|
||||
const nodeBuffer = Buffer.from(arrayBuffer) as unknown as ArrayBufferLike;
|
||||
|
||||
const convertedBuffer = await convert({
|
||||
buffer: nodeBuffer,
|
||||
format: "JPEG",
|
||||
quality: 0.9,
|
||||
});
|
||||
|
||||
return new File([convertedBuffer], parsedInput.file.name.replace(/\.heic$/, ".jpg"), {
|
||||
type: "image/jpeg",
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { toast } from "react-hot-toast";
|
||||
import { TAllowedFileExtension } from "@formbricks/types/common";
|
||||
import { convertHeicToJpegAction } from "./actions";
|
||||
|
||||
export const uploadFile = async (
|
||||
file: File | Blob,
|
||||
@@ -15,8 +16,6 @@ export const uploadFile = async (
|
||||
|
||||
const fileBuffer = await file.arrayBuffer();
|
||||
|
||||
// check the file size
|
||||
|
||||
const bufferBytes = fileBuffer.byteLength;
|
||||
const bufferKB = bufferBytes / 1024;
|
||||
|
||||
@@ -74,7 +73,6 @@ export const uploadFile = async (
|
||||
});
|
||||
}
|
||||
|
||||
// Add the actual file to be uploaded
|
||||
formData.append("file", file);
|
||||
|
||||
const uploadResponse = await fetch(signedUrl, {
|
||||
@@ -96,34 +94,63 @@ export const uploadFile = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllowedFiles = (
|
||||
const isFileSizeExceed = (fileSizeInMB: number, maxSizeInMB?: number) => {
|
||||
if (maxSizeInMB && fileSizeInMB > maxSizeInMB) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const getAllowedFiles = async (
|
||||
files: File[],
|
||||
allowedFileExtensions: string[],
|
||||
maxSizeInMB?: number
|
||||
): File[] => {
|
||||
): Promise<File[]> => {
|
||||
const sizeExceedFiles: string[] = [];
|
||||
const unsupportedExtensionFiles: string[] = [];
|
||||
const convertedFiles: File[] = [];
|
||||
|
||||
const allowedFiles = files.filter((file) => {
|
||||
for (const file of files) {
|
||||
if (!file || !file.type) {
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const extension = file.name.split(".").pop();
|
||||
const fileSizeInMB = file.size / 1000000; // Kb -> Mb
|
||||
const extension = file.name.split(".").pop()?.toLowerCase();
|
||||
const fileSizeInMB = file.size / 1000000;
|
||||
|
||||
if (!allowedFileExtensions.includes(extension as TAllowedFileExtension)) {
|
||||
unsupportedExtensionFiles.push(file.name);
|
||||
return false; // Exclude file if extension not allowed
|
||||
} else if (maxSizeInMB && fileSizeInMB > maxSizeInMB) {
|
||||
sizeExceedFiles.push(file.name);
|
||||
return false; // Exclude files larger than the maximum size
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (isFileSizeExceed(fileSizeInMB, maxSizeInMB)) {
|
||||
sizeExceedFiles.push(file.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extension === "heic") {
|
||||
const convertedFileResponse = await convertHeicToJpegAction({ file });
|
||||
if (!convertedFileResponse?.data) {
|
||||
unsupportedExtensionFiles.push(file.name);
|
||||
continue;
|
||||
} else {
|
||||
const convertedFileSizeInMB = convertedFileResponse.data.size / 1000000;
|
||||
if (isFileSizeExceed(convertedFileSizeInMB, maxSizeInMB)) {
|
||||
sizeExceedFiles.push(file.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
const convertedFile = new File([convertedFileResponse.data], file.name.replace(/\.heic$/, ".jpg"), {
|
||||
type: "image/jpeg",
|
||||
});
|
||||
convertedFiles.push(convertedFile);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
convertedFiles.push(file);
|
||||
}
|
||||
|
||||
// Constructing toast messages based on the issues found
|
||||
let toastMessage = "";
|
||||
if (sizeExceedFiles.length > 0) {
|
||||
toastMessage += `Files exceeding size limit (${maxSizeInMB} MB): ${sizeExceedFiles.join(", ")}. `;
|
||||
@@ -134,7 +161,7 @@ export const getAllowedFiles = (
|
||||
if (toastMessage) {
|
||||
toast.error(toastMessage);
|
||||
}
|
||||
return allowedFiles;
|
||||
return convertedFiles;
|
||||
};
|
||||
|
||||
export const checkForYoutubePrivacyMode = (url: string): boolean => {
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"file-loader": "6.2.0",
|
||||
"framer-motion": "11.15.0",
|
||||
"googleapis": "144.0.0",
|
||||
"heic-convert": "2.1.0",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"jiti": "2.4.1",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
@@ -129,6 +130,7 @@
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"@neshca/cache-handler": "1.9.0",
|
||||
"@types/bcryptjs": "2.4.6",
|
||||
"@types/heic-convert": "2.1.0",
|
||||
"@types/lodash": "4.17.13",
|
||||
"@types/markdown-it": "14.1.2",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
# Mintlify Starter Kit
|
||||
# Formbricks Documentation
|
||||
|
||||
Click on `Use this template` to copy the Mintlify starter kit. The starter kit contains examples including
|
||||
This documentation is built using Mintlify. Here's how to run it locally and contribute.
|
||||
|
||||
- Guide pages
|
||||
- Navigation
|
||||
- Customizations
|
||||
- API Reference pages
|
||||
- Use of popular components
|
||||
## Local Development
|
||||
|
||||
### Development
|
||||
1. Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify):
|
||||
|
||||
Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation changes locally. To install, use the following command
|
||||
|
||||
```
|
||||
```bash
|
||||
npm i -g mintlify
|
||||
```
|
||||
|
||||
Run the following command at the root of your documentation (where mint.json is)
|
||||
2. Clone the Formbricks repository and navigate to the docs folder:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/formbricks/formbricks.git
|
||||
cd formbricks/docs
|
||||
```
|
||||
|
||||
3. Run the documentation locally:
|
||||
|
||||
```bash
|
||||
mintlify dev
|
||||
```
|
||||
|
||||
### Publishing Changes
|
||||
The documentation will be available at `http://localhost:3000`.
|
||||
|
||||
Install our Github App to auto propagate changes from your repo to your deployment. Changes will be deployed to production automatically after pushing to the default branch. Find the link to install on your dashboard.
|
||||
### Contributing
|
||||
|
||||
#### Troubleshooting
|
||||
1. Create a new branch for your changes
|
||||
2. Make your documentation updates
|
||||
3. Submit a pull request to the main repository
|
||||
|
||||
- Mintlify dev isn't running - Run `mintlify install` it'll re-install dependencies.
|
||||
- Page loads as a 404 - Make sure you are running in a folder with `mint.json`
|
||||
### Troubleshooting
|
||||
|
||||
- If Mintlify dev isn't running, try `mintlify install` to reinstall dependencies
|
||||
- If a page loads as a 404, ensure you're in the `docs` folder with the `mint.json` file
|
||||
- For other issues, please check our [Contributing Guidelines](./CONTRIBUTING.md)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "Contributing to Formbricks 🤗"
|
||||
title: "Contribute to Formbricks"
|
||||
description: "How to contribute to Formbricks"
|
||||
icon: "code"
|
||||
---
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "How to setup SAML with Identity Providers"
|
||||
---
|
||||
|
||||
### SAML Registration with Identity Providers
|
||||
|
||||
This guide explains the settings you need to use to configure SAML with your Identity Provider. Once configured, obtain an XML metadata file and upload it on your Formbricks instance.
|
||||
|
||||
> **Note:** Please do not add a trailing slash at the end of the URLs. Create them exactly as shown below.
|
||||
|
||||
**Assertion consumer service URL / Single Sign-On URL / Destination URL:** https://app.formbricks.com/api/auth/saml/callback
|
||||
|
||||
**Entity ID / Identifier / Audience URI / Audience Restriction:** [https://saml.formbricks.com](https://saml.formbricks.com)
|
||||
|
||||
**Response:** Signed
|
||||
|
||||
**Assertion Signature:** Signed
|
||||
|
||||
**Signature Algorithm:** RSA-SHA256
|
||||
|
||||
**Assertion Encryption:** Unencrypted
|
||||
|
||||
**Mapping Attributes / Attribute Statements:**
|
||||
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier -> id
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress -> email
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname -> firstName
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname -> lastName
|
||||
|
||||
### SAML With Okta
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an application with your OIDC provider">
|
||||
For example, in Okta, once you create an account, you can click on Applications on the sidebar menu:
|
||||
<img src="/images/development/guides/auth-and-provision/okta/okta-applications.webp" />
|
||||
</Step>
|
||||
<Step title="Click on Create App Integration">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/create-app-integration.webp" />
|
||||
</Step>
|
||||
<Step title="Select SAML 2.0 in the modal form, and click Next">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/select-saml-2.0.webp" />
|
||||
</Step>
|
||||
<Step title="Fill the general settings as shown and click Next">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/general-settings.webp" />
|
||||
</Step>
|
||||
<Step title="Fill the fields mapping as shown and click Next">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/fields-mapping.webp" />
|
||||
</Step>
|
||||
<Step title="Enter the SAML Integration Settings as shown and click Next">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/saml-integration-settings.webp" />
|
||||
</Step>
|
||||
<Step title="Check the internal app checkbox and click Finish">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/internal-app.webp" />
|
||||
</Step>
|
||||
<Step title="Check that the app is created successfully">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/app-created.webp" />
|
||||
</Step>
|
||||
<Step title="Click on the app and head over to the Assignments tab">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/assignments-tab.webp" />
|
||||
</Step>
|
||||
<Step title="Click on Assign button and select Assign to People">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/assign-to-people.webp" />
|
||||
</Step>
|
||||
<Step title="Select the users you want to assign the app to and click Assign">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/select-users.webp" />
|
||||
</Step>
|
||||
<Step title="Head over to the Sign On tab and scroll to the bottom to get the metadata, click on the Actions button">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/actions-button.webp" />
|
||||
</Step>
|
||||
<Step title="Click on View IdP metadata">
|
||||
<img src="/images/development/guides/auth-and-provision/okta/view-idp-metadata.webp" />
|
||||
</Step>
|
||||
<Step title="Copy the metadata and paste it in the Formbricks SAML configuration"></Step>
|
||||
</Steps>
|
||||
|
||||
That's it. Now when you try to login with SSO, your application on Okta will handle the authentication.
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Development
|
||||
description: Learn how to setup formbricks locally and develop on it
|
||||
title: Overview
|
||||
description: Learn how to setup formbricks locally and build custom integrations and services.
|
||||
icon: "code"
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
---
|
||||
title: File and Directory Org.
|
||||
description: This document outlines the structure and organization of files and directories in the Formbricks codebase, providing guidelines for maintaining consistency and clarity across the project.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
## Project Structure Overview
|
||||
|
||||
The Formbricks codebase follows a monorepo structure using pnpm workspaces, with two main directories:
|
||||
|
||||
* `apps/` - Contains full applications
|
||||
|
||||
* `packages/` - Contains shared libraries and utilities
|
||||
|
||||
## Apps Directory
|
||||
|
||||
The `apps/` directory contains complete applications:
|
||||
|
||||
### apps/web/
|
||||
|
||||
* Main Formbricks web application (Next.js)
|
||||
|
||||
* Primary application with full feature set
|
||||
|
||||
* Uses App Router architecture
|
||||
|
||||
* Contains environment-specific settings and configurations
|
||||
|
||||
### apps/demo/
|
||||
|
||||
* Demo application (Next.js)
|
||||
|
||||
* Showcases Formbricks in-product surveying functionality
|
||||
|
||||
* Used for testing and demonstration purposes
|
||||
|
||||
### apps/demo-react-native/
|
||||
|
||||
* React Native demo app (React Native)
|
||||
|
||||
* Demonstrates mobile integration capabilities
|
||||
|
||||
* Example implementation for React Native
|
||||
|
||||
### apps/storybook/
|
||||
|
||||
* Component documentation
|
||||
|
||||
* Visual documentation of UI components
|
||||
|
||||
* Testing environment for isolated components
|
||||
|
||||
## Packages Directory
|
||||
|
||||
The `packages/` directory contains shared libraries and utilities:
|
||||
|
||||
### packages/js-core/
|
||||
|
||||
* Contains core functionality for in-product surveys
|
||||
|
||||
* Shared logic between different SDK implementations
|
||||
|
||||
* Base classes and utilities
|
||||
|
||||
### packages/js/
|
||||
|
||||
* JavaScript SDK for browser-based applications
|
||||
|
||||
* Used for running surveys on websites and web apps (browser)
|
||||
|
||||
* Public NPM package
|
||||
|
||||
### packages/react-native/
|
||||
|
||||
* React Native SDK
|
||||
|
||||
* Used to run surveys in mobile apps built with React Native / Expo
|
||||
|
||||
* Includes native platform adaptations
|
||||
|
||||
### packages/lib/
|
||||
|
||||
* Shared business logic
|
||||
|
||||
* Shared utilities and helpers
|
||||
|
||||
### packages/types/
|
||||
|
||||
* TypeScript type definitions
|
||||
|
||||
* Zod schemas for validation
|
||||
|
||||
### packages/database/
|
||||
|
||||
* Database schemas and migrations
|
||||
|
||||
* Prisma schema definitions
|
||||
|
||||
* Migration management
|
||||
|
||||
### packages/surveys/
|
||||
|
||||
* Survey-specific functionality
|
||||
|
||||
* Survey rendering logic and UI components
|
||||
|
||||
* Survey state management
|
||||
|
||||
## Module Organization
|
||||
|
||||
### Core Module Structure
|
||||
|
||||
Each feature module follows a consistent structure:
|
||||
|
||||
```
|
||||
modules/
|
||||
└── ee/
|
||||
├── insights/
|
||||
│ ├── components/
|
||||
│ ├── experience/
|
||||
│ └── types/
|
||||
└── contacts/
|
||||
├── segments/
|
||||
└── components/
|
||||
```
|
||||
|
||||
## Adding New Code
|
||||
|
||||
### New Features
|
||||
|
||||
When adding new features, follow these guidelines:
|
||||
|
||||
1. **Determine Scope**:
|
||||
|
||||
* Complete application → `apps/`
|
||||
|
||||
* Shared library → `packages/`
|
||||
|
||||
* Feature for existing app → appropriate module in `apps/web/modules/`
|
||||
|
||||
2. **Module Creation**:
|
||||
Create a new module with the standard structure:
|
||||
|
||||
```
|
||||
modules/
|
||||
└── your-feature/
|
||||
├── components/ # React components
|
||||
├── lib/ # Business logic
|
||||
├── types/ # TypeScript types
|
||||
├── actions.ts # Server actions
|
||||
└── route.ts # API routes
|
||||
```
|
||||
|
||||
3. **Component Organization**:
|
||||
|
||||
* Base UI components → `modules/ui/components/`
|
||||
|
||||
* Feature-specific components → `modules/[feature]/components/`
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Code Organization
|
||||
|
||||
* Keep modules focused and single-purpose
|
||||
|
||||
* Maintain clear separation between UI and business logic
|
||||
|
||||
* Use proper TypeScript interfaces and types
|
||||
|
||||
### File Structure
|
||||
|
||||
* Group related files in descriptive directories
|
||||
|
||||
* Use consistent naming patterns
|
||||
|
||||
* Keep files focused and modular
|
||||
|
||||
### Module Independence
|
||||
|
||||
* Minimize dependencies between modules
|
||||
|
||||
* Share common utilities through appropriate packages
|
||||
|
||||
* Maintain clear module boundaries
|
||||
|
||||
### Documentation
|
||||
|
||||
* Document complex logic and APIs as laid out in the [Documentation](/development/standards/practices/documentation) section
|
||||
|
||||
* Keep documentation current with code changes
|
||||
|
||||
## Testing Organization
|
||||
|
||||
### Test File Location
|
||||
|
||||
* Test files should be located alongside the code they test
|
||||
|
||||
* Use `.test.ts` or `.spec.ts` suffix for test files
|
||||
|
||||
* Example: `user-service.test.ts` for `user-service.ts`
|
||||
|
||||
### Test Directory Structure
|
||||
|
||||
```
|
||||
feature/
|
||||
├── tests/ # Test directory (if grouping tests)
|
||||
├── components/ # Feature components
|
||||
└── lib/ # Business logic
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### Root Level Configuration
|
||||
|
||||
* `.eslintrc.js` - ESLint configuration
|
||||
|
||||
* `tsconfig.json` - TypeScript configuration
|
||||
|
||||
* `package.json` - Package metadata and scripts
|
||||
|
||||
* `.env` - Environment variables
|
||||
|
||||
### Package Level Configuration
|
||||
|
||||
Each package maintains its own configuration files:
|
||||
|
||||
* `package.json` - Package-specific dependencies and scripts
|
||||
|
||||
* `tsconfig.json` - Package-specific TypeScript settings
|
||||
|
||||
* `.eslintrc.js` - Package-specific linting rules
|
||||
|
||||
## Version Control
|
||||
|
||||
### Git Organization
|
||||
|
||||
* `.gitignore` - Specifies ignored files and directories
|
||||
|
||||
* `.github/` - GitHub specific configurations and workflows
|
||||
|
||||
* `CHANGELOG.md` - Documents version changes
|
||||
|
||||
* `LICENSE` - License information
|
||||
|
||||
## Conclusion
|
||||
|
||||
Following these organizational patterns ensures:
|
||||
|
||||
* Consistent code structure across the project
|
||||
|
||||
* Easy navigation and maintenance
|
||||
|
||||
* Clear separation of concerns
|
||||
|
||||
* Scalable architecture for future growth
|
||||
|
||||
Remember to maintain these patterns when adding new code to keep the codebase organized and maintainable.
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Modules & Components
|
||||
description: In this document we outline the best practices for organizing modules and components across the codebase. Clear separation of concerns between modules (which encompass business logic and domain-specific functionality) and components (reusable UI elements and building blocks) promotes clarity, scalability, and maintainability.
|
||||
icon: boxes-stacked
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Our codebase follows a modular architecture with two primary organizational units:
|
||||
|
||||
1. **UI Components**: Reusable components in the `modules/ui/components` directory
|
||||
2. **Feature Modules**: Domain-specific functionality organized by feature in the `modules` directory
|
||||
|
||||
## Module Organization
|
||||
|
||||
### Core Module Structure
|
||||
|
||||
Modules are organized by feature and can be found in the `modules` directory. Each module typically includes:
|
||||
|
||||
```
|
||||
modules/
|
||||
└── feature-name/
|
||||
├── components/ # Feature-specific components
|
||||
├── lib/ # Business logic and utilities
|
||||
├── types/ # TypeScript types
|
||||
├── actions.ts # Server actions
|
||||
└── route.ts # API routes (if needed)
|
||||
```
|
||||
|
||||
### Enterprise Edition (EE) Modules
|
||||
|
||||
Enterprise features are organized in a dedicated `modules/ee` directory:
|
||||
|
||||
```
|
||||
modules/
|
||||
└── ee/
|
||||
├── insights/
|
||||
│ ├── components/
|
||||
│ ├── experience/
|
||||
│ └── types/
|
||||
└── contacts/
|
||||
├── segments/
|
||||
└── components/
|
||||
```
|
||||
|
||||
## Component Organization
|
||||
|
||||
### UI Component Structure
|
||||
|
||||
UI components are centralized in `modules/ui/components` and follow a consistent structure:
|
||||
|
||||
```
|
||||
modules/ui/components/
|
||||
└── component-name/
|
||||
├── index.tsx # Main component implementation
|
||||
├── stories.tsx # Storybook stories
|
||||
└── components/ # Sub-components (if needed)
|
||||
```
|
||||
|
||||
### Component Types
|
||||
|
||||
1. **Base Components**: Fundamental UI elements like Button, Input, Modal
|
||||
|
||||
2. **Composite Components**: More complex components that combine base components
|
||||
|
||||
3. **Feature-Specific Components**: Components tied to specific features
|
||||
|
||||
|
||||
### Feature Module Example
|
||||
|
||||
A feature module with its components and business logic:
|
||||
|
||||
```
|
||||
modules/survey/
|
||||
├── components/
|
||||
│ ├── question-form-input/
|
||||
│ └── template-list/
|
||||
├── editor/
|
||||
│ └── components/
|
||||
├── lib/
|
||||
│ └── utils.ts
|
||||
└── types/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Component Organization**
|
||||
- Keep components focused and single-purpose
|
||||
- Use proper TypeScript interfaces for props
|
||||
- Implement Storybook stories for UI components
|
||||
|
||||
2. **Module Structure**
|
||||
- Organize by feature domain
|
||||
- Separate business logic from UI components
|
||||
- Use proper type definitions
|
||||
|
||||
3. **Code Sharing**
|
||||
- Share common utilities through the ui/lib directory
|
||||
- Maintain clear boundaries between modules
|
||||
- Use proper imports with aliases (@/modules/...)
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: Naming Conventions
|
||||
description: This section outlines the guidelines for naming conventions across the codebase, ensuring consistency and clarity in the project.
|
||||
icon: input-text
|
||||
---
|
||||
|
||||
## Files and Directories
|
||||
|
||||
### General Files
|
||||
- Use lowercase and hyphen-separated names (kebab-case) for files and directories
|
||||
- ✅ `user-profile.ts`
|
||||
- ❌ `UserProfile.ts`
|
||||
- Group related files in directories with descriptive plural names
|
||||
- ✅ `components`, `services`, `utils`
|
||||
- ❌ `component`, `util`
|
||||
|
||||
### Special Files
|
||||
- Configuration files should follow framework conventions
|
||||
- ✅ `next.config.mjs`, `tailwind.config.js`
|
||||
- Test files should mirror source files with `.test` or `.spec` suffix
|
||||
- ✅ `user-service.test.ts` for `user-service.ts`
|
||||
- Database migration files should include timestamp and description
|
||||
- ✅ `20241017124431_add_documents_and_insights.sql`
|
||||
|
||||
## Code Symbols
|
||||
|
||||
### Variables and Functions
|
||||
- Use camelCase for variables and function names
|
||||
- ✅ `fetchUserData`, `isLoading`, `handleSubmit`
|
||||
- ❌ `FetchUserData`, `is_loading`
|
||||
- Boolean variables should use is/has/should prefix
|
||||
- ✅ `isVerifyEmailEnabled`, `hasPermission`, `shouldDisplay`
|
||||
- Async functions should use verb prefixes suggesting async
|
||||
- ✅ `fetchData`, `createUser`, `updateProfile`
|
||||
|
||||
### Classes and Types
|
||||
- Use PascalCase for:
|
||||
- Classes: `Config`, `Client`, `ResponseAPI`
|
||||
- Interfaces: `TSurveySummaryResponse`, `ApiConfig`
|
||||
- Type aliases: `TResponseData`, `TJsTrackProperties`
|
||||
- Prefix types with T and interfaces with I (when helpful)
|
||||
- ✅ `TStats`, `TResponseData`, `IApiConfig`
|
||||
- Enum names should be PascalCase, values in UPPER_SNAKE_CASE
|
||||
```typescript
|
||||
enum ProjectFeatureKeys {
|
||||
FREE = "free",
|
||||
STARTUP = "startup",
|
||||
SCALE = "scale"
|
||||
}
|
||||
```
|
||||
|
||||
### Constants
|
||||
- Use UPPER_SNAKE_CASE for constant values
|
||||
- ✅ `API_TIMEOUT`, `MAX_RETRIES`, `CONTAINER_ID`
|
||||
- Use PascalCase for constant references/objects
|
||||
- ✅ `ErrorCodes`, `Config`
|
||||
|
||||
### Database Models
|
||||
- Use PascalCase singular form for model names
|
||||
- ✅ `Survey`, `Response`, `Document`
|
||||
- Use camelCase for field names
|
||||
- ✅ `createdAt`, `environmentId`, `isSpam`
|
||||
- Use snake_case for database column names
|
||||
- ✅ `created_at`, `updated_at`
|
||||
|
||||
### Components
|
||||
- Use PascalCase for React components and their files
|
||||
- ✅ `SurveyCard.tsx`, `UserProfile.tsx`
|
||||
- Component-specific types should be prefixed with component name
|
||||
- ✅ `SurveyCardProps`, `UserProfileData`
|
||||
|
||||
### API and Endpoints
|
||||
- Use kebab-case for API endpoints
|
||||
- ✅ `/api/user-profile`, `/api/survey-responses`
|
||||
- Use camelCase for query parameters
|
||||
- ✅ `/api/surveys?pageSize=10&sortOrder=desc`
|
||||
|
||||
## Schema and Validation
|
||||
- Prefix Zod schemas with Z
|
||||
- ✅ `ZSurvey`, `ZDocument`, `ZInsight`
|
||||
- Use descriptive names for validation schemas
|
||||
- ✅ `ZUpdateDocumentAction`, `ZGenerateDocumentObjectSchema`
|
||||
|
||||
## Error Handling
|
||||
- Suffix error classes with "Error"
|
||||
- ✅ `ValidationError`, `DatabaseError`, `AuthenticationError`
|
||||
- Use descriptive names for error types
|
||||
- ✅ `SURVEY_NOT_FOUND`, `INVALID_RESPONSE`
|
||||
|
||||
## Best Practices
|
||||
- Keep names descriptive but concise
|
||||
- Be consistent within each context
|
||||
- Follow existing patterns in the codebase
|
||||
- Use full words instead of abbreviations unless widely accepted
|
||||
- ✅ `configuration` vs ❌ `config` (except in standard terms)
|
||||
- ✅ `id`, `url` (standard abbreviations are acceptable)
|
||||
|
||||
By following these conventions, we maintain consistency and clarity across the codebase, making it more maintainable and easier to understand for all team members.
|
||||
184
docs/development/standards/practices/code-formatting.mdx
Normal file
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: "Code Formatting"
|
||||
description: "Standards for consistent code formatting across the Formbricks project"
|
||||
icon: code
|
||||
---
|
||||
|
||||
|
||||
## Core Configuration
|
||||
|
||||
### Prettier Config
|
||||
Formbricks uses a standardized Prettier configuration defined in `@formbricks/config-prettier`:
|
||||
```javascript
|
||||
module.exports = {
|
||||
bracketSpacing: true,
|
||||
bracketSameLine: true,
|
||||
singleQuote: false,
|
||||
jsxSingleQuote: false,
|
||||
trailingComma: "es5",
|
||||
semi: true,
|
||||
printWidth: 110,
|
||||
arrowParens: "always",
|
||||
importOrder: [
|
||||
// Mocks must be at the top as they contain vi.mock calls
|
||||
"(.*)/__mocks__/(.*)",
|
||||
"server-only",
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"^@formbricks/(.*)$",
|
||||
"^~/(.*)$",
|
||||
"^[./]",
|
||||
],
|
||||
importOrderSeparation: false,
|
||||
importOrderSortSpecifiers: true,
|
||||
};
|
||||
```
|
||||
|
||||
Key configurations:
|
||||
- 110 characters line width
|
||||
- Double quotes for strings
|
||||
- ES5 trailing commas
|
||||
- Always use parentheses for arrow functions
|
||||
- Strict import ordering
|
||||
|
||||
### Import Order
|
||||
All imports follow a strict ordering:
|
||||
1. Mocks (for testing)
|
||||
2. Server-only imports
|
||||
3. Third-party modules
|
||||
4. Internal `@formbricks/*` modules
|
||||
5. Local aliases (`~/*)
|
||||
6. Relative imports
|
||||
|
||||
### TypeScript Config
|
||||
- Strict TypeScript checking enabled
|
||||
- Consistent use of `tsconfig.json` extending from `@formbricks/config-typescript`
|
||||
- Example configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true
|
||||
},
|
||||
"extends": "@formbricks/config-typescript/js-library.json",
|
||||
"include": ["src", "package.json"]
|
||||
}
|
||||
```
|
||||
|
||||
### JSDoc Comments
|
||||
Required for public APIs and complex functions:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Creates a new user
|
||||
* @param {string} name - User's name
|
||||
* @returns {Promise<User>} The created user object
|
||||
*/
|
||||
function createUser(name: string): Promise<User> {
|
||||
// implementation
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
Standardized error handling using the ErrorHandler class:
|
||||
|
||||
```typescript
|
||||
export class ErrorHandler {
|
||||
private static instance: ErrorHandler | null;
|
||||
private handleError: (error: unknown) => void;
|
||||
public customized = false;
|
||||
public static initialized = false;
|
||||
|
||||
private constructor(errorHandler?: (error: unknown) => void) {
|
||||
if (errorHandler) {
|
||||
this.handleError = errorHandler;
|
||||
this.customized = true;
|
||||
} else {
|
||||
this.handleError = (error) => {
|
||||
Logger.getInstance().error(JSON.stringify(error));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ... additional methods
|
||||
}
|
||||
```
|
||||
|
||||
## Enforcement Tools
|
||||
|
||||
### Pre-commit Hooks
|
||||
Using Husky and lint-staged for automated checks:
|
||||
|
||||
```json
|
||||
{
|
||||
"lint-staged": {
|
||||
"(apps|packages)/**/*.{js,ts,jsx,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.json": [
|
||||
"prettier --write"
|
||||
],
|
||||
"packages/database/schema.prisma": [
|
||||
"prisma format"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ESLint Configuration
|
||||
Each package extends from `@formbricks/eslint-config` with specific presets:
|
||||
- `library.js` for packages
|
||||
- `next.js` for Next.js applications
|
||||
- `react.js` for React applications
|
||||
|
||||
### Continuous Integration
|
||||
- Automated formatting checks in CI/CD pipeline
|
||||
- SonarCloud integration for code quality analysis
|
||||
- Coverage requirements for tests
|
||||
|
||||
## VS Code Integration
|
||||
|
||||
### Required Extensions
|
||||
- `esbenp.prettier-vscode` - Prettier formatting
|
||||
- `dbaeumer.vscode-eslint` - ESLint integration
|
||||
- `bradlc.vscode-tailwindcss` - Tailwind CSS support
|
||||
|
||||
### Recommended Settings
|
||||
```json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Consistent Formatting**
|
||||
- Always run `pnpm format` before committing
|
||||
- Use VS Code's format on save feature
|
||||
- Follow the established import order
|
||||
|
||||
2. **Type Safety**
|
||||
- Enable strict TypeScript checks
|
||||
- Use explicit type annotations when necessary
|
||||
- Avoid using `any` type
|
||||
|
||||
3. **Code Organization**
|
||||
- Keep files focused and modular
|
||||
- Group related functionality
|
||||
- Use clear, descriptive names
|
||||
|
||||
4. **Documentation**
|
||||
- Document complex logic
|
||||
- Use JSDoc for public APIs
|
||||
- Keep comments current with code changes
|
||||
|
||||
These standards ensure consistency across the Formbricks codebase while maintaining high code quality and developer productivity.
|
||||
133
docs/development/standards/practices/documentation.mdx
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
title: "Documentation"
|
||||
description: "Standards for documenting code and features in the Formbricks codebase"
|
||||
icon: "book"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
At Formbricks, we maintain strict documentation standards to ensure code clarity, maintainability, and ease of use for both internal developers and external contributors.
|
||||
|
||||
### Contribute to Documentation
|
||||
|
||||
We use Mintlify to maintain our documentation. You can find more information about how to contribute to the documentation in the [README](https://github.com/formbricks/formbricks/blob/main/docs/README.md) file.
|
||||
|
||||
## Code Documentation
|
||||
|
||||
### Function Documentation
|
||||
|
||||
1. **Complex Logic Documentation**
|
||||
- All functions with complex logic must include JSDoc comments
|
||||
- Document parameters, return types, and potential side effects
|
||||
- Example:
|
||||
|
||||
|
||||
```typescript
|
||||
/**
|
||||
Creates a new user and initializes their preferences
|
||||
@param {string} name - User's full name
|
||||
@param {UserOptions} options - Configuration options for user creation
|
||||
@returns {Promise<User>} The created user object
|
||||
@throws {ValidationError} If name is invalid
|
||||
*/
|
||||
async function createUser(name: string, options: UserOptions): Promise<User> {
|
||||
// implementation
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
2. **TypeScript Ignore Comments**
|
||||
- When using `@ts-ignore` or `@ts-expect-error`, always include a comment explaining why
|
||||
- Example:
|
||||
|
||||
|
||||
```typescript
|
||||
// @ts-expect-error -- Required for dynamic function calls
|
||||
void window.formbricks.init(...args);
|
||||
```
|
||||
|
||||
|
||||
### API Documentation
|
||||
|
||||
1. **API Endpoints**
|
||||
- All new API endpoints must be documented in the OpenAPI specification
|
||||
- Include request/response schemas, authentication requirements, and examples
|
||||
- Document both Client API and Management API endpoints
|
||||
- Place API documentation in the `docs/api-reference` directory
|
||||
|
||||
2. **Authentication**
|
||||
- Clearly specify authentication requirements
|
||||
- Document API key usage and permissions
|
||||
- Include error scenarios and handling
|
||||
|
||||
### Feature Documentation
|
||||
|
||||
- All new features must include a feature documentation file
|
||||
- Document the feature's purpose, usage, and implementation details
|
||||
- Include code examples and best practices
|
||||
|
||||
## Working with Mintlify
|
||||
We use Mintlify to write our documentation.
|
||||
|
||||
### File Structure
|
||||
|
||||
1. **MDX Files**
|
||||
- All documentation files must use the `.mdx` extension
|
||||
- Include frontmatter with required metadata:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Document Title"
|
||||
description: "Brief description of the content"
|
||||
icon: "appropriate-icon"
|
||||
---
|
||||
```
|
||||
|
||||
|
||||
2. **Navigation**
|
||||
- Add new pages to the appropriate section in `docs/mint.json`
|
||||
- Follow the existing navigation structure
|
||||
- Include proper redirects if URLs change
|
||||
|
||||
### Content Guidelines
|
||||
|
||||
1. **Writing Style**
|
||||
- Use clear, concise language
|
||||
- Break content into logical sections with proper headings
|
||||
- Include practical examples and code snippets
|
||||
- Use Mintlify components for notes, warnings, and callouts:
|
||||
|
||||
```markdown
|
||||
<Note>
|
||||
Important information goes here
|
||||
</Note>
|
||||
```
|
||||
|
||||
|
||||
2. **Media and Assets**
|
||||
- Store images in the appropriate `/images` subdirectory
|
||||
- Use descriptive alt text for all images
|
||||
- Optimize images for web delivery
|
||||
- Use relative paths for internal links
|
||||
|
||||
3. **Code Examples**
|
||||
- Specify the language for all code blocks
|
||||
- Include comments for complex code snippets
|
||||
- Use proper indentation and formatting
|
||||
|
||||
## Validation
|
||||
|
||||
1. **Local Testing**
|
||||
- Test documentation locally using Mintlify CLI:
|
||||
|
||||
```bash
|
||||
mintlify dev
|
||||
```
|
||||
|
||||
2. **Review Process**
|
||||
- Documentation changes require peer review
|
||||
- Verify all links and references work
|
||||
- Ensure proper formatting and rendering
|
||||
|
||||
These documentation requirements ensure that our codebase remains maintainable, accessible, and well-documented for both current and future developers.
|
||||
137
docs/development/standards/practices/error-handling.mdx
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: "Error Handling"
|
||||
description: "Standards for handling errors across the Formbricks codebase"
|
||||
icon: "triangle-exclamation"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
At Formbricks, we follow consistent error handling patterns to ensure reliability, debuggability, and maintainability across our codebase. This document outlines our standard approaches to error handling.
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Type Safety**: Use typed errors and results
|
||||
2. **Meaningful Messages**: Provide clear, actionable error messages
|
||||
3. **Proper Propagation**: Handle or propagate errors appropriately
|
||||
4. **Logging**: Ensure errors are properly logged for debugging
|
||||
5. **Recovery**: Implement graceful fallbacks where possible
|
||||
|
||||
|
||||
|
||||
## Standard Error Types
|
||||
|
||||
We maintain a set of standardized error types for different scenarios:
|
||||
|
||||
```typescript
|
||||
export interface ApiErrorResponse {
|
||||
code:
|
||||
| "not_found"
|
||||
| "gone"
|
||||
| "bad_request"
|
||||
| "internal_server_error"
|
||||
| "unauthorized"
|
||||
| "method_not_allowed"
|
||||
| "not_authenticated"
|
||||
| "forbidden"
|
||||
| "network_error";
|
||||
message: string;
|
||||
status: number;
|
||||
url: URL;
|
||||
details?: Record<string, string | string[] | number | number[] | boolean | boolean[]>;
|
||||
responseMessage?: string;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
### API Error Handling
|
||||
|
||||
For API endpoints:
|
||||
|
||||
```typescript
|
||||
export const GET = async (request: Request) => {
|
||||
try {
|
||||
const authentication = await authenticateRequest(request);
|
||||
if (!authentication) return responses.notAuthenticatedResponse();
|
||||
const data = await fetchData(authentication.environmentId!);
|
||||
return responses.successResponse(data);
|
||||
} catch (error) {
|
||||
if (error instanceof DatabaseError) {
|
||||
return responses.badRequestResponse(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Client-Side Error Handling
|
||||
|
||||
For client-side operations:
|
||||
|
||||
```typescript
|
||||
const handleOperation = async () => {
|
||||
const result = await performAction();
|
||||
if (!result.ok) {
|
||||
logger.error(`Operation failed: ${result.error.message}`);
|
||||
toast.error("Operation failed. Please try again.");
|
||||
return;
|
||||
}
|
||||
// Process successful result
|
||||
processResult(result.data);
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Never Swallow Errors**
|
||||
- Always handle or propagate errors
|
||||
- Log errors appropriately for debugging
|
||||
- Use error boundaries in React components
|
||||
|
||||
2. **Type Safety**
|
||||
- Use typed error responses
|
||||
- Leverage TypeScript for compile-time error checking
|
||||
- Define clear error interfaces
|
||||
|
||||
3. **Error Messages**
|
||||
- Include relevant context in error messages
|
||||
- Make messages actionable for developers
|
||||
- Use consistent error formatting
|
||||
|
||||
4. **Error Recovery**
|
||||
- Implement fallback behaviors where appropriate
|
||||
- Gracefully degrade functionality when possible
|
||||
- Provide user feedback for recoverable errors
|
||||
|
||||
5. **Documentation**
|
||||
- Document expected errors in function JSDoc
|
||||
- Include error handling in code examples
|
||||
- Keep error handling documentation up to date
|
||||
|
||||
## Testing Error Scenarios
|
||||
|
||||
Always include error case testing:
|
||||
|
||||
```typescript
|
||||
describe("fetchEnvironmentState()", () => {
|
||||
test("returns err(...) on network error", async () => {
|
||||
const mockNetworkError = {
|
||||
code: "network_error",
|
||||
message: "Timeout",
|
||||
responseMessage: "Network fail",
|
||||
};
|
||||
const result = await fetchEnvironmentState();
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error.code).toBe(mockNetworkError.code);
|
||||
expect(result.error.message).toBe(mockNetworkError.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
These standards ensure consistent, reliable error handling across the Formbricks codebase while maintaining good developer experience and system reliability.
|
||||
100
docs/development/standards/qa/code-reviews.mdx
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Code Reviews
|
||||
description: This document outlines our comprehensive code review process at Formbricks. We maintain high quality, consistency, and best practices within our codebase through this structured approach.
|
||||
icon: eyes
|
||||
---
|
||||
|
||||
|
||||
|
||||
## Initial Checks via GitHub Workflows
|
||||
|
||||
When a Pull Request (PR) is opened, our automated GitHub workflows perform several critical checks:
|
||||
|
||||
> **Note:** At Formbricks, all feature changes must be submitted through Pull Requests - direct pushes to protected branches are not allowed.
|
||||
|
||||
- **PR Title Validation**: Ensures clear, descriptive titles following our conventions
|
||||
- **Build Verification**: Confirms that the build process completes successfully
|
||||
- **Test Suite**: Runs all automated tests to catch potential regressions
|
||||
- **Translation Check**: Identifies any missing translation keys
|
||||
- **Linting**: Enforces our coding standards and style guidelines
|
||||
|
||||
These checks must pass before proceeding to manual review.
|
||||
|
||||
## Reviewer Assignment
|
||||
|
||||
Once all automated checks pass successfully:
|
||||
|
||||
- A team member with relevant expertise is assigned as the reviewer
|
||||
- The reviewer is notified automatically through GitHub
|
||||
- Priority is given based on PR urgency and complexity
|
||||
|
||||
## Functional Testing and UI Review
|
||||
|
||||
The assigned reviewer performs thorough testing:
|
||||
|
||||
### Functionality Testing
|
||||
|
||||
- Verifies that new features work as specified
|
||||
- Tests edge cases and error scenarios
|
||||
- Ensures proper error handling
|
||||
- Checks integration with existing features
|
||||
|
||||
### UI Review (if applicable)
|
||||
|
||||
- Validates visual consistency
|
||||
- Checks responsive behavior
|
||||
- Ensures accessibility standards
|
||||
- Reviews user interaction flows
|
||||
|
||||
## Feedback Process
|
||||
|
||||
If improvements are needed:
|
||||
|
||||
- Reviewers add inline comments on GitHub
|
||||
- Feedback is specific and actionable
|
||||
- Code suggestions are provided when helpful
|
||||
- Comments follow our constructive feedback guidelines
|
||||
|
||||
## In-depth Code Review
|
||||
|
||||
Our code review process utilizes both human expertise and AI assistance:
|
||||
|
||||
### Manual Review
|
||||
|
||||
- Code structure and organization
|
||||
- Design patterns and best practices
|
||||
- Performance considerations
|
||||
- Security implications
|
||||
- Documentation completeness
|
||||
|
||||
### AI-Assisted Review
|
||||
|
||||
We leverage advanced AI tools for additional insights:
|
||||
|
||||
- **Code Rabbit**: Automated code analysis and suggestions
|
||||
- **Sonarqube**: Code quality metrics and potential issues
|
||||
|
||||
## Resolution and Merge
|
||||
|
||||
Final steps before merging:
|
||||
|
||||
- All review comments must be addressed
|
||||
- Required changes are implemented
|
||||
- At least one reviewer approval is required
|
||||
- All conversations must be resolved
|
||||
- CI/CD pipeline passes again
|
||||
- PR is merged into the target branch
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Keep PRs focused and reasonably sized
|
||||
- Respond to review comments promptly
|
||||
- Use the "Request Changes" feature when necessary
|
||||
- Mark conversations as resolved when addressed
|
||||
- Use approve/request changes workflow appropriately
|
||||
|
||||
## Conclusion
|
||||
|
||||
Our code review process combines automated checks, manual review, and AI assistance to ensure high-quality code contributions. This multi-layered approach helps maintain code quality while promoting knowledge sharing within the team.
|
||||
|
||||
Remember: Code reviews are not just about finding issues—they're opportunities for learning and improving our codebase together.
|
||||
127
docs/development/standards/qa/testing-methodology.mdx
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
title: Testing Methodology
|
||||
icon: magnifying-glass
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
At Formbricks, we maintain a rigorous testing strategy to ensure high-quality code and reliable functionality. Our testing approach is standardized and integrated into our development workflow.
|
||||
|
||||
## Testing Structure
|
||||
|
||||
### Unit Testing with Vitest
|
||||
|
||||
We use Vitest as our primary testing framework. All unit tests follow these conventions:
|
||||
|
||||
1. **File Location and Naming**
|
||||
|
||||
- Test files are co-located with the source files they test
|
||||
- Test files use the `.test.ts` extension (e.g., `utils.test.ts` tests `utils.ts`)
|
||||
|
||||
2. **Test Organization**
|
||||
|
||||
```typescript
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("ComponentName or FeatureName", () => {
|
||||
describe("functionName or scenario", () => {
|
||||
it("should describe expected behavior", () => {
|
||||
// test implementation
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
3. **Coverage Requirements**
|
||||
- Minimum 85% code coverage requirement (WIP 👷♂️)
|
||||
- Coverage is tracked using V8 provider
|
||||
- Coverage reports include:
|
||||
- Text summaries
|
||||
- HTML reports
|
||||
- LCOV reports
|
||||
|
||||
### End-to-End Testing with Playwright
|
||||
|
||||
E2E tests are located in `apps/web/playwright/` and focus on critical user workflows.
|
||||
|
||||
## Testing Setup
|
||||
|
||||
### Configuration
|
||||
|
||||
Our Vitest configuration (`vite.config.ts`) includes:
|
||||
|
||||
```typescript
|
||||
test: {
|
||||
exclude: ['playwright/', 'node_modules/'],
|
||||
setupFiles: ['../../packages/lib/vitestSetup.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'html', 'lcov'],
|
||||
reportsDirectory: './coverage',
|
||||
},
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Test Utilities
|
||||
|
||||
Common test utilities are available in `vitestSetup.ts`:
|
||||
|
||||
- Mock implementations for commonly used functions
|
||||
- Test lifecycle hooks (beforeEach, afterEach)
|
||||
- Validation test helpers
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Test Independence**
|
||||
|
||||
```typescript
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
```
|
||||
|
||||
2. **Mocking**
|
||||
|
||||
- Use Vitest's built-in mocking utilities
|
||||
- Mock external dependencies and services
|
||||
- Example:
|
||||
|
||||
```typescript
|
||||
vi.mock("@formbricks/database", () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
create: vi.fn(),
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
3. **Assertions**
|
||||
- Write clear, specific assertions
|
||||
- Test both success and error cases
|
||||
- Example:
|
||||
```typescript
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.data).toEqual(expectedData);
|
||||
expect(async () => await invalidCall()).rejects.toThrow(ValidationError);
|
||||
```
|
||||
|
||||
## Quality Assurance Process
|
||||
|
||||
1. **Continuous Integration**
|
||||
|
||||
- Automated test suite execution on pull requests
|
||||
- Coverage reports generation
|
||||
- Test results reporting
|
||||
|
||||
2. **New Features**
|
||||
- Must include corresponding unit tests
|
||||
- Must maintain or improve coverage metrics
|
||||
- Must include relevant E2E tests for user-facing features
|
||||
38
docs/development/standards/technical/framework-usage.mdx
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: Framework Usage
|
||||
icon: book
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
This document outlines the framework usage guidelines for Formbricks, which is built primarily on Next.js (with heavy use of Server-Side Rendering through the App Router), Tailwind CSS, and Prisma ORM.
|
||||
|
||||
## Next.js
|
||||
|
||||
### App Router and Server Components
|
||||
|
||||
- Use the Next.js App Router with the /app directory for building the application.
|
||||
- Leverage React Server Components, which are the default in the App Router.
|
||||
- Perform server-side data fetching directly within page.tsx files using async function components.
|
||||
|
||||
### Data Fetching in page.tsx
|
||||
|
||||
- Implement data fetching logic directly in your page.tsx components. This allows for server-side rendering without needing separate data fetching methods like getServerSideProps.
|
||||
- Utilize React Suspense and streaming features if using advanced asynchronous patterns.
|
||||
|
||||
### Server Actions for Mutations
|
||||
|
||||
- Define Server Actions in dedicated action.ts files.
|
||||
- Server actions serve as entry points for mutations and delegate to service layers that perform Prisma operations and other business logic.
|
||||
- This approach promotes separation of concerns and maintainability by keeping form actions and mutations decoupled from UI components.
|
||||
|
||||
### Error Handling and Performance
|
||||
|
||||
- Integrate error boundaries and loading states appropriately within your App Router structure.
|
||||
- Optimize performance with Next.js built-in features such as dynamic imports, the Image component, and code splitting.
|
||||
|
||||
## Tailwind CSS
|
||||
We follow the Tailwind framework as intended. Please have a look at the [Tailwind CSS Guidelines](https://tailwindcss.com/docs/styling-with-utility-classes) for more information.
|
||||
|
||||
## Prisma ORM
|
||||
We're working with Prisma as our ORM. Please have a look at the [Prisma ORM Documentation](https://www.prisma.io/docs/orm) for more information.
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Language-Specific
|
||||
description: This document outlines the language-specific conventions for the Formbricks codebase, providing guidelines for writing code in TypeScript/JavaScript.
|
||||
icon: code
|
||||
---
|
||||
|
||||
## TypeScript
|
||||
|
||||
Our codebase follows the Vercel Engineering Style Guide conventions.
|
||||
|
||||
### ESLint Configuration
|
||||
|
||||
We maintain three primary ESLint configurations for different project types:
|
||||
|
||||
1. **Library Configuration** (for packages):
|
||||
|
||||
```javascript
|
||||
extends: [
|
||||
"@vercel/style-guide/eslint/node",
|
||||
"@vercel/style-guide/eslint/typescript"
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
2. **React Configuration** (for React applications):
|
||||
|
||||
```javascript
|
||||
extends: [
|
||||
"@vercel/style-guide/eslint/node",
|
||||
"@vercel/style-guide/eslint/typescript",
|
||||
"@vercel/style-guide/eslint/browser",
|
||||
"@vercel/style-guide/eslint/react",
|
||||
"@vercel/style-guide/eslint/next"
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
3. **Next.js Configuration** (for Next.js applications):
|
||||
|
||||
```javascript
|
||||
extends: [
|
||||
"@vercel/style-guide/eslint/node",
|
||||
"@vercel/style-guide/eslint/typescript",
|
||||
"@vercel/style-guide/eslint/browser",
|
||||
"@vercel/style-guide/eslint/react",
|
||||
"@vercel/style-guide/eslint/next"
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
### Key Conventions
|
||||
|
||||
1. **TypeScript Usage**
|
||||
- Strict TypeScript checking enabled
|
||||
- Explicit type annotations when necessary
|
||||
- Proper interface and type naming (prefix with T for types, I for interfaces when helpful)
|
||||
- No use of `any` type unless absolutely necessary
|
||||
|
||||
2. **Imports/Exports**
|
||||
- Follow strict import ordering:
|
||||
1. Mocks (for testing)
|
||||
2. Server-only imports
|
||||
3. Third-party modules
|
||||
4. Internal `@formbricks/*` modules
|
||||
5. Local aliases (`~/*)
|
||||
6. Relative imports
|
||||
|
||||
3. **Error Handling**
|
||||
- Use typed error responses
|
||||
- Proper error propagation
|
||||
- Consistent error message formatting
|
||||
- Implement error boundaries in React components
|
||||
|
||||
4. **Async/Await**
|
||||
- Prefer async/await over raw promises
|
||||
- Proper error handling in async functions
|
||||
- Use Promise.all for parallel operations
|
||||
|
||||
5. **React Specific**
|
||||
- Functional components with TypeScript
|
||||
- Proper use of hooks
|
||||
- Consistent prop typing
|
||||
- Server Components by default in Next.js App Router
|
||||
|
||||
### Code Formatting
|
||||
|
||||
We use Prettier with specific configurations:
|
||||
|
||||
```javascript
|
||||
{
|
||||
bracketSpacing: true,
|
||||
bracketSameLine: true,
|
||||
singleQuote: false,
|
||||
jsxSingleQuote: false,
|
||||
trailingComma: "es5",
|
||||
semi: true,
|
||||
printWidth: 110,
|
||||
arrowParens: "always"
|
||||
}
|
||||
```
|
||||
|
||||
## Swift
|
||||
Will be added upon release of the native iOS SDK.
|
||||
|
||||
## Kotlin
|
||||
Will be added upon release of the native Android SDK.
|
||||
|
||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -92,7 +92,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"group": "Core Features",
|
||||
"pages": [
|
||||
@@ -139,6 +138,10 @@
|
||||
"xm-and-surveys/core-features/email-customization"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Enterprise Features",
|
||||
"pages": ["xm-and-surveys/enterprise-features/saml-sso"]
|
||||
},
|
||||
{
|
||||
"group": "XM",
|
||||
"pages": [
|
||||
@@ -179,9 +182,55 @@
|
||||
"pages": ["development/technical-handbook/overview", "development/technical-handbook/database-model"]
|
||||
},
|
||||
{
|
||||
"group": "Contribution",
|
||||
"group": "Standards",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Code Organization",
|
||||
"icon": "code",
|
||||
"pages": [
|
||||
"development/standards/organization/file-and-directory-organization",
|
||||
"development/standards/organization/naming-conventions",
|
||||
"development/standards/organization/module-component-structure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Development Practices",
|
||||
"icon": "laptop",
|
||||
"pages": [
|
||||
"development/standards/practices/code-formatting",
|
||||
"development/standards/practices/documentation",
|
||||
"development/standards/practices/error-handling"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Technical Standards",
|
||||
"icon": "gear",
|
||||
"pages": [
|
||||
"development/standards/technical/language-specific-conventions",
|
||||
"development/standards/technical/framework-usage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Quality Assurance",
|
||||
"icon": "shield",
|
||||
"pages": ["development/standards/qa/code-reviews", "development/standards/qa/testing-methodology"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Contributions",
|
||||
"pages": ["development/contribution/contribution"]
|
||||
},
|
||||
{
|
||||
"group": "Guides",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Auth & Provision",
|
||||
"icon": "user-shield",
|
||||
"pages": ["development/guides/auth-and-provision/setup-saml-with-identity-providers"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Support",
|
||||
"pages": ["development/support/troubleshooting"]
|
||||
@@ -195,7 +244,8 @@
|
||||
"pages": [
|
||||
"self-hosting/setup/one-click",
|
||||
"self-hosting/setup/docker",
|
||||
"self-hosting/setup/cluster-setup"
|
||||
"self-hosting/setup/cluster-setup",
|
||||
"self-hosting/setup/monitoring"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -203,7 +253,14 @@
|
||||
"pages": [
|
||||
"self-hosting/configuration/custom-ssl",
|
||||
"self-hosting/configuration/environment-variables",
|
||||
"self-hosting/configuration/oauth",
|
||||
{
|
||||
"group": "Auth & SSO",
|
||||
"icon": "lock",
|
||||
"pages": [
|
||||
"self-hosting/configuration/auth-sso/oauth",
|
||||
"self-hosting/configuration/auth-sso/saml-sso"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"icon": "bridge",
|
||||
|
||||
76
docs/self-hosting/configuration/auth-sso/saml-sso.mdx
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "SAML SSO"
|
||||
icon: "user-shield"
|
||||
description: "How to set up SAML SSO for Formbricks"
|
||||
---
|
||||
|
||||
<Note>You require an Enterprise License along with a SAML SSO add-on to avail this feature.</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
Formbricks supports SAML Single Sign-On (SSO) to enable secure, centralized authentication. With SAML, organizations can integrate their existing Identity Provider (IdP) infrastructure for streamlined access management. Formbricks internally uses BoxyHQ's SAML Jackson to manage SAML connections. SAML Jackson is a service provided by BoxyHQ that manages SAML connection details and validates assertions. It is part of the Formbricks server.
|
||||
|
||||
To learn more about SAML Jackson, please refer to the [BoxyHQ SAML Jackson documentation](https://boxyhq.com/docs/jackson/deploy).
|
||||
|
||||
## How SAML Works in Formbricks
|
||||
|
||||
SAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between an Identity Provider (IdP) and Formbricks. Here's how the integration works with BoxyHQ Jackson embedded into the flow:
|
||||
|
||||
1. **Login Initiation:**
|
||||
The user clicks “Sign in with SAML” on Formbricks.
|
||||
|
||||
2. **Configuration Retrieval via BoxyHQ:**
|
||||
Formbricks requests the SAML connection details from BoxyHQ Jackson. BoxyHQ securely stores and manages the IdP configuration, including endpoints, certificates, and other metadata.
|
||||
|
||||
3. **Redirection:**
|
||||
With the configuration details from BoxyHQ, Formbricks redirects the user to the IdP’s login page (e.g., Okta).
|
||||
|
||||
4. **Authentication:**
|
||||
The user authenticates directly with the IdP.
|
||||
|
||||
5. **SAML Response:**
|
||||
Upon successful authentication, the IdP sends a signed SAML response back to Formbricks via the user’s browser.
|
||||
|
||||
6. **Validation via BoxyHQ:**
|
||||
BoxyHQ Jackson validates the SAML assertion—verifying the signature and extracting user details—before sending the validated data back to Formbricks.
|
||||
|
||||
7. **Access Granted:**
|
||||
Formbricks logs the user in using the verified information.
|
||||
|
||||
## SAML Authentication Flow Sequence Diagram
|
||||
|
||||
Below is a sequence diagram illustrating the complete SAML authentication flow with BoxyHQ Jackson integrated:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant FB as Formbricks (SP)
|
||||
participant BHQ as BoxyHQ Jackson
|
||||
participant OK as Okta (IdP)
|
||||
|
||||
Note over FB,BHQ: (Setup phase, done beforehand)<br/>1. Admin configures SAML metadata in Formbricks<br/>2. BoxyHQ stores & manages SAML connection details
|
||||
|
||||
U->>FB: Clicks “Sign in with SAML”
|
||||
FB->>BHQ: Request SAML connection details
|
||||
BHQ->>FB: Returns SAML configuration (IdP info)
|
||||
FB->>OK: Redirect user to Okta (SAML Auth Request)
|
||||
OK->>U: Prompts user for credentials
|
||||
U->>OK: Submits credentials
|
||||
OK->>FB: Sends SAML Assertion (Callback URL)
|
||||
FB->>BHQ: Validates assertion & extracts user info
|
||||
BHQ->>FB: Returns validated user data
|
||||
FB->>U: Logs user into Formbricks
|
||||
|
||||
```
|
||||
|
||||
## Setting Up SAML SSO
|
||||
|
||||
To configure SAML SSO in Formbricks, follow these steps:
|
||||
|
||||
1. **Database Setup:** Configure a dedicated database for SAML by setting the `SAML_DATABASE_URL` environment variable (e.g., `postgresql://postgres:@localhost:5432/formbricks-saml`). If you're using a self-signed certificate for Postgres, include the `sslmode=no-verify` parameter.
|
||||
2. **Admin Configuration:** Define the SAML administrators by setting `SAML_ADMINS` as a comma-separated list of admin emails.
|
||||
3. **IdP Application:** Create a SAML application in your IdP by following your provider's instructions([SAML Setup](/development/guides/auth-and-provision/setup-saml-with-identity-providers))
|
||||
4. **User Provisioning:** Provision users in your IdP and configure access to the IdP SAML app for all your users (who need access to Formbricks).
|
||||
5. **Metadata:** Keep the XML metadata from your IdP handy for the next step.
|
||||
6. **Metadata Upload:** Log in with one of the SAML admin accounts and navigate to **Organization Settings -> Single Sign-On** in Formbricks. Paste the XML metadata from your IdP into the SAML configuration section.
|
||||
7. **Finalize Setup:** Save your configuration. Provisioned users can now use SAML SSO to access Formbricks.
|
||||
91
docs/self-hosting/setup/monitoring.mdx
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: "Monitoring"
|
||||
description: "Monitoring your Formbricks installation for optimal performance."
|
||||
icon: "magnifying-glass-chart"
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
Formbricks follows Next.js best practices with all logs being written to stdout/stderr, making it easy to collect and forward logs to your preferred logging solution.
|
||||
|
||||
### Docker Container Logs
|
||||
|
||||
```bash
|
||||
# One-Click setup
|
||||
cd formbricks
|
||||
docker compose logs
|
||||
|
||||
# Standard Docker commands
|
||||
docker logs <container-name>
|
||||
docker logs -f <container-name> # Follow logs
|
||||
```
|
||||
|
||||
### Kubernetes Pod Logs
|
||||
|
||||
```bash
|
||||
kubectl logs <pod-name> -n <namespace>
|
||||
kubectl logs -f <pod-name> -n <namespace> # Follow logs
|
||||
```
|
||||
|
||||
### Log Forwarding
|
||||
|
||||
Since all logs are written to stdout/stderr, you can integrate with various logging solutions:
|
||||
|
||||
- ELK Stack (Elasticsearch, Logstash, Kibana)
|
||||
- Fluentd/Fluent Bit
|
||||
- Datadog
|
||||
- Splunk
|
||||
- CloudWatch Logs (AWS)
|
||||
|
||||
## OpenTelemetry Integration (Beta)
|
||||
|
||||
Formbricks leverages Next.js's built-in OpenTelemetry instrumentation for comprehensive observability. When enabled, it automatically instruments various aspects of your application.
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```env
|
||||
OTEL_ENABLED=true
|
||||
OTEL_ENDPOINT=<your-collector-endpoint>
|
||||
OTEL_SERVICE_NAME=formbricks
|
||||
NEXT_OTEL_VERBOSE=1 # Optional: enables detailed tracing
|
||||
```
|
||||
|
||||
### Default Instrumentation
|
||||
|
||||
The OpenTelemetry integration automatically tracks:
|
||||
|
||||
- HTTP requests and responses
|
||||
- Route rendering
|
||||
- API route execution
|
||||
- Server-side operations
|
||||
- Database queries
|
||||
- External API calls
|
||||
|
||||
### Supported Backends
|
||||
|
||||
OpenTelemetry can export data to:
|
||||
|
||||
- Jaeger
|
||||
- Zipkin
|
||||
- Prometheus
|
||||
- New Relic
|
||||
- Datadog
|
||||
- Azure Monitor
|
||||
|
||||
### Key Metrics
|
||||
|
||||
- HTTP request duration
|
||||
- Database performance
|
||||
- Memory usage
|
||||
- Response times
|
||||
- Error rates
|
||||
|
||||
## Health Checks
|
||||
|
||||
Available endpoints:
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
Use these endpoints for monitoring system health in container orchestration and monitoring tools.
|
||||
@@ -1,19 +1,12 @@
|
||||
export const metadata = {
|
||||
title: "Airtable Setup",
|
||||
description: "Instantly populate your airtable table with survey data",
|
||||
};
|
||||
|
||||
#### Integrations
|
||||
|
||||
# Airtable
|
||||
---
|
||||
title: "Airtable"
|
||||
description: "A step-by-step guide to integrate Airtable with Formbricks Cloud."
|
||||
---
|
||||
|
||||
The Airtable integration allows you to automatically send responses to an Airtable of your choice.
|
||||
|
||||
<Note>
|
||||
If you are on a self-hosted instance, you will need to configure this
|
||||
integration separately. Please follow the guides
|
||||
[here](/self-hosting/configuration/integrations) to configure integrations on your
|
||||
self-hosted instance.
|
||||
If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance.
|
||||
</Note>
|
||||
|
||||
## Formbricks Cloud
|
||||
@@ -22,34 +15,31 @@ The Airtable integration allows you to automatically send responses to an Airtab
|
||||
|
||||

|
||||
|
||||
2. Now click on the "Connect with Airtable" button to authenticate yourself with Airtable.
|
||||
1. Now click on the "Connect with Airtable" button to authenticate yourself with Airtable.
|
||||
|
||||

|
||||
|
||||
3. You will now be taken to a page where you need to add and grant access to the base you want to use for the integration.
|
||||
1. You will now be taken to a page where you need to add and grant access to the base you want to use for the integration.
|
||||
|
||||

|
||||
|
||||
|
||||
4. Once you add and grant access to your base, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
1. Once you add and grant access to your base, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
|
||||
Before the next step, make sure that you have a Formbricks Survey with at least one question and a Airtable base with atleast one table in the Airtable account you integrated.
|
||||
|
||||
Before the next step, make sure that you have a Formbricks Survey with at least one question and a Airtable base with atleast one table in the Airtable account you integrated.
|
||||
</Note>
|
||||
|
||||
6. Now click on the "Link New Table" button to link an Airtable with Formbricks and a modal will open up.
|
||||
1. Now click on the "Link New Table" button to link an Airtable with Formbricks and a modal will open up.
|
||||
|
||||

|
||||
|
||||
7. Select the Base and table you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in Airtable. Select the questions and click on the "Save" button.
|
||||
1. Select the Base and table you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in Airtable. Select the questions and click on the "Save" button.
|
||||
|
||||

|
||||
|
||||
8. On submitting, the modal will close and you will see the linked table in the list of linked tables.
|
||||
1. On submitting, the modal will close and you will see the linked table in the list of linked tables.
|
||||
|
||||

|
||||
|
||||
@@ -62,10 +52,13 @@ Still struggling or something not working as expected? [Join us on Github Discus
|
||||
To remove the integration with Airtable,
|
||||
|
||||
1. Visit the Integrations tab in your Formbricks Cloud dashboard.
|
||||
|
||||
2. Select "Manage" button in the Airtable card.
|
||||
|
||||
3. Click on the "Connected with `<your-email-here`>" just before the "Link new Table" button.
|
||||
|
||||
4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Airtable Account.
|
||||
|
||||

|
||||
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
@@ -4,17 +4,8 @@ description:
|
||||
"The Google Sheets integration allows you to automatically send responses to a Google Sheet of your choice."
|
||||
---
|
||||
|
||||
#### Integrations
|
||||
|
||||
# Google Sheets
|
||||
|
||||
The Google Sheets integration allows you to automatically send responses to a Google Sheet of your choice.
|
||||
|
||||
<Note>
|
||||
If you are on a self-hosted instance, you will need to configure this
|
||||
integration separately. Please follow the guides
|
||||
[here](/self-hosting/configuration/integrations) to configure integrations on your
|
||||
self-hosted instance.
|
||||
If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance.
|
||||
</Note>
|
||||
|
||||
## Connect Google Sheets
|
||||
@@ -23,31 +14,29 @@ The Google Sheets integration allows you to automatically send responses to a Go
|
||||
|
||||

|
||||
|
||||
2. Now click on the "Connect with Google" button to authenticate yourself with Google.
|
||||
1. Now click on the "Connect with Google" button to authenticate yourself with Google.
|
||||
|
||||

|
||||
|
||||
3. You will now be taken to the Google OAuth page where you can select the Google account you want to use for the integration.
|
||||
1. You will now be taken to the Google OAuth page where you can select the Google account you want to use for the integration.
|
||||
|
||||
4. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
2. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
|
||||
Before the next step, make sure that you have a Formbricks Survey with at least one question and a Google Sheet in the Google account you integrated.
|
||||
|
||||
Before the next step, make sure that you have a Formbricks Survey with at least one question and a Google Sheet in the Google account you integrated.
|
||||
</Note>
|
||||
|
||||
5. Now click on the "Link New Sheet" button to link a Google Sheet with Formbricks and a modal will open up.
|
||||
1. Now click on the "Link New Sheet" button to link a Google Sheet with Formbricks and a modal will open up.
|
||||
|
||||

|
||||
|
||||
6. Enter the spreadsheet URL for the Google Sheet you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in the Google Sheet. Select the questions and click on the "Link Sheet" button.
|
||||
1. Enter the spreadsheet URL for the Google Sheet you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in the Google Sheet. Select the questions and click on the "Link Sheet" button.
|
||||
|
||||

|
||||
|
||||
7. On submitting, the modal will close and you will see the linked Google Sheet in the list of linked Google Sheets.
|
||||
1. On submitting, the modal will close and you will see the linked Google Sheet in the list of linked Google Sheets.
|
||||
|
||||

|
||||
|
||||
@@ -58,23 +47,29 @@ Congratulations! You have successfully linked a Google Sheet with Formbricks. No
|
||||
To remove the integration with Google Account,
|
||||
|
||||
1. Visit the Integrations tab in your Formbricks Cloud dashboard.
|
||||
|
||||
2. Select **Manage Sheets** button in the Google Sheets card.
|
||||
|
||||
3. Click on the **Delete Integration** button.
|
||||
|
||||
4. It will now ask for a confirmation to remove the integration. Click on the **Delete** button to remove the integration. You can always come back and connect again with the same Google Account.
|
||||
|
||||

|
||||
|
||||
## What info do you need?
|
||||
|
||||
- Your **Email ID** for authentication (We use this to identify you)
|
||||
- Your **Google Sheets Names and IDs** (We fetch this to list and show you the options of choosing a sheet to integrate with)
|
||||
- Write access to **selected Google Sheet** (The google sheet you choose to integrate it with, we write survey responses to it)
|
||||
* Your **Email ID** for authentication (We use this to identify you)
|
||||
|
||||
* Your **Google Sheets Names and IDs** (We fetch this to list and show you the options of choosing a sheet to integrate with)
|
||||
|
||||
* Write access to **selected Google Sheet** (The google sheet you choose to integrate it with, we write survey responses to it)
|
||||
|
||||
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 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))
|
||||
|
||||
2. **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 store as little personal information as possible.</Note>
|
||||
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
@@ -4,16 +4,10 @@ description:
|
||||
"Discover how to seamlessly integrate Formbricks with Make.com. Dive into our comprehensive guide to set up scenarios, connect with a plethora of apps, and send your survey data to more than 1000 platforms."
|
||||
---
|
||||
|
||||
#### Integrations
|
||||
|
||||
# Make.com Setup
|
||||
|
||||
Make is a powerful tool to send information between Formbricks and thousands of apps. Here's how to set it up.
|
||||
|
||||
<Note>
|
||||
Nailed down your survey?? Any changes in the survey cause additional work in
|
||||
the _Scenario_. It makes sense to first settle on the survey you want to run
|
||||
and then get to setting up Make.
|
||||
Nailed down your survey?? Any changes in the survey cause additional work in the *Scenario*. It makes sense to first settle on the survey you want to run and then get to setting up Make.
|
||||
</Note>
|
||||
|
||||
## Step 1: Setup your survey incl. `questionId` for every question
|
||||
@@ -22,13 +16,12 @@ Set up the `questionId`s of your survey questions before publishing.
|
||||
|
||||

|
||||
|
||||
_Update the Question ID field in every question card under Advanced Settings._
|
||||
*Update the Question ID field in every question card under Advanced Settings.*
|
||||
|
||||
<Note>
|
||||
Already published? Duplicate survey You can only update the questionId
|
||||
before publishing the survey. If already published, simply duplicate it.
|
||||

|
||||
|
||||
</Note>
|
||||
|
||||
## Step 2: Setup Make.com
|
||||
@@ -51,7 +44,7 @@ Click "Create a webhook":
|
||||
|
||||

|
||||
|
||||
Enter the Formbricks API Host and API Key. API Host is by default set to https://app.formbricks.com but can be modified for self hosting instances. Learn how to get an API Key from the [API Key tutorial](/api-reference/rest-api#how-to-generate-an-api-key).
|
||||
Enter the Formbricks API Host and API Key. API Host is by default set to [https://app.formbricks.com](https://app.formbricks.com) but can be modified for self hosting instances. Learn how to get an API Key from the [API Key tutorial](/api-reference/rest-api#how-to-generate-an-api-key).
|
||||
|
||||

|
||||
|
||||
@@ -83,4 +76,4 @@ Specify the spreadsheet details and match the Formbricks data:
|
||||
|
||||
A new row gets added to the spreadsheet for every response:
|
||||
|
||||

|
||||

|
||||
@@ -3,11 +3,6 @@ title: "n8n"
|
||||
description: "Integrate Formbricks with n8n for a streamlined workflow experience."
|
||||
---
|
||||
|
||||
|
||||
#### Integrations
|
||||
|
||||
# n8n Setup
|
||||
|
||||
<Note>
|
||||
The Formbricks n8n node is currently only available in the n8n self-hosted
|
||||
version as a community node. To install it go to "Settings" -> "Community
|
||||
@@ -28,7 +23,7 @@ When setting up the node your life will be easier when you change the `questionI
|
||||
|
||||

|
||||
|
||||
_In every question card in the Advanced Settings you find the Question ID field. Update it so that you'll recognize the response tied to this question._
|
||||
*In every question card in the Advanced Settings you find the Question ID field. Update it so that you'll recognize the response tied to this question.*
|
||||
|
||||
<Note>
|
||||
Already published? Duplicate survey You can only update the questionId
|
||||
@@ -79,7 +74,7 @@ Here, we are selecting two surveys.
|
||||
|
||||
## Step 6: Test your trigger
|
||||
|
||||
In order to set up n8n you'll need a test response in the selected survey. This allows you to select the individual values of each response in your workflow.
|
||||
In order to set up n8n you'll need a test response in the selected survey. This allows you to select the individual values of each response in your workflow.
|
||||
|
||||

|
||||
|
||||
@@ -105,4 +100,4 @@ Fill in the `Webhook URL` and the `Content` that you want to receive in the resp
|
||||
|
||||
Once the execution is successful, you'll receive the content in the discord channel.
|
||||
|
||||

|
||||

|
||||
@@ -4,13 +4,8 @@ description:
|
||||
"The notion integration allows you to automatically send responses to a Notion database of your choice."
|
||||
---
|
||||
|
||||
The notion integration allows you to automatically send responses to a Notion database of your choice.
|
||||
|
||||
<Note>
|
||||
If you are on a self-hosted instance, you will need to configure this
|
||||
integration separately. Please follow the guides
|
||||
[here](/self-hosting/configuration/integrations) to configure integrations on your
|
||||
self-hosted instance.
|
||||
If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance.
|
||||
</Note>
|
||||
|
||||
## Formbricks Cloud
|
||||
@@ -19,13 +14,13 @@ The notion integration allows you to automatically send responses to a Notion da
|
||||
|
||||

|
||||
|
||||
2. Now click on the "Connect with Notion" button to authenticate yourself with Notion.
|
||||
1. Now click on the "Connect with Notion" button to authenticate yourself with Notion.
|
||||
|
||||

|
||||
|
||||
3. You will now be taken to the Notion OAuth page where you can select the Notion account you want to use for the integration
|
||||
1. You will now be taken to the Notion OAuth page where you can select the Notion account you want to use for the integration
|
||||
|
||||
4. Once you have selected the account and databases and completed the authentication and authorization process, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
2. Once you have selected the account and databases and completed the authentication and authorization process, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
|
||||

|
||||
|
||||
@@ -34,15 +29,15 @@ The notion integration allows you to automatically send responses to a Notion da
|
||||
least one question and a Notion database in the Notion account you integrated.
|
||||
</Note>
|
||||
|
||||
5. Now click on the "Link New Database" button to link a Notion database with Formbricks and a modal will open up.
|
||||
1. Now click on the "Link New Database" button to link a Notion database with Formbricks and a modal will open up.
|
||||
|
||||

|
||||
|
||||
6. Select the Notion database you want to link with Formbricks and the Survey. On doing so, you will be asked to map formbricks' survey questions with selected databases' column. Complete the mapping and click on the "Link Database" button.
|
||||
1. Select the Notion database you want to link with Formbricks and the Survey. On doing so, you will be asked to map formbricks' survey questions with selected databases' column. Complete the mapping and click on the "Link Database" button.
|
||||
|
||||

|
||||
|
||||
7. On submitting, the modal will close and you will see the linked Notion database in the list of linked Notion databases.
|
||||
1. On submitting, the modal will close and you will see the linked Notion database in the list of linked Notion databases.
|
||||
|
||||

|
||||
|
||||
@@ -53,16 +48,26 @@ Congratulations! You have successfully linked a Notion database with Formbricks.
|
||||
Enabling the Notion Integration in a self-hosted environment requires a setup using Notion account and changing the environment variables of your Formbricks instance.
|
||||
|
||||
1. Sign up for a [Notion](https://www.notion.so/) account, if you don't have one already.
|
||||
|
||||
2. Go to the [my integrations](https://www.notion.so/my-integrations) page and click on **New integration**.
|
||||
|
||||
3. Fill up the basic information like **Name**, **Logo** and click on **Submit**.
|
||||
|
||||
4. Now, click on **Distribution** tab on the sidebar. A text will appear which will ask you to make the integration public. Click on that toggle button. A form will appear below the text.
|
||||
|
||||
5. Now provide it the details such as requested. Under **Redirect URIs** field:
|
||||
- If you are running formbricks locally, you can enter `http://localhost:3000/api/v1/integrations/notion/callback`.
|
||||
- Or, you can enter `https://<your-public-facing-url>/api/v1/integrations/notion/callback`
|
||||
|
||||
* If you are running formbricks locally, you can enter `http://localhost:3000/api/v1/integrations/notion/callback`.
|
||||
|
||||
* Or, you can enter `https://<your-public-facing-url>/api/v1/integrations/notion/callback`
|
||||
|
||||
6. Once you've filled all the necessary details, click on **Submit**.
|
||||
|
||||
7. A screen will appear which will have **Client ID** and **Client secret**. Copy them and set them as the environment variables in your Formbricks instance as:
|
||||
- `NOTION_OAUTH_CLIENT_ID` - OAuth Client ID
|
||||
- `NOTION_OAUTH_CLIENT_SECRET` - OAuth Client Secret
|
||||
|
||||
* `NOTION_OAUTH_CLIENT_ID` - OAuth Client ID
|
||||
|
||||
* `NOTION_OAUTH_CLIENT_SECRET` - OAuth Client Secret
|
||||
|
||||
Voila! You have successfully enabled the Notion integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Formbricks Cloud](#formbricks-cloud) section to link a Notion database with Formbricks.
|
||||
|
||||
@@ -71,10 +76,13 @@ Voila! You have successfully enabled the Notion integration in your self-hosted
|
||||
To remove the integration with Slack Workspace,
|
||||
|
||||
1. Visit the Integrations tab in your Formbricks Cloud dashboard.
|
||||
|
||||
2. Select "Manage" button in the Slack card.
|
||||
|
||||
3. Click on the "Delete Integration" button.
|
||||
|
||||
4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Notion Account.
|
||||
|
||||

|
||||
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
@@ -1,27 +1,32 @@
|
||||
---
|
||||
title: "Overview"
|
||||
description: "Configure third-party integrations with a Formbricks instance."
|
||||
description: "Configure third-party integrations with Formbricks Cloud."
|
||||
---
|
||||
|
||||
At Formbricks, we understand the importance of integrating with third-party applications. We have step-by-step guides to configure our third-party integrations with a your Formbricks instance. We currently support the below integrations, click on them to see their individual guides:
|
||||
At Formbricks, we understand the importance of integrating with third-party applications. We have step-by-step guides to configure our third-party integrations with Formbricks Cloud. 
|
||||
|
||||
<Note>
|
||||
If you are on a self-hosted instance, you will need to configure these
|
||||
integrations manually. Please follow the guides
|
||||
[here](/self-hosting/configuration/integrations) to configure integrations on your
|
||||
self-hosted instance.
|
||||
If you are on a self-hosted instance, you will need to configure these integrations manually. Please follow the guides [here](/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance.
|
||||
</Note>
|
||||
|
||||
- [ActivePieces](/xm-and-surveys/core-features/integrations/activepieces) (Open Source): Automate workflows with ActivePieces no-code automation tool
|
||||
- [Airtable](/xm-and-surveys/core-features/integrations/airtable): Automatically send responses to an Airtable of your choice.
|
||||
- [Google Sheets](/xm-and-surveys/core-features/integrations/google-sheets): Automatically send responses to a Google Sheet of your choice.
|
||||
- [Make](/xm-and-surveys/core-features/integrations/make): Leverage Make's powerful automation capabilities to automate your workflows.
|
||||
- [n8n](/xm-and-surveys/core-features/integrations/n8n)(Open Source): Automate workflows with n8n's no-code automation tool
|
||||
- [Notion](/xm-and-surveys/core-features/integrations/notion): Automatically send responses to a Notion database of your choice.
|
||||
- [Slack](/xm-and-surveys/core-features/integrations/slack): Automatically send responses to a Slack channel of your choice on response events.
|
||||
- [Wordpress](/xm-and-surveys/core-features/integrations/wordpress)(Open Source): Automatically integrate your Formbricks surveys with your Wordpress website.
|
||||
- [Zapier](/xm-and-surveys/core-features/integrations/zapier): Connect Formbricks with 2000+ apps on Zapier.
|
||||
* [ActivePieces](/xm-and-surveys/core-features/integrations/activepieces) (Open Source): Automate workflows with ActivePieces no-code automation tool
|
||||
|
||||
---
|
||||
* [Airtable](/xm-and-surveys/core-features/integrations/airtable): Automatically send responses to an Airtable of your choice.
|
||||
|
||||
If you have any questions or need help with any of the integrations or even want a new integration, please reach out to us on [Github Discussions](https://github.com/formbricks/formbricks/discussions).
|
||||
* [Google Sheets](/xm-and-surveys/core-features/integrations/google-sheets): Automatically send responses to a Google Sheet of your choice.
|
||||
|
||||
* [Make](/xm-and-surveys/core-features/integrations/make): Leverage Make's powerful automation capabilities to automate your workflows.
|
||||
|
||||
* [n8n](/xm-and-surveys/core-features/integrations/n8n)(Open Source): Automate workflows with n8n's no-code automation tool
|
||||
|
||||
* [Notion](/xm-and-surveys/core-features/integrations/notion): Automatically send responses to a Notion database of your choice.
|
||||
|
||||
* [Slack](/xm-and-surveys/core-features/integrations/slack): Automatically send responses to a Slack channel of your choice on response events.
|
||||
|
||||
* [Wordpress](/xm-and-surveys/core-features/integrations/wordpress)(Open Source): Automatically integrate your Formbricks surveys with your Wordpress website.
|
||||
|
||||
* [Zapier](/xm-and-surveys/core-features/integrations/zapier): Connect Formbricks with 2000+ apps on Zapier.
|
||||
|
||||
***
|
||||
|
||||
If you have any questions or need help with any of the integrations or even want a new integration, please reach out to us on [Github Discussions](https://github.com/formbricks/formbricks/discussions).
|
||||
@@ -4,13 +4,8 @@ description:
|
||||
"The slack integration allows you to automatically send responses to a Slack channel of your choice."
|
||||
---
|
||||
|
||||
The slack integration allows you to automatically send responses to a Slack channel of your choice.
|
||||
|
||||
<Note>
|
||||
If you are on a self-hosted instance, you will need to configure this
|
||||
integration separately. Please follow the guides
|
||||
[here](/self-hosting/configuration/integrations) to configure integrations on your
|
||||
self-hosted instance.
|
||||
If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance.
|
||||
</Note>
|
||||
|
||||
## Formbricks Cloud
|
||||
@@ -19,15 +14,15 @@ The slack integration allows you to automatically send responses to a Slack chan
|
||||
|
||||

|
||||
|
||||
2. Now click on the "Connect with Slack" button to authenticate yourself with Slack.
|
||||
1. Now click on the "Connect with Slack" button to authenticate yourself with Slack.
|
||||
|
||||

|
||||
|
||||
3. You will now be taken to the Slack OAuth page where you can select the Slack channel you want to link with Formbricks and click on the "Allow" button.
|
||||
1. You will now be taken to the Slack OAuth page where you can select the Slack channel you want to link with Formbricks and click on the "Allow" button.
|
||||
|
||||

|
||||
|
||||
4. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
1. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below:
|
||||
|
||||

|
||||
|
||||
@@ -36,7 +31,7 @@ The slack integration allows you to automatically send responses to a Slack chan
|
||||
least one question and a Slack channel in the Slack workspace you integrated.
|
||||
</Note>
|
||||
|
||||
5. In order to make your channel available in channel dropdown, you need to add formbricks integration bot to the channel you want to link. You can do this by going to channel settings -> Integrations -> Add apps -> Search for "Formbricks" -> Select the bot -> Add.
|
||||
1. In order to make your channel available in channel dropdown, you need to add formbricks integration bot to the channel you want to link. You can do this by going to channel settings -> Integrations -> Add apps -> Search for "Formbricks" -> Select the bot -> Add.
|
||||
|
||||

|
||||
|
||||
@@ -46,20 +41,18 @@ The slack integration allows you to automatically send responses to a Slack chan
|
||||
|
||||

|
||||
|
||||
6. Now click on the "Link channel" button to link a Slack channel with Formbricks and a modal will open up.
|
||||
1. Now click on the "Link channel" button to link a Slack channel with Formbricks and a modal will open up.
|
||||
|
||||

|
||||
|
||||
|
||||
7. Select the channel you want to link with Formbricks and the Survey. On doing so, you will be asked to select the questions' responses you want to feed in the Slack channel. Select the questions and click on the "Link Channel" button.
|
||||
1. Select the channel you want to link with Formbricks and the Survey. On doing so, you will be asked to select the questions' responses you want to feed in the Slack channel. Select the questions and click on the "Link Channel" button.
|
||||
|
||||

|
||||
|
||||
8. On submitting, the modal will close and you will see the linked Slack channel in the list of linked Slack channels.
|
||||
1. On submitting, the modal will close and you will see the linked Slack channel in the list of linked Slack channels.
|
||||
|
||||

|
||||
|
||||
|
||||
Congratulations! You have successfully linked a Slack channel with Formbricks. Now whenever a response is submitted for the linked survey, it will be automatically sent to the linked Slack channel.
|
||||
|
||||
## Setup in self-hosted Formbricks
|
||||
@@ -68,49 +61,62 @@ Enabling the Slack Integration in a self-hosted environment requires a setup usi
|
||||
|
||||
<Note>
|
||||
If you are running Formbricks locally:
|
||||
|
||||
You need to use `https` instead of `http` for the redirect URI.
|
||||
- You can update the `go` script in your `apps/web/package.json` to include the `--experimental-https` flag. The
|
||||
command will look like: <br />
|
||||
|
||||
You need to use `https` instead of `http` for the redirect URI.
|
||||
|
||||
* You can update the `go` script in your `apps/web/package.json` to include the `--experimental-https` flag. The
|
||||
command will look like: <br />
|
||||
|
||||
```bash
|
||||
"go": next dev --experimental-https -p 3000
|
||||
```
|
||||
|
||||
- You also need to update the .env file in the `apps/web` directory to include the `WEBAPP_URL` as `https://localhost:3000` instead of `http://localhost:3000`.
|
||||
|
||||
- You also need to run the terminal in admin mode to run the `go` script(to acquire the SSL certificate). You can do this by running the terminal as an administrator or using the `sudo` command in Unix-based systems.
|
||||
* You also need to update the .env file in the `apps/web` directory to include the `WEBAPP_URL` as `https://localhost:3000` instead of `http://localhost:3000`.
|
||||
|
||||
* You also need to run the terminal in admin mode to run the `go` script(to acquire the SSL certificate). You can do this by running the terminal as an administrator or using the `sudo` command in Unix-based systems.
|
||||
</Note>
|
||||
|
||||
1. Create a Slack workspace if you don't have one already.
|
||||
|
||||
2. Go to the [Your apps](https://api.slack.com/apps) page and **Create New App**.
|
||||
|
||||
3. Click on **From Scratch** and provide the **App Name** and select your workspace in **Pick a workspace to develop your app in:** dropdown. Click on **Create App**.
|
||||
|
||||
4. Go to the **OAuth & Permissions** tab on the sidebar and add the following **Bot Token Scopes**:
|
||||
|
||||
- `channels:read`
|
||||
- `groups:read`
|
||||
- `chat:write`
|
||||
- `chat:write.public`
|
||||
- `chat:write.customize`
|
||||
* `channels:read`
|
||||
|
||||
* `groups:read`
|
||||
|
||||
* `chat:write`
|
||||
|
||||
* `chat:write.public`
|
||||
|
||||
* `chat:write.customize`
|
||||
|
||||
5. Add the **Redirect URLs** under **OAuth & Permissions** tab. You can add the following URLs:
|
||||
|
||||
- If you are running formbricks locally, you can enter `https://localhost:3000/api/v1/integrations/slack/callback`.
|
||||
- Or, you can enter `https://<your-public-facing-url>/api/v1/integrations/slack/callback`
|
||||
* If you are running formbricks locally, you can enter `https://localhost:3000/api/v1/integrations/slack/callback`.
|
||||
|
||||
* Or, you can enter `https://<your-public-facing-url>/api/v1/integrations/slack/callback`
|
||||
|
||||
6. Now, click on **Install to Workspace** and **Allow** the permissions.
|
||||
|
||||
7. Go to the **Basic Information** tab on the sidebar and copy the **Client ID** and **Client Secret**. Copy them and set them as the environment variables in your Formbricks instance as:
|
||||
|
||||
- `SLACK_CLIENT_ID` - OAuth Client ID
|
||||
- `SLACK_CLIENT_SECRET` - OAuth Client Secret
|
||||
* `SLACK_CLIENT_ID` - OAuth Client ID
|
||||
|
||||
8. Now, you need to enable the public distribution of your app. Go to the **Basic Information** tab and click on the **Manage distribution** button and click on the "Distribute App".
|
||||
9. Scroll down to the **Share your app with other workspaces** section, complete the checklist and click on the **Activate public distribution** button.
|
||||
* `SLACK_CLIENT_SECRET` - OAuth Client Secret
|
||||
|
||||
1. Now, you need to enable the public distribution of your app. Go to the **Basic Information** tab and click on the **Manage distribution** button and click on the "Distribute App".
|
||||
|
||||
2. Scroll down to the **Share your app with other workspaces** section, complete the checklist and click on the **Activate public distribution** button.
|
||||
|
||||
### By now, your environment variables should include the below ones:
|
||||
|
||||
- `SLACK_CLIENT_ID`
|
||||
- `SLACK_CLIENT_SECRET`
|
||||
* `SLACK_CLIENT_ID`
|
||||
|
||||
* `SLACK_CLIENT_SECRET`
|
||||
|
||||
Voila! You have successfully enabled the Slack integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Formbricks Cloud](#formbricks-cloud) section to link a Slack workspace with Formbricks.
|
||||
|
||||
@@ -119,10 +125,13 @@ Voila! You have successfully enabled the Slack integration in your self-hosted F
|
||||
To remove the integration with Slack Workspace,
|
||||
|
||||
1. Visit the Integrations tab in your Formbricks Cloud dashboard.
|
||||
|
||||
2. Select "Manage" button in the Slack card.
|
||||
|
||||
3. Click on the "Delete Integration" button.
|
||||
|
||||
4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Slack Workspace.
|
||||
|
||||

|
||||
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you!
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: "Webhooks"
|
||||
description: "With webhooks, you can receive real-time HTTP notifications when specific objects change in your Formbricks environment. This allows you to stay updated and trigger automated actions based on these events."
|
||||
---
|
||||
|
||||
With webhooks, you can receive real-time HTTP notifications when specific objects change in your Formbricks environment. This allows you to stay updated and trigger automated actions based on these events.
|
||||
|
||||
### **Webhook Triggers**
|
||||
|
||||
Webhooks are configured to send notifications based on trigger events. The available triggers include:
|
||||
|
||||
@@ -4,16 +4,20 @@ description:
|
||||
"Target specific visitors with a survey on your WordPress page using Formbricks for free. Show survey on specific page or on button click."
|
||||
---
|
||||
|
||||
# Connect Formbricks with your WordPress page
|
||||
To run a targeted survey on your WordPress website, Formbricks is the way to go! 
|
||||
|
||||
If you want to run a targeted survey on your WordPress website, Formbricks is the way to go! With our generous free plan and open source tech, you get everything you need to get started and keep full control over your data.
|
||||
With our generous free plan and open source tech, you get everything you need to get started and keep full control over your data.
|
||||
|
||||
## TLDR
|
||||
|
||||
1. Install the Formbricks WordPress plugin
|
||||
|
||||
2. Create a [free Formbricks account](https://app.formbricks.com/auth/signup)
|
||||
|
||||
3. Find and copy the `environment id`
|
||||
|
||||
4. Copy the environment id into the right field in the plugin settings
|
||||
|
||||
5. Create survey on trigger “New Session” to test it
|
||||
|
||||
## Step 1: Install the Formbricks WordPress plugin
|
||||
@@ -30,7 +34,6 @@ When you see this screen, you’re there:
|
||||
|
||||

|
||||
|
||||
|
||||
## Step 3: Find and copy the environmentId
|
||||
|
||||
Go to Settings > Setup Checklist where you’ll find your environmentId:
|
||||
@@ -53,30 +56,28 @@ Great!
|
||||
|
||||
Now that all is setup, we create a survey to display an example survey. Create a survey and open survey editor.
|
||||
|
||||
|
||||
Keep the content for now, click on the Settings tab:
|
||||
|
||||

|
||||
|
||||
|
||||
Here we do three things:
|
||||
|
||||
1. Change survey type to **App Survey** or **Website Survey**(for public facing)
|
||||
|
||||
2. Select trigger “New Session”
|
||||
|
||||
3. Publish
|
||||
|
||||

|
||||
|
||||
|
||||
When you see this page, you did it!
|
||||
|
||||

|
||||
|
||||
|
||||
## Step 6: Reload your page to check out your survey 🤓
|
||||
|
||||
You did it! Reload the WordPress page and your survey should appear!
|
||||
|
||||
## Doesn't work?
|
||||
|
||||
If you have any questions or need help, feel free to reach out to us on **[Github Discussions](https://github.com/formbricks/formbricks/discussions)**
|
||||
If you have any questions or need help, feel free to reach out to us on [Github Discussions](https://github.com/formbricks/formbricks/discussions)
|
||||
@@ -1,15 +1,10 @@
|
||||
---
|
||||
title: "Zapier"
|
||||
description:
|
||||
"Master the integration of Formbricks with Zapier using our detailed guide. Seamlessly connect your surveys to 5000+ apps, automate data transfers, and enhance feedback management. Start optimizing your workflow today."
|
||||
description: "Master the integration of Formbricks with Zapier using our detailed guide. Seamlessly connect your surveys to 5000+ apps, automate data transfers, and enhance feedback management. Start optimizing your workflow today."
|
||||
---
|
||||
|
||||
Zapier is a powerful ally. Hook up Formbricks with Zapier and you can send your data to 5000+ other apps. Here is how to do it.
|
||||
|
||||
<Note>
|
||||
### Nail down your survey first? Any changes in the survey cause additional
|
||||
work in the Zap. It makes sense to first settle on the survey you want to run
|
||||
and then get to setting up Zapier.
|
||||
Nail down your survey first? Any changes in the survey cause additional work in the Zap. It makes sense to first settle on the survey you want to run and then get to setting up Zapier.
|
||||
</Note>
|
||||
|
||||
## Step 1: Setup your survey incl. `questionId` for every question
|
||||
@@ -18,7 +13,7 @@ When setting up the Zap your life will be easier when you change the `questionId
|
||||
|
||||

|
||||
|
||||
_In every question card in the Advanced Settings you find the Question ID field. Update it so that you’ll recognize the response tied to this question._
|
||||
*In every question card in the Advanced Settings you find the Question ID field. Update it so that you’ll recognize the response tied to this question.*
|
||||
|
||||
<Note>
|
||||
Already published? Duplicate survey You can only update the questionId
|
||||
@@ -29,7 +24,7 @@ _In every question card in the Advanced Settings you find the Question ID field.
|
||||
|
||||
## Step 2: Send a test response
|
||||
|
||||
In order to set up Zapier you’ll need a test response. This allows you to select the individual values of each response in your Zap.
|
||||
In order to set up Zapier you’ll need a test response. This allows you to select the individual values of each response in your Zap.
|
||||
|
||||

|
||||
|
||||
@@ -51,7 +46,6 @@ Now, you have to connect Zapier with Formbricks via an API Key:
|
||||
|
||||

|
||||
|
||||
|
||||
Now you need an API key. Please refer to the [API Key Setup](/api-reference/rest-api#how-to-generate-an-api-key) page to learn how to create one.
|
||||
|
||||
Once you copied it in the newly opened Zapier window, you will be connected:
|
||||
@@ -70,7 +64,7 @@ Once you hit “Test” you will see the three most recent submissions for this
|
||||
|
||||

|
||||
|
||||
_Now you're happy that you updated the questionId's_
|
||||
*Now you're happy that you updated the questionId's*
|
||||
|
||||
## Step 7: Set up your Zap
|
||||
|
||||
@@ -84,5 +78,4 @@ In the action itself we can determine the data and layout of the message. Here,
|
||||
|
||||
We now receive a notifcation in our Slack channel whenever a Churn survey is completed:
|
||||
|
||||

|
||||
|
||||

|
||||
42
docs/xm-and-surveys/enterprise-features/saml-sso.mdx
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: "SAML SSO"
|
||||
icon: "user-shield"
|
||||
description: "How to set up SAML SSO for Formbricks"
|
||||
---
|
||||
|
||||
<Note>This feature is only available with the Formbricks Enterprise plan having a SAML SSO add-on.</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
Formbricks supports Security Assertion Markup Language (SAML) SSO. We prioritize your ease of access and security by providing robust Single Sign-On (SSO) capabilities.
|
||||
|
||||
### Setting up SAML login
|
||||
|
||||
<Steps>
|
||||
<Step title='Create a SAML application with your Identity Provider (IdP)'>
|
||||
Follow the instructions here - [SAML Setup](/development/guides/auth-and-provision/setup-saml-with-identity-providers)
|
||||
</Step>
|
||||
<Step title='Configure access to the IdP SAML app'>
|
||||
Ensure that all users who need access to Formbricks have access to the IdP SAML
|
||||
app.
|
||||
</Step>
|
||||
<Step title='Retrieve XML metadata from your IdP'>
|
||||
Keep the XML metadata from your IdP accessible, as you will need it later.
|
||||
</Step>
|
||||
<Step title='Log in to your Organization Admin account'>
|
||||
Visit `environments/<ENVIRONMENT_ID>/settings/sso`.
|
||||
<img src='/images/xm-and-surveys/core-features/saml-sso/sso-settings.webp' />
|
||||
</Step>
|
||||
<Step title='Configure SSO with SAML'>
|
||||
Click on the `Configure` button for `SSO with SAML`.
|
||||
<img src='/images/xm-and-surveys/core-features/saml-sso/sso-settings-configure.webp' />
|
||||
</Step>
|
||||
<Step title='Paste the XML metadata and Save'>
|
||||
In the SAML configuration section, copy and paste the XML metadata from step
|
||||
3 and click on Save.
|
||||
<img src='/images/xm-and-surveys/core-features/saml-sso/sso-settings-modal.webp' />
|
||||
</Step>
|
||||
<Step title='Your users can now log into Formbricks using SAML'>
|
||||
Once setup is complete, provisioned users can log into Formbricks using SAML.
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -8,17 +8,11 @@ Experience Management is the practice of measuring and managing how a stakeholde
|
||||
|
||||
Historically, Experience Management has three steps:
|
||||
|
||||
1. **Gather**
|
||||
1. **Gather** data
|
||||
|
||||
data
|
||||
2. **Analyze** and report on the data
|
||||
|
||||
2. **Analyze**
|
||||
|
||||
and report on the data
|
||||
|
||||
3. **Integrate**
|
||||
|
||||
and automate to measure experiences at scale
|
||||
3. **Integrate** and automate to measure experiences at scale
|
||||
|
||||
## Gather data
|
||||
|
||||
@@ -32,4 +26,4 @@ We're working on a fully compliant way to leverage AI to harvest insights from u
|
||||
|
||||
## Integrate & Automate
|
||||
|
||||
Experience Management scales best, when it is automated. Webhooks and the comprehensive REST API make it fast and easy to build integrations into your existing tech stack. Formbricks also powers integrations for n8n, ActivePieces, Zapier and Make.com to build any flow that you need.
|
||||
Experience Management scales best, when it is automated. Webhooks and the comprehensive REST API make it fast and easy to build integrations into your existing tech stack. Formbricks also powers integrations for n8n, ActivePieces, Zapier and Make.com to build any flow that you need.
|
||||
@@ -123,7 +123,6 @@
|
||||
"are_you_sure": "Bist Du sicher?",
|
||||
"are_you_sure_this_action_cannot_be_undone": "Bist Du sicher? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"attributes": "Attribute",
|
||||
"automatic": "Automatisch",
|
||||
"avatar": "Avatar",
|
||||
"back": "Zurück",
|
||||
"billing": "Abrechnung",
|
||||
@@ -180,7 +179,6 @@
|
||||
"edit": "Bearbeiten",
|
||||
"email": "E-Mail",
|
||||
"embed": "Einbetten",
|
||||
"enable": "Aktivieren",
|
||||
"enterprise_license": "Enterprise Lizenz",
|
||||
"environment_not_found": "Umgebung nicht gefunden",
|
||||
"environment_notice": "Du befindest dich derzeit in der {environment}-Umgebung.",
|
||||
@@ -1835,8 +1833,7 @@
|
||||
"multiple_industries": "Mehrere Branchen",
|
||||
"use_this_template": "Vorlage verwenden",
|
||||
"uses_branching_logic": "Diese Umfrage verwendet Logik."
|
||||
},
|
||||
"this_is_a_new_key": "Das ist ein neuer Schlüssel "
|
||||
}
|
||||
},
|
||||
"xm-templates": {
|
||||
"ces": "CES",
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
"are_you_sure": "Are you sure?",
|
||||
"are_you_sure_this_action_cannot_be_undone": "Are you sure? This action cannot be undone.",
|
||||
"attributes": "Attributes",
|
||||
"automatic": "Automatic",
|
||||
"avatar": "Avatar",
|
||||
"back": "Back",
|
||||
"billing": "Billing",
|
||||
@@ -180,7 +179,6 @@
|
||||
"edit": "Edit",
|
||||
"email": "Email",
|
||||
"embed": "Embed",
|
||||
"enable": "Enable",
|
||||
"enterprise_license": "Enterprise License",
|
||||
"environment_not_found": "Environment not found",
|
||||
"environment_notice": "You're currently in the {environment} environment.",
|
||||
@@ -1835,8 +1833,7 @@
|
||||
"multiple_industries": "Multiple industries",
|
||||
"use_this_template": "Use this template",
|
||||
"uses_branching_logic": "This survey uses branching logic."
|
||||
},
|
||||
"this_is_a_new_key": "This is a updated key"
|
||||
}
|
||||
},
|
||||
"xm-templates": {
|
||||
"ces": "CES",
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
"are_you_sure": "Es-tu sûr ?",
|
||||
"are_you_sure_this_action_cannot_be_undone": "Êtes-vous sûr ? Cette action ne peut pas être annulée.",
|
||||
"attributes": "Attributs",
|
||||
"automatic": "Automatique",
|
||||
"avatar": "Avatar",
|
||||
"back": "Retour",
|
||||
"billing": "Facturation",
|
||||
@@ -180,7 +179,6 @@
|
||||
"edit": "Modifier",
|
||||
"email": "Email",
|
||||
"embed": "Intégrer",
|
||||
"enable": "Activer",
|
||||
"enterprise_license": "Licence d'entreprise",
|
||||
"environment_not_found": "Environnement non trouvé",
|
||||
"environment_notice": "Vous êtes actuellement dans l'environnement {environment}.",
|
||||
@@ -1835,8 +1833,7 @@
|
||||
"multiple_industries": "Plusieurs secteurs",
|
||||
"use_this_template": "Utilisez ce modèle",
|
||||
"uses_branching_logic": "Cette enquête utilise une logique de branchement."
|
||||
},
|
||||
"this_is_a_new_key": "Ceci est une clé mise à jour"
|
||||
}
|
||||
},
|
||||
"xm-templates": {
|
||||
"ces": "CES",
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
"are_you_sure": "Certeza?",
|
||||
"are_you_sure_this_action_cannot_be_undone": "Tem certeza? Essa ação não pode ser desfeita.",
|
||||
"attributes": "atributos",
|
||||
"automatic": "Automático",
|
||||
"avatar": "Avatar",
|
||||
"back": "Voltar",
|
||||
"billing": "Faturamento",
|
||||
@@ -180,7 +179,6 @@
|
||||
"edit": "Editar",
|
||||
"email": "Email",
|
||||
"embed": "incorporar",
|
||||
"enable": "habilitar",
|
||||
"enterprise_license": "Licença Empresarial",
|
||||
"environment_not_found": "Ambiente não encontrado",
|
||||
"environment_notice": "Você está atualmente no ambiente {environment}.",
|
||||
@@ -1835,8 +1833,7 @@
|
||||
"multiple_industries": "várias indústrias",
|
||||
"use_this_template": "Use esse modelo",
|
||||
"uses_branching_logic": "Essa pesquisa usa lógica de ramificação."
|
||||
},
|
||||
"this_is_a_new_key": "Esta é uma chave atualizada"
|
||||
}
|
||||
},
|
||||
"xm-templates": {
|
||||
"ces": "CES",
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
"are_you_sure": "您確定嗎?",
|
||||
"are_you_sure_this_action_cannot_be_undone": "您確定嗎?此操作無法復原。",
|
||||
"attributes": "屬性",
|
||||
"automatic": "自動",
|
||||
"avatar": "頭像",
|
||||
"back": "返回",
|
||||
"billing": "帳單",
|
||||
@@ -180,7 +179,6 @@
|
||||
"edit": "編輯",
|
||||
"email": "電子郵件",
|
||||
"embed": "嵌入",
|
||||
"enable": "啟用",
|
||||
"enterprise_license": "企業授權",
|
||||
"environment_not_found": "找不到環境",
|
||||
"environment_notice": "您目前在 '{'environment'}' 環境中。",
|
||||
@@ -1835,8 +1833,7 @@
|
||||
"multiple_industries": "多個產業",
|
||||
"use_this_template": "使用此範本",
|
||||
"uses_branching_logic": "此問卷使用分支邏輯。"
|
||||
},
|
||||
"this_is_a_new_key": "這是一個更新的 key"
|
||||
}
|
||||
},
|
||||
"xm-templates": {
|
||||
"ces": "CES",
|
||||
|
||||
@@ -19,6 +19,7 @@ export const ZPlacement = z.enum(["bottomLeft", "bottomRight", "topLeft", "topRi
|
||||
export type TPlacement = z.infer<typeof ZPlacement>;
|
||||
|
||||
export const ZAllowedFileExtension = z.enum([
|
||||
"heic",
|
||||
"png",
|
||||
"jpeg",
|
||||
"jpg",
|
||||
|
||||
86
pnpm-lock.yaml
generated
@@ -387,6 +387,9 @@ importers:
|
||||
googleapis:
|
||||
specifier: 144.0.0
|
||||
version: 144.0.0(encoding@0.1.13)
|
||||
heic-convert:
|
||||
specifier: 2.1.0
|
||||
version: 2.1.0
|
||||
https-proxy-agent:
|
||||
specifier: 7.0.6
|
||||
version: 7.0.6
|
||||
@@ -520,6 +523,9 @@ importers:
|
||||
'@types/bcryptjs':
|
||||
specifier: 2.4.6
|
||||
version: 2.4.6
|
||||
'@types/heic-convert':
|
||||
specifier: 2.1.0
|
||||
version: 2.1.0
|
||||
'@types/lodash':
|
||||
specifier: 4.17.13
|
||||
version: 4.17.13
|
||||
@@ -591,7 +597,7 @@ importers:
|
||||
version: 8.18.0(eslint@8.57.0)(typescript@5.7.2)
|
||||
'@vercel/style-guide':
|
||||
specifier: 6.0.0
|
||||
version: 6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@3.0.5(tsx@4.19.2))
|
||||
version: 6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
|
||||
eslint-config-next:
|
||||
specifier: 15.1.0
|
||||
version: 15.1.0(eslint@8.57.0)(typescript@5.7.2)
|
||||
@@ -5467,6 +5473,9 @@ packages:
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
|
||||
'@types/heic-convert@2.1.0':
|
||||
resolution: {integrity: sha512-Cf5Sdc2Gm2pfZ0uN1zjj35wcf3mF1lJCMIzws5OdJynrdMJRTIRUGa5LegbVg0hatzOPkH2uAf2JRjPYgl9apg==}
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6':
|
||||
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
|
||||
|
||||
@@ -8359,8 +8368,8 @@ packages:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hast-util-to-jsx-runtime@2.3.2:
|
||||
resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==}
|
||||
hast-util-to-jsx-runtime@2.3.3:
|
||||
resolution: {integrity: sha512-pdpkP8YD4v+qMKn2lnKSiJvZvb3FunDmFYQvVOsoO08+eTNWdaWKPMrC5wwNICtU3dQWHhElj5Sf5jPEnv4qJg==}
|
||||
|
||||
hast-util-whitespace@3.0.0:
|
||||
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
|
||||
@@ -8369,6 +8378,14 @@ packages:
|
||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||
hasBin: true
|
||||
|
||||
heic-convert@2.1.0:
|
||||
resolution: {integrity: sha512-1qDuRvEHifTVAj3pFIgkqGgJIr0M3X7cxEPjEp0oG4mo8GFjq99DpCo8Eg3kg17Cy0MTjxpFdoBHOatj7ZVKtg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
heic-decode@2.0.0:
|
||||
resolution: {integrity: sha512-NU+zsiDvdL+EebyTjrEqjkO2XYI7FgLhQzsbmO8dnnYce3S0PBSDm/ZyI4KpcGPXYEdb5W72vp/AQFuc4F8ASg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
help-me@3.0.0:
|
||||
resolution: {integrity: sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==}
|
||||
|
||||
@@ -8944,6 +8961,9 @@ packages:
|
||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
jpeg-js@0.4.4:
|
||||
resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
|
||||
|
||||
js-cookie@2.2.1:
|
||||
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
|
||||
|
||||
@@ -9133,6 +9153,10 @@ packages:
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
libheif-js@1.18.2:
|
||||
resolution: {integrity: sha512-4Nk0dKhhRfVS4mECcX2jSDpNU6gcHQLneJjkGQq61N8COGtjSpSA3CI+1Q3kUYv5Vf+SwIqUtaDSdU6JO37c6w==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
lighthouse-logger@1.4.2:
|
||||
resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==}
|
||||
|
||||
@@ -10478,6 +10502,10 @@ packages:
|
||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
pngjs@6.0.0:
|
||||
resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
|
||||
polished@4.3.1:
|
||||
resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -10747,8 +10775,8 @@ packages:
|
||||
prop-types@15.8.1:
|
||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||
|
||||
property-information@6.5.0:
|
||||
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
|
||||
property-information@7.0.0:
|
||||
resolution: {integrity: sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
@@ -18841,6 +18869,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
'@types/heic-convert@2.1.0': {}
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||
|
||||
'@types/istanbul-lib-report@3.0.3':
|
||||
@@ -19259,7 +19289,7 @@ snapshots:
|
||||
next: 15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
|
||||
'@vercel/style-guide@6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@3.0.5(tsx@4.19.2))':
|
||||
'@vercel/style-guide@6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.26.0
|
||||
'@babel/eslint-parser': 7.26.5(@babel/core@7.26.0)(eslint@8.57.0)
|
||||
@@ -19267,7 +19297,7 @@ snapshots:
|
||||
'@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.7.2)
|
||||
eslint-config-prettier: 9.1.0(eslint@8.57.0)
|
||||
eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0))
|
||||
eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.31.0)
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.0)
|
||||
eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)
|
||||
@@ -19279,7 +19309,7 @@ snapshots:
|
||||
eslint-plugin-testing-library: 6.5.0(eslint@8.57.0)(typescript@5.7.2)
|
||||
eslint-plugin-tsdoc: 0.2.17
|
||||
eslint-plugin-unicorn: 51.0.1(eslint@8.57.0)
|
||||
eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@3.0.5(tsx@4.19.2))
|
||||
eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
|
||||
prettier-plugin-packagejson: 2.5.8(prettier@3.4.2)
|
||||
optionalDependencies:
|
||||
'@next/eslint-plugin-next': 15.1.0
|
||||
@@ -19369,7 +19399,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
vite: 5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
|
||||
|
||||
'@vitest/mocker@3.0.5(vite@6.0.9(tsx@4.19.2))':
|
||||
'@vitest/mocker@3.0.5(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.5
|
||||
estree-walker: 3.0.3
|
||||
@@ -21378,9 +21408,9 @@ snapshots:
|
||||
eslint: 8.57.0
|
||||
eslint-plugin-turbo: 2.3.3(eslint@8.57.0)
|
||||
|
||||
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)):
|
||||
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0):
|
||||
dependencies:
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)
|
||||
|
||||
eslint-import-resolver-node@0.3.9:
|
||||
dependencies:
|
||||
@@ -21402,11 +21432,11 @@ snapshots:
|
||||
is-glob: 4.0.3
|
||||
stable-hash: 0.0.4
|
||||
optionalDependencies:
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0):
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
@@ -21461,7 +21491,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0)
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
@@ -21628,7 +21658,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@3.0.5(tsx@4.19.2)):
|
||||
eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.7.2)
|
||||
eslint: 8.57.0
|
||||
@@ -22399,7 +22429,7 @@ snapshots:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
hast-util-to-jsx-runtime@2.3.2:
|
||||
hast-util-to-jsx-runtime@2.3.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
'@types/hast': 3.0.4
|
||||
@@ -22411,7 +22441,7 @@ snapshots:
|
||||
mdast-util-mdx-expression: 2.0.1
|
||||
mdast-util-mdx-jsx: 3.2.0
|
||||
mdast-util-mdxjs-esm: 2.0.1
|
||||
property-information: 6.5.0
|
||||
property-information: 7.0.0
|
||||
space-separated-tokens: 2.0.2
|
||||
style-to-object: 1.0.8
|
||||
unist-util-position: 5.0.0
|
||||
@@ -22425,6 +22455,16 @@ snapshots:
|
||||
|
||||
he@1.2.0: {}
|
||||
|
||||
heic-convert@2.1.0:
|
||||
dependencies:
|
||||
heic-decode: 2.0.0
|
||||
jpeg-js: 0.4.4
|
||||
pngjs: 6.0.0
|
||||
|
||||
heic-decode@2.0.0:
|
||||
dependencies:
|
||||
libheif-js: 1.18.2
|
||||
|
||||
help-me@3.0.0:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
@@ -23028,6 +23068,8 @@ snapshots:
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
jpeg-js@0.4.4: {}
|
||||
|
||||
js-cookie@2.2.1: {}
|
||||
|
||||
js-sdsl@4.3.0: {}
|
||||
@@ -23245,6 +23287,8 @@ snapshots:
|
||||
dependencies:
|
||||
isomorphic.js: 0.2.5
|
||||
|
||||
libheif-js@1.18.2: {}
|
||||
|
||||
lighthouse-logger@1.4.2:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
@@ -24933,6 +24977,8 @@ snapshots:
|
||||
|
||||
pngjs@5.0.0: {}
|
||||
|
||||
pngjs@6.0.0: {}
|
||||
|
||||
polished@4.3.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.7
|
||||
@@ -25137,7 +25183,7 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
react-is: 16.13.1
|
||||
|
||||
property-information@6.5.0: {}
|
||||
property-information@7.0.0: {}
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
@@ -25325,7 +25371,7 @@ snapshots:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/react': 19.0.1
|
||||
devlop: 1.1.0
|
||||
hast-util-to-jsx-runtime: 2.3.2
|
||||
hast-util-to-jsx-runtime: 2.3.3
|
||||
html-url-attributes: 3.0.1
|
||||
mdast-util-to-hast: 13.2.0
|
||||
react: 19.0.0
|
||||
@@ -27274,7 +27320,7 @@ snapshots:
|
||||
vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 3.0.5
|
||||
'@vitest/mocker': 3.0.5(vite@6.0.9(tsx@4.19.2))
|
||||
'@vitest/mocker': 3.0.5(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
|
||||
'@vitest/pretty-format': 3.0.5
|
||||
'@vitest/runner': 3.0.5
|
||||
'@vitest/snapshot': 3.0.5
|
||||
|
||||