Merge branch 'main' into surveyBg

This commit is contained in:
Anjy Gupta
2023-10-30 18:44:41 +05:30
committed by GitHub
70 changed files with 567 additions and 526 deletions

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Implementing Code Actions in Formbricks | Real-time User Action Tracking",
description:
"Dive into the world of Formbricks' code actions. Learn how to seamlessly integrate formbricks.track() method into your codebase, enabling real-time tracking of user actions like button clicks, visiting a specific URL. Up your survey game with precise and exact triggers.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Implementing No-Code Actions in Formbricks | Real-time User Action Tracking",
description:
"Discover the power of Formbricks' No-Code Actions. Easily set up triggers based on Page URL, innerText, and CSS Selectors without touching a line of code. Inccrease user engagement and get insights at precise moments in the user journey.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Using Actions in Formbricks | Fine-tuning User Moments",
description:
"Dive deep into how actions in Formbricks help products and teams to engage users at precise moments in their journey. Discover the power of actions, from coding to no-code setups, to refine user targeting and generate richer, more detailed user insights.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks Public Client API Guide: Manage Survey Displays & Responses",
description:
"Dive deep into Formbricks' Public Client API designed for customisation. This comprehensive guide provides detailed instructions on how to mark surveys as displayed as well as responded for individual persons, ensuring seamless client-side interactions without compromising data security.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Formbricks API Overview: Public Client & Management API Breakdown",
description:
"Get a detailed understanding of Formbricks' dual API offerings: the unauthenticated Public Client API optimized for client-side tasks and the secured Management API for advanced account operations. Choose the perfect fit for your integration needs and ensure robust data handling",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks Responses API Documentation - Manage Your Survey Data Seamlessly",
description:
"Unlock the full potential of Formbricks' Responses API. From fetching to updating survey responses, our comprehensive guide helps you integrate and manage survey data efficiently without compromising security. Ideal for client-side interactions.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks People API: Fetch or Create Person Overview",
description:
"Dive into Formbricks' People API within the Public Client API suite, designed to work without authentication requirements. Seamlessly fetch or create a person by their userId and environmentId, optimizing client-side interactions while maintaining data privacy.",

View File

@@ -3,7 +3,7 @@ import Image from "next/image";
import AddApiKey from "./add-api-key.webp";
import ApiKeySecret from "./api-key-secret.webp";
export const meta = {
export const metadata = {
title: "Formbricks API Key: Setup and Testing",
description:
"This guide provides step-by-step instructions to generate, store, and delete API keys, ensuring safe and authenticated access to your Formbricks account.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks People API: Fetch or Create Person Overview",
description:
"Dive into Formbricks' People API within the Public Client API suite, designed to work without authentication requirements. Seamlessly fetch or create a person by their userId and environmentId, optimizing client-side interAttributes while maintaining data privacy.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks People API: Fetch or Create Person Overview",
description:
"Dive into Formbricks' People API within the Public Client API suite, designed to work without authentication requirements. Seamlessly fetch or create a person by their userId and environmentId, optimizing client-side interactions while maintaining data privacy.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks People API: Fetch or Create Person Overview",
description:
"Dive into Formbricks' People API within the Public Client API suite, designed to work without authentication requirements. Seamlessly fetch or create a person by their userId and environmentId, optimizing client-side interactions while maintaining data privacy.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks Responses API Documentation - Manage Your Survey Data Seamlessly",
description:
"Unlock the full potential of Formbricks' Responses API. From fetching to updating survey responses, our comprehensive guide helps you integrate and manage survey data efficiently without compromising security. Ideal for client-side interactions.",

View File

@@ -1,6 +1,6 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
export const metadata = {
title: "Formbricks Surveys API Documentation - How to Retrieve All Surveys",
description:
"Explore the comprehensive guide to the Formbricks Surveys API. Learn how to effectively retrieve all the surveys in your environment with the necessary headers and API key setup. Includes sample request and response formats.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Formbricks Webhook API Documentation - List, Retrieve, Create, and Delete Webhooks",
description:
"Explore the comprehensive guide to the Formbricks Webhooks API. This is all you need to interact and play with the Formbricks Webhooks and integrate them into any third party app of your choice",
@@ -20,7 +20,7 @@ This set of API can be used to
- [Create Webhook](#create-webhook)
- [Delete Webhook](#delete-webhook-by-id)
And the detailed Webhook Paylod is elaborated [here](#webhook-payload).
And the detailed Webhook Payload is elaborated [here](#webhook-payload).
These APIs are designed to facilitate seamless integration of Formbricks with third-party systems. By making use of our webhook API, you can automate the process of sending data to these systems whenever significant events occur within your Formbricks environment.

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Guide for Setting Custom Attributes | Formbricks Documentation",
description:
"Learn how to set attributes in code using setAttribute function. Enhance user segmentation, target surveys effectively, and gather valuable insights for better decisions. Easily send user-specific details for better survey segmentation and gain deeper insights.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "User Identification in Formbricks | Enhancing Survey Feedback",
description:
"A comprehensive guide on identifying users in Formbricks without compromising privacy. Learn how to set User ID, email, and custom attributes to optimize survey targeting, recontact users, and control survey intervals, all while respecting user anonymity.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Understanding User Attributes in Formbricks Surveys",
description:
"Dive into the importance of attributes in surveys. Learn how key-value pairs can significantly improve survey targeting, enhance feedback quality, and guide data-driven decisions with Formbricks.",

View File

@@ -10,7 +10,7 @@ import RecontactOptions from "./recontact-options.webp";
import PublishSurvey from "./publish-survey.webp";
import SelectAction from "./select-action.webp";
export const meta = {
export const metadata = {
title: "Mastering Churn Surveys with Formbricks | Essential Tips & Steps",
description: "Learn how to effectively utilize Formbricks' Churn Surveys to gain deeper insights into user departures. Dive into a step-by-step guide to craft, trigger, and optimize your churn surveys, ensuring you capture invaluable feedback at critical junctures",
};
@@ -23,7 +23,7 @@ Churn is hard, but can teach you a lot. Whenever a user decides that your produc
## Purpose
The Churn Survey is among the most effective ways to identify weaknesses in you offering. People were willing to pay but now are not anymore: What changed? Lets find out!
The Churn Survey is among the most effective ways to identify weaknesses in your offering. People were willing to pay but now are not anymore: What changed? Lets find out!
## Preview

View File

@@ -10,7 +10,7 @@ import SwitchToDev from "./switch-to-dev.webp";
import WhenToAsk from "./when-to-ask.webp";
import CopyIds from "./copy-ids.webp";
export const meta = {
export const metadata = {
title:
"Integrate Docs Feedback in Your Website: A Step-by-Step Guide on getting feedback on your Documentation with Formbricks",
description:
@@ -74,7 +74,7 @@ To get this running, you'll need a bit of time. Here are the steps we're going t
5. In the same way, you can change the Internal Question ID of the _Please elaborate_ question to **“additionalFeedback”** and the one of the _Page URL_ question to **“pageUrl”**.
<Note>
## Answers need to be identical If you want different answers than “Yes 👍” and “No 👎” you need update the
## Answers need to be identical If you want different answers than “Yes 👍” and “No 👎” you need to update the
choices accordingly. They have to be identical to the frontend we're building in the next step.
</Note>

View File

@@ -9,7 +9,7 @@ import Publish from "./publish.webp";
import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
export const metadata = {
title: "Setting Up Feature Chaser Surveys with Formbricks: A Comprehensive Guide",
description: "Learn how to harness the power of Formbricks to gather targeted user feedback on specific features. Dive deep into creating, triggering, and publishing the Feature Chaser survey to enhance your product with actionable insights for specific users.",
};
@@ -22,7 +22,7 @@ Following up on specific features only makes sense with very targeted surveys. F
## Purpose
Product analytics never tell you why a feature is used - and why not. Following up on specfic features with highly relevant questions is a great way to gather feedback and improve your product.
Product analytics never tell you why a feature is used - and why not. Following up on specific features with highly relevant questions is a great way to gather feedback and improve your product.
## Preview

View File

@@ -11,7 +11,7 @@ import PublishSurvey from "./publish-survey.webp";
import SelectAction from "./select-feedback-button-action.webp";
import RecontactOptions from "./set-recontact-options.webp";
export const meta = {
export const metadata = {
title: "Implementing the Feedback Box with Formbricks: A Step-by-Step Tutorial",
description: "Unlock user insights effortlessly! Discover how to set up the Feedback Box in your app using Formbricks, allowing your users to provide real-time feedback. Follow our comprehensive guide to enhance user experience and respond rapidly to feedback",
};

View File

@@ -9,7 +9,7 @@ import Publish from "./publish.webp";
import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
export const metadata = {
title: "Boost Your Trial Conversion Rates with Formbricks: Comprehensive Guide",
description: "Unlock the secret to converting more trial users into paying customers using Formbricks. Understand insights behind trial cancellations and tailor your offering to fit user needs. Dive into our step-by-step tutorial and improve your conversion strategy today",
};

View File

@@ -12,7 +12,7 @@ import Publish from "./publish-survey.webp";
import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
export const metadata = {
title: "Maximize User Interview Participation with In-app Interview Prompts",
description: "Engage with your power users seamlessly using Formbricks' In-app Interview Prompt. Ditch traditional email invites and experience way more more respondents. Dive into our comprehensive guide on setting up auto-scheduled interviews today and enhance your user understanding",
};
@@ -112,7 +112,7 @@ To create the trigger to show your Interview Prompt, go to the “Audience” ta
appear in your Actions overview as long as the SDK is embedded.
</Note>
Generally, we have two types of user actions: Page views and clicks. The Interview Prompt, youll likely want to display on a page visit since you already filter who sees the prompt by attributes.
Generally, we have two types of user actions: Page views and clicks. The Interview Prompt, youll likely want to display it on a page visit since you already filter who sees the prompt by attributes.
1. **pageURL:** Whenever a user visits a page the survey will be displayed, as long as the other conditions match. Other conditions are pre-segmentation, if this user has seen a survey in the past 2 weeks, etc.

View File

@@ -9,7 +9,7 @@ import Publish from "./publish.webp";
import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
export const metadata = {
title: "How to Set Up a Product-Market Fit Survey Using Formbricks - Step-by-Step Guide",
description: "Learn to leverage Formbricks to create and implement a Product-Market Fit survey in your web app. Follow our detailed step-by-step guide to measure and understand your PMF effectively. Ensure high data quality, efficient triggers, and actionable insights.",
};

View File

@@ -2,7 +2,7 @@ import Image from "next/image";
import DemoApp from "./demoapp.webp";
export const meta = {
export const metadata = {
title: "Formbricks Demo App Guide: Play around with Formbricks",
description: "To test in-app surveys, trigger actions and set attributes, you can use the Demo App. This guide provides hands-on examples of sending both code and no-code actions",
};

View File

@@ -5,7 +5,7 @@ import GitpodNewWorkspace from "./gitpod-new-workspace.webp";
import GitpodPreparing from "./gitpod-preparing.webp";
import GitpodRunning from "./gitpod-running.webp";
export const meta = {
export const metadata = {
title: "Gitpod Setup",
description:
"With one click, you can setup the Formbricks developer environment in your browser using Gitpod",
@@ -74,7 +74,7 @@ After clicking the one-click setup button, Gitpod will open a new tab or window.
- This is the Gitpod Authentication Page. It appears when you click the "Open in GitPod" button and Gitpod needs to authenticate your access to the workspace. Click on 'Continue With Github' to authorize your GitPod session.
### 3. Creating a New Workspace
<Image src={GitpodNewWorkspace} alt="Gitpod New Worskpace Page" quality="100" className="rounded-lg max-w-full sm:max-w-3xl" />
<Image src={GitpodNewWorkspace} alt="Gitpod New workspace Page" quality="100" className="rounded-lg max-w-full sm:max-w-3xl" />
- After authentication, Gitpod asks to create a new workspace for you. This page displays the configurations of your workspace.
- You can use either choose either VS Code Browser or VS Code Desktop editor with the 'Standard Class' for your workspace class.
- If you opt for the VS Code Desktop, follow the following steps
@@ -83,7 +83,7 @@ After clicking the one-click setup button, Gitpod will open a new tab or window.
### 4. Gitpod preparing the created Workspace
<Image src={GitpodPreparing} alt="Gitpod Preparing Worskpace Page" quality="100" className="rounded-lg max-w-full sm:max-w-3xl" />
<Image src={GitpodPreparing} alt="Gitpod Preparing workspace Page" quality="100" className="rounded-lg max-w-full sm:max-w-3xl" />
- Gitpod is preparing your workspace with all the necessary dependencies and configurations. You will see this page while Gitpod sets up your development environment.
### 5. Gitpod running the Workspace

View File

@@ -1,7 +1,7 @@
import Image from "next/image";
import CorsHandling from "./cors-handling-in-api.webp";
export const meta = {
export const metadata = {
title: "Formbricks Code Contribution Guide: Best Practices and Standards",
description:
"Effortlessly Navigate Your Contribution Journey with Formbricks' Coding Guidelines and PR Review Process",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Formbricks Open Source Contribution Guide: How to Enhance yourself and Contribute to Formbricks",
description:
"Join the Formbricks community and learn how to effectively contribute. From raising issues and feature requests to creating PRs, discover the best practices and communicate with our responsive team on Discord",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Formbricks Development Setup: Complete Guide to Local Environment Configuration for Dev",
description:
"Step-by-step guide to setting up your local development environment for Formbricks. Includes installing essential tools like Node.JS, pnpm, and Docker, and accessing the entire Formbricks stack including the Demo app and the main website",

View File

@@ -4,7 +4,7 @@ import ClearAppData from "./clear-app-data.webp";
import UncaughtPromise from "./uncaught-promise.webp";
import Logout from "./logout.webp";
export const meta = {
export const metadata = {
title: "Formbricks Troubleshooting Guide: How to Solve & Debug Common Issues",
description:
"Facing issues with Formbricks? This troubleshooting guide covers frequently encountered problems, from Prisma migrations to package errors and more. Detailed solutions, accompanied by visual aids, ensure a smoother user experience with Formbricks",

View File

@@ -1,6 +1,6 @@
import FAQ from "@/components/docs/docsFaq";
export const meta = {
export const metadata = {
title: "FAQ",
description: "Frequently Asked Questions about Formbricks and how to use it.",
};

View File

@@ -13,7 +13,7 @@ export const metadata = {
# Framework Guides
One can integrate Formbricks into their app using multipe options! Checkout the options below that we provide! If you are looking
One can integrate Formbricks into their app using multiple options! Checkout the options below that we provide! If you are looking
for something else, please [join our Discord!](https://formbricks.com/discord) and we would be glad to help. {{ className: 'lead' }}
<Libraries />
@@ -24,7 +24,7 @@ for something else, please [join our Discord!](https://formbricks.com/discord) a
Before getting started, make sure you have:
1. A web application in your desired framework set up and running.
1. A web application in your desired framework is set up and running.
2. A Formbricks account with access to your environment ID and API host. You can find these in the **Setup Checklist** in the Settings:
<Image
@@ -64,7 +64,7 @@ All you need to do is copy a `<script>` tag to your HTML head, and thats abou
</Property>
</Properties>
Refer our [Example HTML project](https://github.com/formbricks/examples/tree/main/html) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
Refer to our [Example HTML project](https://github.com/formbricks/examples/tree/main/html) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
---
@@ -135,7 +135,7 @@ The app initializes 'formbricks' when it's loaded in a browser environment (due
className="max-w-full rounded-lg sm:max-w-3xl"
/>
Refer our [Example ReactJs project](https://github.com/formbricks/examples/tree/main/reactjs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
Refer to our [Example ReactJs project](https://github.com/formbricks/examples/tree/main/reactjs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
---
@@ -213,7 +213,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</CodeGroup>
</Col>
Refer our [Example NextJS App Directory project](https://github.com/formbricks/examples/tree/main/nextjs-app) for more help!
Refer to our [Example NextJS App Directory project](https://github.com/formbricks/examples/tree/main/nextjs-app) for more help!
### Pages Directory
@@ -252,7 +252,7 @@ export default function App({ Component, pageProps }: AppProps) {
</CodeGroup>
</Col>
Refer our [Example NextJS Pages Directory project](https://github.com/formbricks/examples/tree/main/nextjs-pages) for more help!
Refer to our [Example NextJS Pages Directory project](https://github.com/formbricks/examples/tree/main/nextjs-pages) for more help!
### Required Customizations to be Made
@@ -364,7 +364,7 @@ router.afterEach((to, from) => {
</Property>
</Properties>
Refer our [Example VueJs project](https://github.com/formbricks/examples/tree/main/vuejs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
Refer to our [Example VueJs project](https://github.com/formbricks/examples/tree/main/vuejs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
## Validate your setup

View File

@@ -13,7 +13,7 @@ import I10 from "./10-micro-survey-pop-up-in-app.webp";
import I11 from "./11-survey-logs-in-app-survey-popup.webp";
import ReactApp from "../framework-guides/react-in-app-survey-app-popup-form.webp";
export const meta = {
export const metadata = {
title: "Formbricks Quickstart Guide: In-App Surveys Made Simple",
description: "Launch your first in-app survey effortlessly. Dive into our step-by-step guide to set up, integrate, and debug Formbricks in your web app in under 15 minutes.",
};

View File

@@ -9,7 +9,7 @@ import ListLinkedSurveys from "./list-linked-surveys.webp";
import DeleteConnection from "./delete-connection.webp";
import Image from "next/image";
export const meta = {
export const metadata = {
title: "n8n Setup",
description: "Wire up Formbricks with n8n and 350+ other apps",
};

View File

@@ -14,7 +14,7 @@ import SelectFields from "./select-fields.webp";
import Result from "./result.webp";
import SelectAction from "./select-action.webp";
export const meta = {
export const metadata = {
title: "Formbricks Integration with Make.com: A Step-by-Step Guide",
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.",
};

View File

@@ -18,7 +18,7 @@ import SubmitTestResponse from "./submit-test-response.png";
import SuccessConnection from "./success-connection.png";
import UpdateQuestionId from "./update-question-id.png";
export const meta = {
export const metadata = {
title: "Comprehensive Guide to Integrating Formbricks with n8n",
description: "Unlock the potential of combining Formbricks with n8n for a streamlined workflow experience. Dive into our step-by-step guide and send your survey data effortlessly to 350+ applications. Streamline your data processes now!",
};

View File

@@ -14,7 +14,7 @@ import TestSubmission from "./test-submission.webp";
import UpdateQuestionId from "./update-question-id.webp";
import ZapierMessage from "./zapier-message.webp";
export const meta = {
export const metadata = {
title: "Step-by-Step Guide to Integrating Formbricks with 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.",
};

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Inside Look: Formbricks In-Product Micro-Surveys",
description:
"Unlock the full potential of Formbricks: From intuitive form-building and event-based triggers to effortless integrations and deep analytics. Master the art of in-product surveys for your SaaS or digital platform.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Formbricks vs. Generic Survey Tools: A Comparative Insight",
description:
"Discover how Formbricks excels as a specialized in-product micro-survey platform for SaaS. Get unmatched targeting, seamless integrations, and make informed decisions with our open-source advantage.",

View File

@@ -8,7 +8,7 @@ import { type Metadata } from "next";
export const metadata: Metadata = {
title: {
template: "Formbricks Open Source Experience Management",
template: "%s - Formbricks Docs",
default: "Formbricks Docs",
},
};

View File

@@ -2,7 +2,7 @@ import Image from "next/image";
import QuestionId from "./question-id.webp";
export const meta = {
export const metadata = {
title: "URL Data Prefilling for Link Surveys in Formbricks",
description: "Master the art of data prefilling in Formbricks link surveys. Dive into our guide on how to use URL parameters to prepopulate answers, boosting conversion rates and enhancing user experience. Learn through examples and ensure correct validation for each question type.",
};

View File

@@ -6,7 +6,7 @@ import Settings from "./single-use-setting.webp";
import Message from "./used-message.webp";
import Metadata from "./metadata.webp";
export const meta = {
export const metadata = {
title: "Single Use Links",
description: "Make sure that each respondent only replies once with single use links.",
};
@@ -59,7 +59,7 @@ You can encrypt single use URLs to assure information to be protected. To enable
## Check suId of a submission
You can find the suId of each submission in the submission meta data. To view it, simplte hover over the Avatar:
You can find the suId of each submission in the submission meta data. To view it, simple hover over the Avatar:
<Image
src={Metadata}

View File

@@ -2,7 +2,7 @@ import Image from "next/image";
import PeopleView from "./people-view.webp";
export const meta = {
export const metadata = {
title: "Effective User Identification in Formbricks Link Surveys",
description:
"Discover how to seamlessly connect responses from Formbricks link surveys to existing users in your database. Learn the intricacies of the userId URL parameter to enhance user tracking, profiling, and segmentation, ensuring more personalized interactions and data-driven decisions.",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Comprehensive Guide to Self-Hosting Formbricks",
description:
"Discover versatile options to deploy Formbricks tailored to your expertise level. From Ubuntu setups using shell scripts, swift Docker deployments, to manual source configurations, harness the flexibility and power of Formbricks to fit your unique hosting needs. Dive in today!",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Guide to Deploying Formbricks Using Docker",
description:
"Step-by-step tutorial on how to effortlessly set up and run Formbricks via Docker. Explore the quick deployment process with Docker-Compose, learn how to update Formbricks, and troubleshoot common issues. Ideal for those looking for a hassle-free Formbricks experience",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Migrating Formbricks to v1.1",
description:
"Formbricks v1.1 comes with an amazing set of features including the ability to define most environment variables at runtime itself! No need to build the image again! This guide will help you migrate your existing Formbricks instance to v1.1",

View File

@@ -1,4 +1,4 @@
export const meta = {
export const metadata = {
title: "Step by Step Guide on Deploying Formbricks to Production on Ubuntu",
description:
"Master the swift deployment of Formbricks on an Ubuntu server with our step-by-step guide. Use a single command to automate Docker, Postgres DB, SSL certificate configuration, and more. Encounter issues? Dive into our troubleshooting steps or join our community on Discord for assistance.",

View File

@@ -246,7 +246,6 @@ export const navigation: Array<NavGroup> = [
{ title: "Deployment", href: "/docs/self-hosting/deployment" },
{ title: "Production", href: "/docs/self-hosting/production" },
{ title: "Docker", href: "/docs/self-hosting/docker" },
{ title: "From Source", href: "/docs/self-hosting/from-source" },
{ title: "Migration Guide", href: "/docs/self-hosting/migration-guide" },
],
},

View File

@@ -26,6 +26,7 @@ export default function UrlShortenerModal({ open, setOpen, webAppUrl }: UrlShort
register,
handleSubmit,
watch,
reset,
formState: { isSubmitting },
} = useForm<UrlShortenerFormDataProps>({
mode: "onSubmit",
@@ -60,6 +61,7 @@ export default function UrlShortenerModal({ open, setOpen, webAppUrl }: UrlShort
const resetForm = () => {
setUrlValidationState("default");
reset(); // resets the long url field
setShortUrl("");
};

View File

@@ -1,43 +0,0 @@
import EmptySpaceFiller from "@formbricks/ui/EmptySpaceFiller";
import { ActivityItemContent, ActivityItemIcon, ActivityItemPopover } from "./ActivityItemComponents";
import { TActivityFeedItem } from "@formbricks/types/activity";
import { TEnvironment } from "@formbricks/types/environment";
interface ActivityFeedProps {
activities: TActivityFeedItem[];
sortByDate: boolean;
environment: TEnvironment;
}
export default function ActivityFeed({ activities, sortByDate, environment }: ActivityFeedProps) {
const sortedActivities: TActivityFeedItem[] = activities.sort((a, b) =>
sortByDate
? new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
: new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
);
return (
<>
{sortedActivities.length === 0 ? (
<EmptySpaceFiller type={"event"} environment={environment} />
) : (
<div>
{sortedActivities.map((activityItem) => (
<li key={activityItem.id} className="list-none">
<div className="relative pb-12">
<span className="absolute left-6 top-4 -ml-px h-full w-0.5 bg-slate-200" aria-hidden="true" />
<div className="relative">
<ActivityItemPopover activityItem={activityItem}>
<div className="flex space-x-3 text-left">
<ActivityItemIcon activityItem={activityItem} />
<ActivityItemContent activityItem={activityItem} />
</div>
</ActivityItemPopover>
</div>
</div>
</li>
))}
</div>
)}
</>
);
}

View File

@@ -1,54 +1,30 @@
import { capitalizeFirstLetter } from "@/app/lib/utils";
import { TActivityFeedItem } from "@formbricks/types/activity";
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/Popover";
import { Label } from "@formbricks/ui/Label";
import {
CodeBracketIcon,
CursorArrowRaysIcon,
EyeIcon,
QuestionMarkCircleIcon,
SparklesIcon,
TagIcon,
} from "@heroicons/react/24/solid";
import { formatDistance } from "date-fns";
import { TAction } from "@formbricks/types/actions";
import { CodeBracketIcon, CursorArrowRaysIcon, SparklesIcon } from "@heroicons/react/24/solid";
export const ActivityItemIcon = ({ activityItem }: { activityItem: TActivityFeedItem }) => (
export const ActivityItemIcon = ({ actionItem }: { actionItem: TAction }) => (
<div className="h-12 w-12 rounded-full bg-white p-3 text-slate-500 duration-100 ease-in-out group-hover:scale-110 group-hover:text-slate-600">
{activityItem.type === "attribute" ? (
<TagIcon />
) : activityItem.type === "display" ? (
<EyeIcon />
) : activityItem.type === "event" ? (
<div>
{activityItem.actionType === "code" && <CodeBracketIcon />}
{activityItem.actionType === "noCode" && <CursorArrowRaysIcon />}
{activityItem.actionType === "automatic" && <SparklesIcon />}
</div>
) : (
<QuestionMarkCircleIcon />
)}
<div>
{actionItem.actionClass?.type === "code" && <CodeBracketIcon />}
{actionItem.actionClass?.type === "noCode" && <CursorArrowRaysIcon />}
{actionItem.actionClass?.type === "automatic" && <SparklesIcon />}
</div>
</div>
);
export const ActivityItemContent = ({ activityItem }: { activityItem: TActivityFeedItem }) => (
export const ActivityItemContent = ({ actionItem }: { actionItem: TAction }) => (
<div>
<div className="font-semibold text-slate-700">
{activityItem.type === "attribute" ? (
<p>{capitalizeFirstLetter(activityItem.attributeLabel)} added</p>
) : activityItem.type === "display" ? (
<p>Seen survey</p>
) : activityItem.type === "event" ? (
<p>{activityItem.actionLabel} triggered</p>
) : (
<p>Unknown Activity</p>
)}
{actionItem.actionClass ? <p>{actionItem.actionClass.name}</p> : <p>Unknown Activity</p>}
</div>
<div className="text-sm text-slate-400">
<time
dateTime={formatDistance(activityItem.createdAt, new Date(), {
dateTime={formatDistance(actionItem.createdAt, new Date(), {
addSuffix: true,
})}>
{formatDistance(activityItem.createdAt, new Date(), {
{formatDistance(actionItem.createdAt, new Date(), {
addSuffix: true,
})}
</time>
@@ -57,10 +33,10 @@ export const ActivityItemContent = ({ activityItem }: { activityItem: TActivityF
);
export const ActivityItemPopover = ({
activityItem,
actionItem,
children,
}: {
activityItem: TActivityFeedItem;
actionItem: TAction;
children: React.ReactNode;
}) => {
return (
@@ -68,39 +44,15 @@ export const ActivityItemPopover = ({
<PopoverTrigger className="group">{children}</PopoverTrigger>
<PopoverContent className="bg-white">
<div>
{activityItem.type === "attribute" ? (
{actionItem && (
<div>
<Label className="font-normal text-slate-400">Attribute Label</Label>
<p className=" mb-2 text-sm font-medium text-slate-900">{activityItem.attributeLabel}</p>
<Label className="font-normal text-slate-400">Attribute Value</Label>
<p className="text-sm font-medium text-slate-900">{activityItem.attributeValue}</p>
<Label className="font-normal text-slate-400">Action Label</Label>
<p className=" mb-2 text-sm font-medium text-slate-900">{actionItem.actionClass!.name}</p>
<Label className="font-normal text-slate-400">Action Description</Label>
<p className="text-sm font-medium text-slate-900">{actionItem.actionClass!.description}</p>
<Label className="font-normal text-slate-400">Action Type</Label>
<p className="text-sm font-medium text-slate-900">{actionItem.actionClass!.type}</p>
</div>
) : activityItem.type === "display" ? (
<div>
<Label className="font-normal text-slate-400">Survey Name</Label>
<p className=" mb-2 text-sm font-medium text-slate-900">{activityItem.displaySurveyName}</p>
</div>
) : activityItem.type === "event" ? (
<div>
<div>
<Label className="font-normal text-slate-400">Action Display Name</Label>
<p className=" mb-2 text-sm font-medium text-slate-900">{activityItem.actionLabel}</p>{" "}
<Label className="font-normal text-slate-400">Action Description</Label>
<p className=" mb-2 text-sm font-medium text-slate-900">
{activityItem.actionDescription ? (
<span>{activityItem.actionDescription}</span>
) : (
<span>-</span>
)}
</p>
<Label className="font-normal text-slate-400">Action Type</Label>
<p className="text-sm font-medium text-slate-900">
{capitalizeFirstLetter(activityItem.actionType)}
</p>
</div>
</div>
) : (
<QuestionMarkCircleIcon />
)}
</div>
</PopoverContent>

View File

@@ -1,5 +1,5 @@
import ActivityTimeline from "@/app/(app)/environments/[environmentId]/people/[personId]/components/ActivityTimeline";
import { getActivityTimeline } from "@formbricks/lib/activity/service";
import { getActionsByPersonId } from "@formbricks/lib/action/service";
import { getEnvironment } from "@formbricks/lib/environment/service";
export default async function ActivitySection({
@@ -9,17 +9,18 @@ export default async function ActivitySection({
environmentId: string;
personId: string;
}) {
const [activities, environment] = await Promise.all([
getActivityTimeline(personId),
const [environment, actions] = await Promise.all([
getEnvironment(environmentId),
getActionsByPersonId(personId, 1),
]);
if (!environment) {
throw new Error("Environment not found");
}
return (
<div className="md:col-span-1">
<ActivityTimeline environment={environment} activities={activities} />
<ActivityTimeline environment={environment} actions={actions.slice(0, 10)} />
</div>
);
}

View File

@@ -1,37 +1,57 @@
"use client";
import ActivityFeed from "@/app/(app)/environments/[environmentId]/people/[personId]/components/ActivityFeed";
import { TActivityFeedItem } from "@formbricks/types/activity";
import { TAction } from "@formbricks/types/actions";
import { TEnvironment } from "@formbricks/types/environment";
import { ArrowsUpDownIcon } from "@heroicons/react/24/outline";
import { useState } from "react";
import EmptySpaceFiller from "@formbricks/ui/EmptySpaceFiller";
import { ActivityItemContent, ActivityItemIcon, ActivityItemPopover } from "./ActivityItemComponents";
export default function ActivityTimeline({
environment,
activities,
actions,
}: {
environment: TEnvironment;
activities: TActivityFeedItem[];
actions: TAction[];
}) {
const [activityAscending, setActivityAscending] = useState(true);
const toggleSortActivity = () => {
setActivityAscending(!activityAscending);
};
return (
<>
<div className="flex items-center justify-between pb-6">
<h2 className="text-lg font-bold text-slate-700">Activity Timeline</h2>
<div className="text-right">
<button
onClick={toggleSortActivity}
className="hover:text-brand-dark flex items-center px-1 text-slate-800">
<ArrowsUpDownIcon className="inline h-4 w-4" />
</button>
</div>
<h2 className="text-lg font-bold text-slate-700">Actions Timeline</h2>
</div>
<ActivityFeed activities={activities} sortByDate={activityAscending} environment={environment} />
<div className="relative">
{actions.length === 0 ? (
<EmptySpaceFiller type={"event"} environment={environment} />
) : (
<div>
{actions.map(
(actionItem, index) =>
actionItem && (
<li key={actionItem.id} className="list-none">
<div className="relative pb-12">
{index !== actions.length - 1 && (
<span
className="absolute left-6 top-4 -ml-px h-full w-0.5 bg-slate-200"
aria-hidden="true"
/>
)}
<div className="relative">
<ActivityItemPopover actionItem={actionItem}>
<div className="flex space-x-3 text-left">
<ActivityItemIcon actionItem={actionItem} />
<ActivityItemContent actionItem={actionItem} />
</div>
</ActivityItemPopover>
</div>
</div>
</li>
)
)}
<div className="relative">
{actions.length === 10 && (
<div className="absolute bottom-0 flex h-56 w-full items-end justify-center bg-gradient-to-t from-slate-50 to-transparent"></div>
)}
</div>
</div>
)}
</div>
</>
);
}

View File

@@ -2,36 +2,44 @@ import {
ActivityItemIcon,
ActivityItemPopover,
} from "@/app/(app)/environments/[environmentId]/people/[personId]/components/ActivityItemComponents";
import { TActivityFeedItem } from "@formbricks/types/activity";
import { TAction } from "@formbricks/types/actions";
import { BackIcon } from "@formbricks/ui/icons";
import { ArrowsUpDownIcon } from "@heroicons/react/24/outline";
import { TrashIcon } from "lucide-react";
export default function Loading() {
const unifiedList: TActivityFeedItem[] = [
const actionItemList: TAction[] = [
{
id: "clk9o7gnu000kz8kw4nb26o21",
type: "event",
actionType: "noCode",
id: "demoId1",
createdAt: new Date(),
actionLabel: "Loading User Acitivity",
updatedAt: null,
attributeLabel: null,
attributeValue: null,
actionDescription: null,
displaySurveyName: null,
sessionId: "",
properties: {},
actionClass: {
id: "demoId1",
createdAt: new Date(),
updatedAt: new Date(),
name: "Loading User Acitivity",
description: null,
type: "automatic",
noCodeConfig: null,
environmentId: "testEnvironment",
},
},
{
id: "clk9o7fwc000iz8kw4s0ha0ql",
type: "event",
actionType: "automatic",
id: "demoId2",
createdAt: new Date(),
actionLabel: "Loading User Session Info",
updatedAt: null,
attributeLabel: null,
attributeValue: null,
actionDescription: null,
displaySurveyName: null,
sessionId: "",
properties: {},
actionClass: {
id: "demoId2",
createdAt: new Date(),
updatedAt: new Date(),
name: "Loading User Acitivity",
description: null,
type: "automatic",
noCodeConfig: null,
environmentId: "testEnvironment",
},
},
];
return (
@@ -118,17 +126,17 @@ export default function Loading() {
</div>
</div>
<div>
{unifiedList.map((activityItem) => (
<li key={activityItem.id} className="list-none">
{actionItemList.map((actionItem) => (
<li key={actionItem.id} className="list-none">
<div className="relative pb-12">
<span
className="absolute left-6 top-4 -ml-px h-full w-0.5 bg-slate-200"
aria-hidden="true"
/>
<div className="relative animate-pulse cursor-not-allowed select-none">
<ActivityItemPopover activityItem={activityItem}>
<ActivityItemPopover actionItem={actionItem}>
<div className="flex cursor-not-allowed select-none items-center space-x-3">
<ActivityItemIcon activityItem={activityItem} />
<ActivityItemIcon actionItem={actionItem} />
<div className="font-semibold text-slate-700">Loading</div>
</div>
</ActivityItemPopover>

View File

@@ -192,7 +192,7 @@ export default function SettingsNavbar({
return (
<>
<div className="fixed hidden h-full bg-white py-2 pl-4 pr-10 md:block ">
<div className="fixed hidden h-full overflow-auto bg-white py-2 pl-4 pr-10 md:block">
<nav className="flex-1 space-y-1 bg-white px-2">
{navigation.map((item) => (
<div key={item.title}>

View File

@@ -76,7 +76,7 @@ export default function ResponseOptionsCard({
};
const handleProtectSurveyWithPinToggle = () => {
setLocalSurvey((prevSurvey) => ({ ...prevSurvey, pin: isPinProtectionEnabled ? null : 1234 }));
setLocalSurvey((prevSurvey) => ({ ...prevSurvey, pin: isPinProtectionEnabled ? null : "1234" }));
};
const handleProtectSurveyPinChange = (pin: string) => {

View File

@@ -6,7 +6,7 @@ import { XCircleIcon } from "@heroicons/react/24/solid";
import { signIn } from "next-auth/react";
import Link from "next/dist/client/link";
import { useRouter, useSearchParams } from "next/navigation";
import { useMemo, useRef, useState } from "react";
import { useMemo, useRef, useState, useEffect } from "react";
import { Controller, SubmitHandler, useForm, FormProvider } from "react-hook-form";
import { cn } from "@formbricks/lib/cn";
@@ -88,10 +88,16 @@ export const SigninForm = ({
const [totpBackup, setTotpBackup] = useState(false);
const [signInError, setSignInError] = useState("");
const formRef = useRef<HTMLFormElement>(null);
const error = searchParams?.get("error");
const callbackUrl = searchParams?.get("callbackUrl");
const inviteToken = callbackUrl ? new URL(callbackUrl).searchParams.get("token") : null;
useEffect(() => {
if (error) {
setSignInError(error);
}
}, []);
const formLabel = useMemo(() => {
if (totpBackup) {
return "Enter your backup code";

View File

@@ -1,5 +1,6 @@
import { NextRequest } from "next/server";
import { ImageResponse } from "next/og";
import { ImageResponse, NextRequest } from "next/server";
/* import { NextRequest } from "next/server";
import { ImageResponse } from "next/og"; */
// App router includes @vercel/og.
// No need to install it.

View File

@@ -51,13 +51,14 @@ export async function generateMetadata({ params }: LinkSurveyPageProps): Promise
const brandColor = getBrandColorForURL(product.brandColor);
const surveyName = getNameForURL(survey.name);
const ogImgURL = `${WEBAPP_URL}/api/v1/og?brandColor=${brandColor}&name=${surveyName}`;
const ogImgURL = `/api/v1/og?brandColor=${brandColor}&name=${surveyName}`;
return {
metadataBase: new URL(WEBAPP_URL),
openGraph: {
title: survey.name,
description: "Create your own survey like this with Formbricks' open source survey suite.",
url: `${WEBAPP_URL}/${survey.id}`,
url: `/s/${survey.id}`,
siteName: "",
images: [ogImgURL],
locale: "en_US",

View File

@@ -7,6 +7,9 @@ import "./env.mjs";
const nextConfig = {
assetPrefix: process.env.ASSET_PREFIX_URL || undefined,
output: "standalone",
experimental: {
serverActions: true,
},
transpilePackages: ["@formbricks/database", "@formbricks/ee", "@formbricks/ui", "@formbricks/lib"],
images: {
remotePatterns: [

View File

@@ -11,7 +11,7 @@
"lint": "next lint"
},
"dependencies": {
"@aws-sdk/s3-presigned-post": "^3.433.0",
"@aws-sdk/s3-presigned-post": "^3.438.0",
"@formbricks/api": "workspace:*",
"@formbricks/database": "workspace:*",
"@formbricks/ee": "workspace:*",
@@ -26,10 +26,10 @@
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@react-email/components": "^0.0.7",
"@sentry/nextjs": "^7.75.0",
"@vercel/og": "^0.5.20",
"@react-email/components": "^0.0.9",
"@sentry/nextjs": "^7.76.0",
"@t3-oss/env-nextjs": "^0.7.1",
"@vercel/og": "^0.5.20",
"bcryptjs": "^2.4.3",
"encoding": "^0.1.13",
"framer-motion": "10.16.4",
@@ -37,12 +37,12 @@
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"lru-cache": "^10.0.1",
"lucide-react": "^0.288.0",
"lucide-react": "^0.290.0",
"mime": "^3.0.0",
"next": "14.0.0",
"next": "13.5.6",
"nodemailer": "^6.9.7",
"otplib": "^12.0.1",
"posthog-js": "^1.85.1",
"posthog-js": "^1.87.3",
"prismjs": "^1.29.0",
"qrcode": "^1.5.3",
"react": "18.2.0",
@@ -52,7 +52,8 @@
"react-hook-form": "^7.47.0",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.11.0",
"ua-parser-js": "^1.0.36",
"ua-parser-js": "^1.0.37",
"webpack": "^5.89.0",
"xlsx": "^0.18.5"
},
"devDependencies": {

View File

@@ -8,13 +8,13 @@
"clean": "rimraf node_modules .turbo"
},
"devDependencies": {
"eslint": "^8.51.0",
"eslint-config-next": "^13.5.5",
"eslint": "^8.52.0",
"eslint-config-next": "^14.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-turbo": "latest",
"eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"eslint-plugin-react-refresh": "^0.4.4",
"eslint-plugin-storybook": "^0.6.15"
}
}

View File

@@ -2,6 +2,7 @@ import { revalidateTag } from "next/cache";
interface RevalidateProps {
environmentId?: string;
personId?: string;
}
export const actionCache = {
@@ -9,6 +10,9 @@ export const actionCache = {
byEnvironmentId(environmentId: string): string {
return `environments-${environmentId}-actions`;
},
byPersonId(personId: string): string {
return `environments-${personId}-actions`;
},
},
revalidate({ environmentId }: RevalidateProps): void {
if (environmentId) {

View File

@@ -70,6 +70,54 @@ export const getLatestActionByEnvironmentId = async (environmentId: string): Pro
: action;
};
export const getActionsByPersonId = async (personId: string, page?: number): Promise<TAction[]> => {
const actions = await unstable_cache(
async () => {
validateInputs([personId, ZId], [page, ZOptionalNumber]);
const actionsPrisma = await prisma.event.findMany({
where: {
session: {
personId: personId,
},
},
orderBy: {
createdAt: "desc",
},
take: page ? ITEMS_PER_PAGE : undefined,
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
include: {
eventClass: true,
},
});
const actions: TAction[] = [];
// transforming response to type TAction[]
actionsPrisma.forEach((action) => {
actions.push({
id: action.id,
createdAt: action.createdAt,
sessionId: action.sessionId,
properties: action.properties,
actionClass: action.eventClass,
});
});
return actions;
},
[`getActionsByPersonId-${personId}-${page}`],
{
tags: [actionCache.tag.byPersonId(personId)],
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
// Deserialize dates if caching does not support deserialization
return actions.map((action) => ({
...action,
createdAt: new Date(action.createdAt),
}));
};
export const getActionsByEnvironmentId = async (environmentId: string, page?: number): Promise<TAction[]> => {
const actions = await unstable_cache(
async () => {

View File

@@ -1,98 +0,0 @@
import "server-only";
import { prisma } from "@formbricks/database";
import { TActivityFeedItem } from "@formbricks/types/activity";
import { validateInputs } from "../utils/validate";
import { ZId } from "@formbricks/types/environment";
import { unstable_cache } from "next/cache";
import { ResourceNotFoundError } from "@formbricks/types/errors";
import { personCache } from "../person/cache";
import { formatActivityFeedItemDateFields } from "./util";
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
export const getActivityTimeline = async (personId: string): Promise<TActivityFeedItem[]> => {
const activityFeedItem = await unstable_cache(
async () => {
validateInputs([personId, ZId]);
const person = await prisma.person.findUnique({
where: {
id: personId,
},
include: {
attributes: {
include: {
attributeClass: true,
},
},
displays: {
include: {
survey: true,
},
},
sessions: {
include: {
events: {
include: {
eventClass: true,
},
},
},
},
},
});
if (!person) {
throw new ResourceNotFoundError("Person", personId);
}
const { attributes, displays, sessions } = person;
const unifiedAttributes: TActivityFeedItem[] = attributes.map((attribute) => ({
id: attribute.id,
type: "attribute",
createdAt: attribute.createdAt,
updatedAt: attribute.updatedAt,
attributeLabel: attribute.attributeClass.name,
attributeValue: attribute.value,
actionLabel: null,
actionDescription: null,
actionType: null,
displaySurveyName: null,
}));
const unifiedDisplays: TActivityFeedItem[] = displays.map((display) => ({
id: display.id,
type: "display",
createdAt: display.createdAt,
updatedAt: display.updatedAt,
attributeLabel: null,
attributeValue: null,
actionLabel: null,
actionDescription: null,
actionType: null,
displaySurveyName: display.survey.name,
}));
const unifiedEvents: TActivityFeedItem[] = sessions.flatMap((session) =>
session.events.map((event) => ({
id: event.id,
type: "event",
createdAt: event.createdAt,
updatedAt: null,
attributeLabel: null,
attributeValue: null,
actionLabel: event.eventClass?.name || null,
actionDescription: event.eventClass?.description || null,
actionType: event.eventClass?.type || null,
displaySurveyName: null,
}))
);
const unifiedList: TActivityFeedItem[] = [...unifiedAttributes, ...unifiedDisplays, ...unifiedEvents];
return unifiedList;
},
[`getActivityTimeline-${personId}`],
{ tags: [personCache.tag.byId(personId)], revalidate: SERVICES_REVALIDATION_INTERVAL }
)();
return formatActivityFeedItemDateFields(activityFeedItem);
};

View File

@@ -1,11 +0,0 @@
import { TActivityFeedItem } from "@formbricks/types/activity";
export const formatActivityFeedItemDateFields = (
activityFeedItem: TActivityFeedItem[]
): TActivityFeedItem[] => {
return activityFeedItem.map((item) => ({
...item,
createdAt: new Date(item.createdAt),
updatedAt: item.updatedAt ? new Date(item.updatedAt) : item.updatedAt,
}));
};

View File

@@ -453,11 +453,8 @@ export default function ToolbarPlugin(props: TextEditorProps) {
StartIcon={Bold}
onClick={() => {
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold");
if (isItalic) {
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic");
}
}}
className={isBold ? "bg-subtle" : ""}
className={isBold ? "bg-subtle active-button" : "inactive-button"}
/>
)}
{!props.excludedToolbarItems?.includes("italic") && (
@@ -468,11 +465,8 @@ export default function ToolbarPlugin(props: TextEditorProps) {
StartIcon={Italic}
onClick={() => {
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic");
if (isItalic) {
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold");
}
}}
className={isItalic ? "bg-subtle" : ""}
className={isItalic ? "bg-subtle active-button" : "inactive-button"}
/>
)}
{!props.excludedToolbarItems?.includes("link") && (
@@ -483,7 +477,7 @@ export default function ToolbarPlugin(props: TextEditorProps) {
type="button"
StartIcon={Link}
onClick={insertLink}
className={isLink ? "bg-subtle" : ""}
className={isLink ? "bg-subtle active-button" : "inactive-button"}
/>
{isLink && createPortal(<FloatingLinkEditor editor={editor} />, document.body)}{" "}
</>

View File

@@ -335,3 +335,12 @@ i.italic {
i.link {
background-image: url(images/icons/link.svg);
}
.active-button {
color: #000000;
}
.inactive-button {
color: #777;
}

View File

@@ -10,7 +10,7 @@
"devDependencies": {
"@formbricks/tsconfig": "workspace:*",
"@formbricks/types": "workspace:*",
"concurrently": "^8.2.1",
"concurrently": "^8.2.2",
"eslint-config-formbricks": "workspace:*",
"postcss": "^8.4.31",
"react": "18.2.0"
@@ -41,10 +41,10 @@
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"lexical": "^0.12.2",
"lucide-react": "^0.287.0",
"lucide-react": "^0.290.0",
"react-colorful": "^5.6.1",
"react-confetti": "^6.1.0",
"react-day-picker": "^8.9.0",
"react-day-picker": "^8.9.1",
"react-hot-toast": "^2.4.1",
"react-radio-group": "^3.0.3",
"react-use": "^17.4.0",

468
pnpm-lock.yaml generated
View File

@@ -282,7 +282,7 @@ importers:
apps/web:
dependencies:
'@aws-sdk/s3-presigned-post':
specifier: ^3.433.0
specifier: ^3.438.0
version: 3.438.0
'@formbricks/api':
specifier: workspace:*
@@ -327,11 +327,11 @@ importers:
specifier: ^2.0.6
version: 2.0.6(react-dom@18.2.0)(react@18.2.0)
'@react-email/components':
specifier: ^0.0.7
version: 0.0.7
specifier: ^0.0.9
version: 0.0.9(react@18.2.0)
'@sentry/nextjs':
specifier: ^7.75.0
version: 7.76.0(encoding@0.1.13)(next@14.0.0)(react@18.2.0)
specifier: ^7.76.0
version: 7.76.0(encoding@0.1.13)(next@13.5.6)(react@18.2.0)(webpack@5.89.0)
'@t3-oss/env-nextjs':
specifier: ^0.7.1
version: 0.7.1(zod@3.22.4)
@@ -360,14 +360,14 @@ importers:
specifier: ^10.0.1
version: 10.0.1
lucide-react:
specifier: ^0.288.0
version: 0.288.0(react@18.2.0)
specifier: ^0.290.0
version: 0.290.0(react@18.2.0)
mime:
specifier: ^3.0.0
version: 3.0.0
next:
specifier: 14.0.0
version: 14.0.0(react-dom@18.2.0)(react@18.2.0)
specifier: 13.5.6
version: 13.5.6(react-dom@18.2.0)(react@18.2.0)
nodemailer:
specifier: ^6.9.7
version: 6.9.7
@@ -375,8 +375,8 @@ importers:
specifier: ^12.0.1
version: 12.0.1
posthog-js:
specifier: ^1.85.1
version: 1.87.2
specifier: ^1.87.3
version: 1.87.3
prismjs:
specifier: ^1.29.0
version: 1.29.0
@@ -405,8 +405,11 @@ importers:
specifier: ^4.11.0
version: 4.11.0(react@18.2.0)
ua-parser-js:
specifier: ^1.0.36
specifier: ^1.0.37
version: 1.0.37
webpack:
specifier: ^5.89.0
version: 5.89.0
xlsx:
specifier: ^0.18.5
version: 0.18.5
@@ -510,17 +513,17 @@ importers:
packages/eslint-config-formbricks:
devDependencies:
eslint:
specifier: ^8.51.0
specifier: ^8.52.0
version: 8.52.0
eslint-config-next:
specifier: ^13.5.5
version: 13.5.6(eslint@8.52.0)(typescript@5.2.2)
specifier: ^14.0.0
version: 14.0.0(eslint@8.52.0)(typescript@5.2.2)
eslint-config-prettier:
specifier: ^9.0.0
version: 9.0.0(eslint@8.52.0)
eslint-config-turbo:
specifier: latest
version: 1.10.16(eslint@8.52.0)
version: 1.8.8(eslint@8.52.0)
eslint-plugin-react:
specifier: 7.33.2
version: 7.33.2(eslint@8.52.0)
@@ -528,7 +531,7 @@ importers:
specifier: ^4.6.0
version: 4.6.0(eslint@8.52.0)
eslint-plugin-react-refresh:
specifier: ^0.4.3
specifier: ^0.4.4
version: 0.4.4(eslint@8.52.0)
eslint-plugin-storybook:
specifier: ^0.6.15
@@ -857,8 +860,8 @@ importers:
specifier: ^0.12.2
version: 0.12.2
lucide-react:
specifier: ^0.287.0
version: 0.287.0(react@18.2.0)
specifier: ^0.290.0
version: 0.290.0(react@18.2.0)
mime:
specifier: ^3.0.0
version: 3.0.0
@@ -869,7 +872,7 @@ importers:
specifier: ^6.1.0
version: 6.1.0(react@18.2.0)
react-day-picker:
specifier: ^8.9.0
specifier: ^8.9.1
version: 8.9.1(date-fns@2.30.0)(react@18.2.0)
react-hot-toast:
specifier: ^2.4.1
@@ -888,7 +891,7 @@ importers:
specifier: workspace:*
version: link:../types
concurrently:
specifier: ^8.2.1
specifier: ^8.2.2
version: 8.2.2
eslint-config-formbricks:
specifier: workspace:*
@@ -4946,8 +4949,8 @@ packages:
resolution: {integrity: sha512-cIKhxkfVELB6hFjYsbtEeTus2mwrTC+JissfZYM0n+8Fv+g8ucUfOlm3VEDtwtwydZ0Nuauv3bl0qF82nnCAqA==}
dev: false
/@next/eslint-plugin-next@13.5.6:
resolution: {integrity: sha512-ng7pU/DDsxPgT6ZPvuprxrkeew3XaRf4LAT4FabaEO/hAbvVx4P7wqnqdbTdDn1kgTvsI4tpIgT4Awn/m0bGbg==}
/@next/eslint-plugin-next@14.0.0:
resolution: {integrity: sha512-Ye37nNI09V3yt7pzuzSQtwlvuJ2CGzFszHXkcTHHZgNr7EhTMFLipn3VSJChy+e5+ahTdNApPphc3qCPUsn10A==}
dependencies:
glob: 7.1.7
dev: true
@@ -4977,6 +4980,15 @@ packages:
dev: false
optional: true
/@next/swc-darwin-arm64@13.5.6:
resolution: {integrity: sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-arm64@14.0.0:
resolution: {integrity: sha512-HQKi159jCz4SRsPesVCiNN6tPSAFUkOuSkpJsqYTIlbHLKr1mD6be/J0TvWV6fwJekj81bZV9V/Tgx3C2HO9lA==}
engines: {node: '>= 10'}
@@ -4995,6 +5007,15 @@ packages:
dev: false
optional: true
/@next/swc-darwin-x64@13.5.6:
resolution: {integrity: sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64@14.0.0:
resolution: {integrity: sha512-4YyQLMSaCgX/kgC1jjF3s3xSoBnwHuDhnF6WA1DWNEYRsbOOPWjcYhv8TKhRe2ApdOam+VfQSffC4ZD+X4u1Cg==}
engines: {node: '>= 10'}
@@ -5013,6 +5034,15 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-gnu@13.5.6:
resolution: {integrity: sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-gnu@14.0.0:
resolution: {integrity: sha512-io7fMkJ28Glj7SH8yvnlD6naIhRDnDxeE55CmpQkj3+uaA2Hko6WGY2pT5SzpQLTnGGnviK85cy8EJ2qsETj/g==}
engines: {node: '>= 10'}
@@ -5031,6 +5061,15 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-musl@13.5.6:
resolution: {integrity: sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl@14.0.0:
resolution: {integrity: sha512-nC2h0l1Jt8LEzyQeSs/BKpXAMe0mnHIMykYALWaeddTqCv5UEN8nGO3BG8JAqW/Y8iutqJsaMe2A9itS0d/r8w==}
engines: {node: '>= 10'}
@@ -5049,6 +5088,15 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-gnu@13.5.6:
resolution: {integrity: sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu@14.0.0:
resolution: {integrity: sha512-Wf+WjXibJQ7hHXOdNOmSMW5bxeJHVf46Pwb3eLSD2L76NrytQlif9NH7JpHuFlYKCQGfKfgSYYre5rIfmnSwQw==}
engines: {node: '>= 10'}
@@ -5067,6 +5115,15 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-musl@13.5.6:
resolution: {integrity: sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl@14.0.0:
resolution: {integrity: sha512-WTZb2G7B+CTsdigcJVkRxfcAIQj7Lf0ipPNRJ3vlSadU8f0CFGv/ST+sJwF5eSwIe6dxKoX0DG6OljDBaad+rg==}
engines: {node: '>= 10'}
@@ -5085,6 +5142,15 @@ packages:
dev: false
optional: true
/@next/swc-win32-arm64-msvc@13.5.6:
resolution: {integrity: sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc@14.0.0:
resolution: {integrity: sha512-7R8/x6oQODmNpnWVW00rlWX90sIlwluJwcvMT6GXNIBOvEf01t3fBg0AGURNKdTJg2xNuP7TyLchCL7Lh2DTiw==}
engines: {node: '>= 10'}
@@ -5103,6 +5169,15 @@ packages:
dev: false
optional: true
/@next/swc-win32-ia32-msvc@13.5.6:
resolution: {integrity: sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc@14.0.0:
resolution: {integrity: sha512-RLK1nELvhCnxaWPF07jGU4x3tjbyx2319q43loZELqF0+iJtKutZ+Lk8SVmf/KiJkYBc7Cragadz7hb3uQvz4g==}
engines: {node: '>= 10'}
@@ -5121,6 +5196,15 @@ packages:
dev: false
optional: true
/@next/swc-win32-x64-msvc@13.5.6:
resolution: {integrity: sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc@14.0.0:
resolution: {integrity: sha512-g6hLf1SUko+hnnaywQQZzzb3BRecQsoKkF3o/C+F+dOA4w/noVAJngUVkfwF0+2/8FzNznM7ofM6TGZO9svn7w==}
engines: {node: '>= 10'}
@@ -6823,109 +6907,138 @@ packages:
dependencies:
'@babel/runtime': 7.23.2
/@react-email/body@0.0.2:
resolution: {integrity: sha512-SqZrZdxZlH7viwnrLvrMnVzOKpiofVAtho09bmm2siDzy0VMDGItXRzUPLcpg9vcbVJCHZRCIKoNXqA+PtokzQ==}
/@react-email/body@0.0.4(react@18.2.0):
resolution: {integrity: sha512-NmHOumdmyjWvOXomqhQt06KbgRxhHrVznxQp/oWiPWes8nAJo2Y4L27aPHR9nTcs7JF7NmcJe9YSN42pswK+GQ==}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/button@0.0.9:
resolution: {integrity: sha512-eYWQ1X4RFlkKYYSPgSrT6rk98wuLOieEAGENrp9j37t1v/1C+jMmBu0UjZvwHsHWdbOMRjbVDFeMI/+MxWKSEg==}
engines: {node: '>=16.0.0'}
/@react-email/button@0.0.11(react@18.2.0):
resolution: {integrity: sha512-mB5ySfZifwE5ybtIWwXGbmKk1uKkH4655gftL4+mMxZAZCkINVa2KXTi5pO+xZhMtJI9xtAsikOrOEU1gTDoww==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/column@0.0.7:
resolution: {integrity: sha512-B29wVXyIcuVprgGpLkR23waPh/twlqmugZQsCKk05JlMCQ80/Puv4Lgj4dRsIJzgyTLMwG6xq17+Uxc5iGfuaQ==}
engines: {node: '>=16.0.0'}
/@react-email/column@0.0.8(react@18.2.0):
resolution: {integrity: sha512-blChqGU8e/L6KZiB5EPww8bkZfdyHDuS0vKIvU+iS14uK+xfAw+5P5CU9BYXccEuJh2Gftfngu1bWMFp2Sc6ag==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/components@0.0.7:
resolution: {integrity: sha512-GpRKV8E7EvK9OPf61f5Z8hliB3p0hTot8tslmEUVCTtX7tdL0wM2YEcZiDWU4PJcudJ/QWHJ7Y5wGzNEARcooA==}
engines: {node: '>=16.0.0'}
/@react-email/components@0.0.9(react@18.2.0):
resolution: {integrity: sha512-ORFZ2QKiiIx5FTl5tg3JSAdcGbAVits4lBrN9DYsn9aB8BdN+na6XVImJeZTlXRA+lpqBtWoe1hlq9aZjMVs8w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
'@react-email/body': 0.0.2
'@react-email/button': 0.0.9
'@react-email/column': 0.0.7
'@react-email/container': 0.0.8
'@react-email/font': 0.0.2
'@react-email/head': 0.0.5
'@react-email/heading': 0.0.8
'@react-email/hr': 0.0.5
'@react-email/html': 0.0.4
'@react-email/img': 0.0.5
'@react-email/link': 0.0.5
'@react-email/preview': 0.0.6
'@react-email/render': 0.0.7
'@react-email/row': 0.0.5
'@react-email/section': 0.0.9
'@react-email/tailwind': 0.0.8
'@react-email/text': 0.0.5
'@react-email/body': 0.0.4(react@18.2.0)
'@react-email/button': 0.0.11(react@18.2.0)
'@react-email/column': 0.0.8(react@18.2.0)
'@react-email/container': 0.0.10(react@18.2.0)
'@react-email/font': 0.0.4(react@18.2.0)
'@react-email/head': 0.0.6(react@18.2.0)
'@react-email/heading': 0.0.9
'@react-email/hr': 0.0.6(react@18.2.0)
'@react-email/html': 0.0.6(react@18.2.0)
'@react-email/img': 0.0.6(react@18.2.0)
'@react-email/link': 0.0.6(react@18.2.0)
'@react-email/preview': 0.0.7(react@18.2.0)
'@react-email/render': 0.0.8
'@react-email/row': 0.0.6(react@18.2.0)
'@react-email/section': 0.0.10(react@18.2.0)
'@react-email/tailwind': 0.0.11(react@18.2.0)
'@react-email/text': 0.0.6(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
- ts-node
dev: false
/@react-email/container@0.0.8:
resolution: {integrity: sha512-MQZQxvTOoLWjJR+Jm689jltm0I/mtZbEaDnwZbNkkHKgccr++wwb9kOKMgXG777Y7tGa1JATAsZpvFYiCITwUg==}
engines: {node: '>=16.0.0'}
/@react-email/container@0.0.10(react@18.2.0):
resolution: {integrity: sha512-goishY7ocq+lord0043/LZK268bqvMFW/sxpUt/dSCPJyrrZZNCbpW2t8w8HztU38cYj0qGQLxO5Qvpn/RER3w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/font@0.0.2:
resolution: {integrity: sha512-mmkyOCAcbgytE7DfIuOBVG1YVDUZY9rPCor4o7pUEzGJiU2y/TNuV8CgNPSU/VgXeBKL/94QDjB62OrGHlFNMQ==}
/@react-email/font@0.0.4(react@18.2.0):
resolution: {integrity: sha512-rN/pFlAcDNmfYFxpufT/rFRrM5KYBJM4nTA2uylTehlVOro6fb/q6n0zUwLF6OmQ4QIuRbqdEy7DI9mmJiNHxA==}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/head@0.0.5:
resolution: {integrity: sha512-s84OxJxZMee2z5b1a+RVwY1NOSUNNf1ecjPf6n64aZmMNcNUyn4gOl7RO6xbfBrZko7TigBwsFB1Cgjxtn/ydg==}
engines: {node: '>=16.0.0'}
/@react-email/head@0.0.6(react@18.2.0):
resolution: {integrity: sha512-9BrBDalb34nBOmmQVQc7/pjJotcuAeC3rhBl4G88Ohiipuv15vPIKqwy8vPJcFNi4l7yGlitfG3EESIjkLkoIw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/heading@0.0.8:
resolution: {integrity: sha512-7atATmoHBHTk7hFYFsFFzOIBV3u1zPpsSOWkLBojdjSUdenpk2SbX8GP8/3aBhWl/tuFX9RBGcu1Xes+ZijFLg==}
/@react-email/heading@0.0.9:
resolution: {integrity: sha512-xzkcGlm+/aFrNlJZBKzxRKkRYJ2cRx92IqmSKAuGnwuKQ/uMKomXzPsHPu3Dclmnhn3wVKj4uprkgQOoxP6uXQ==}
engines: {node: '>=16.0.0'}
dependencies:
'@radix-ui/react-slot': 1.0.0(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.28)(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
dev: false
/@react-email/hr@0.0.5:
resolution: {integrity: sha512-nwB8GmSdvPlR/bWjDS07yHtgdfJqtvCaPXee3SVUY69YYP7NeDO/VACJlgrS9V2l79bj1lUpH0MJMU6MNAk5FQ==}
engines: {node: '>=16.0.0'}
/@react-email/hr@0.0.6(react@18.2.0):
resolution: {integrity: sha512-W+wINBz7z7BRv3i9GS+QoJBae1PESNhv6ZY6eLnEpqtBI/2++suuRNJOU/KpZzE6pykeTp6I/Z7UcL0LEYKgyg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/html@0.0.4:
resolution: {integrity: sha512-7tRYSnudYAWez+NkPWOM8yLZH7EuYFtYdiLPnzpD+pf4cdk16Gz4up531DaIX6dNBbfbyEFpQxhXZxGeJ5ZkfQ==}
engines: {node: '>=16.0.0'}
dev: false
/@react-email/img@0.0.5:
resolution: {integrity: sha512-9ziFgBfrIAL+DpVlsraFcd2KwsTRyobLpqTnoiBYCcVZGod59xbYkmsmB3CbUosmLwPYg6AeD7Q7e+hCiwkWgg==}
engines: {node: '>=16.0.0'}
/@react-email/html@0.0.6(react@18.2.0):
resolution: {integrity: sha512-8Fo20VOqxqc087gGEPjT8uos06fTXIC8NSoiJxpiwAkwiKtQnQH/jOdoLv6XaWh5Zt2clj1uokaoklnaM5rY1w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/link@0.0.5:
resolution: {integrity: sha512-z+QW9f4gXBdyfhl7iYMY3td+rXKeZYK/2AGElEMsxVoywn5D0b6cF8m5w2jbf0U2V3enT+zy9yc1R6AyT59NOg==}
engines: {node: '>=16.0.0'}
/@react-email/img@0.0.6(react@18.2.0):
resolution: {integrity: sha512-Wd7xKI3b1Jvb2ZEHyVpJ9D98u0GHrRl+578b8LV24PavM/65V61Q5LN5Fr9sAhj+4VGqnHDIVeXIYEzVbWaa3Q==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/preview@0.0.6:
resolution: {integrity: sha512-mXDCc3NGpm/4W7gowBtjsTxYXowLNOLsJsYhIfrsjNJWGlVhVFB9uEHm55LjBLpxSG020g6/8LIrpJU6g22qvg==}
engines: {node: '>=16.0.0'}
/@react-email/link@0.0.6(react@18.2.0):
resolution: {integrity: sha512-bYYHroWGS//nDl9yhh8V6K2BrNwAsyX7N/XClSCRku3x56NrZ6D0nBKWewYDPlJ9rW9TIaJm1jDYtO9XBzLlkQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/preview@0.0.7(react@18.2.0):
resolution: {integrity: sha512-YLfIwHdexPi8IgP1pSuVXdAmKzMQ8ctCCLEjkMttT2vkSFqT6m/e6UFWK2l30rKm2dDsLvQyEvo923mPXjnNzg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
@@ -6940,33 +7053,40 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-email/render@0.0.7:
resolution: {integrity: sha512-hMMhxk6TpOcDC5qnKzXPVJoVGEwfm+U5bGOPH+MyTTlx0F02RLQygcATBKsbP7aI/mvkmBAZoFbgPIHop7ovug==}
engines: {node: '>=16.0.0'}
/@react-email/render@0.0.8:
resolution: {integrity: sha512-DwrCG5t2nXlXYcchZz8h4NOhBYoU5Rpogz54Z18mANUmwMtzl88x6NwRDwzZoAVAlTLOPqvsQbYtWHvfpTfmmA==}
engines: {node: '>=18.0.0'}
dependencies:
html-to-text: 9.0.3
html-to-text: 9.0.5
pretty: 2.0.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-email/row@0.0.5:
resolution: {integrity: sha512-dir5l1M7Z/1BQqQkUrKUPIIDPt6ueEf6ScMGoBOcUh+VNNqmnqJE2Q2CH5X3w2uo6a5X7tnVhoJHGa2KTKe8Sw==}
engines: {node: '>=16.0.0'}
dev: false
/@react-email/section@0.0.9:
resolution: {integrity: sha512-3EbcWJ1jUZrzquWSvXrv8Hbk9V+BGvLcMWQIli4NdIpQlddmlGKUYfXU2mB2d2pf+5ojqkGcFZZ9fWxycB84jQ==}
engines: {node: '>=16.0.0'}
/@react-email/row@0.0.6(react@18.2.0):
resolution: {integrity: sha512-msJ2TnDJNwpgDfDzUO63CvhusJHeaGLMM+8Zz86VPvxzwe/DkT7N48QKRWRCkt8urxVz5U+EgivORA9Dum9p3Q==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/tailwind@0.0.8:
resolution: {integrity: sha512-0BLjD5GpiyBK7YDlaDrjHIpj9eTrrZrMJud3f1UPoCZhS+0S/M8LcR8WMbQsR+8/aLGmiy4F4TGZuRQcsJEsFw==}
engines: {node: '>=16.0.0'}
/@react-email/section@0.0.10(react@18.2.0):
resolution: {integrity: sha512-x9B2KYFqj+d8I1fK9bgeVm/3mLE4Qgn4mm/GbDtcJeSzKU/G7bTb7/3+BMDk9SARPGkg5XAuZm1XgcqQQutt2A==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/tailwind@0.0.11(react@18.2.0):
resolution: {integrity: sha512-YZXVLPksAE3ke7HZjWsqxWbxe1OeqVifz+b/Do6K5Hgk3Wb+2HBACTrkkhnqIVv9T4OaeZ4C6ZxTDqvSSSXQhw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
html-react-parser: 3.0.9(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
tw-to-css: 0.0.11
@@ -6974,9 +7094,11 @@ packages:
- ts-node
dev: false
/@react-email/text@0.0.5:
resolution: {integrity: sha512-LXhHiaC6oRRsNAfOzJDos4wQA22eIdVJvR6G7uu4QzUvYNOAatDMf89jRQcKGrxX7InkS640v8sHd9jl5ztM5w==}
engines: {node: '>=16.0.0'}
/@react-email/text@0.0.6(react@18.2.0):
resolution: {integrity: sha512-PDUTAD1PjlzXFOIUrR1zuV2xxguL62yne5YLcn1k+u/dVUyzn6iU/5lFShxCfzuh3QDWCf4+JRNnXN9rmV6jzw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
react: 18.2.0
dev: false
@@ -7070,6 +7192,13 @@ packages:
selderee: 0.10.0
dev: false
/@selderee/plugin-htmlparser2@0.11.0:
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
dependencies:
domhandler: 5.0.3
selderee: 0.11.0
dev: false
/@sentry-internal/tracing@7.76.0:
resolution: {integrity: sha512-QQVIv+LS2sbGf/e5P2dRisHzXpy02dAcLqENLPG4sZ9otRaFNjdFYEqnlJ4qko+ORpJGQEQp/BX7Q/qzZQHlAg==}
engines: {node: '>=8'}
@@ -7125,7 +7254,7 @@ packages:
localforage: 1.10.0
dev: false
/@sentry/nextjs@7.76.0(encoding@0.1.13)(next@14.0.0)(react@18.2.0):
/@sentry/nextjs@7.76.0(encoding@0.1.13)(next@13.5.6)(react@18.2.0)(webpack@5.89.0):
resolution: {integrity: sha512-3/iTnBJ7qOrhoEUQw85CmZ+S2wTZapRui5yfWO6/We11T8q6tvrUPIYmnE0BY/2BIelz4dfPwXRHXRJlgEarhg==}
engines: {node: '>=8'}
peerDependencies:
@@ -7146,11 +7275,12 @@ packages:
'@sentry/vercel-edge': 7.76.0
'@sentry/webpack-plugin': 1.20.0(encoding@0.1.13)
chalk: 3.0.0
next: 14.0.0(react-dom@18.2.0)(react@18.2.0)
next: 13.5.6(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
resolve: 1.22.8
rollup: 2.78.0
stacktrace-parser: 0.1.10
webpack: 5.89.0
transitivePeerDependencies:
- encoding
- supports-color
@@ -12669,8 +12799,8 @@ packages:
source-map: 0.6.1
dev: true
/eslint-config-next@13.5.6(eslint@8.52.0)(typescript@5.2.2):
resolution: {integrity: sha512-o8pQsUHTo9aHqJ2YiZDym5gQAMRf7O2HndHo/JZeY7TDD+W4hk6Ma8Vw54RHiBeb7OWWO5dPirQB+Is/aVQ7Kg==}
/eslint-config-next@14.0.0(eslint@8.52.0)(typescript@5.2.2):
resolution: {integrity: sha512-jtXeE+/pGQ3h9n11QyyuPN50kO13GO5XvjU5ZRq6W+XTpOMjyobWmK2s7aowy0FtzA49krJzYzEU9s1RMwoJ6g==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
typescript: '>=3.3.1'
@@ -12678,7 +12808,7 @@ packages:
typescript:
optional: true
dependencies:
'@next/eslint-plugin-next': 13.5.6
'@next/eslint-plugin-next': 14.0.0
'@rushstack/eslint-patch': 1.5.1
'@typescript-eslint/parser': 6.8.0(eslint@8.52.0)(typescript@5.2.2)
eslint: 8.52.0
@@ -12707,13 +12837,13 @@ packages:
resolution: {integrity: sha512-NB/L/1Y30qyJcG5xZxCJKW/+bqyj+llbcCwo9DEz8bESIP0SLTOQ8T1DWCCFc+wJ61AMEstj4511PSScqMMfCw==}
dev: true
/eslint-config-turbo@1.10.16(eslint@8.52.0):
resolution: {integrity: sha512-O3NQI72bQHV7FvSC6lWj66EGx8drJJjuT1kuInn6nbMLOHdMBhSUX/8uhTAlHRQdlxZk2j9HtgFCIzSc93w42g==}
/eslint-config-turbo@1.8.8(eslint@8.52.0):
resolution: {integrity: sha512-+yT22sHOT5iC1sbBXfLIdXfbZuiv9bAyOXsxTxFCWelTeFFnANqmuKB3x274CFvf7WRuZ/vYP/VMjzU9xnFnxA==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
eslint: 8.52.0
eslint-plugin-turbo: 1.10.16(eslint@8.52.0)
eslint-plugin-turbo: 1.8.8(eslint@8.52.0)
dev: true
/eslint-import-resolver-node@0.3.9:
@@ -12915,12 +13045,11 @@ packages:
- typescript
dev: true
/eslint-plugin-turbo@1.10.16(eslint@8.52.0):
resolution: {integrity: sha512-ZjrR88MTN64PNGufSEcM0tf+V1xFYVbeiMeuIqr0aiABGomxFLo4DBkQ7WI4WzkZtWQSIA2sP+yxqSboEfL9MQ==}
/eslint-plugin-turbo@1.8.8(eslint@8.52.0):
resolution: {integrity: sha512-zqyTIvveOY4YU5jviDWw9GXHd4RiKmfEgwsjBrV/a965w0PpDwJgEUoSMB/C/dU310Sv9mF3DSdEjxjJLaw6rA==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
dotenv: 16.0.3
eslint: 8.52.0
dev: true
@@ -14549,13 +14678,6 @@ packages:
/hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
/html-dom-parser@3.1.3:
resolution: {integrity: sha512-fI0yyNlIeSboxU+jnrA4v8qj4+M8SI9/q6AKYdwCY2qki22UtKCDTxvagHniECu7sa5/o2zFRdLleA67035lsA==}
dependencies:
domhandler: 5.0.3
htmlparser2: 8.0.1
dev: false
/html-encoding-sniffer@3.0.0:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
engines: {node: '>=12'}
@@ -14567,18 +14689,6 @@ packages:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
dev: true
/html-react-parser@3.0.9(react@18.2.0):
resolution: {integrity: sha512-gOPZmaCMXNYu7Y9+58k2tLhTMXQ+QN8ctNFijzLuBxJaLZ6TsN+tUpN+MhbI+6nGaBCRGT2rpw6y/AqkTFZckg==}
peerDependencies:
react: 0.14 || 15 || 16 || 17 || 18
dependencies:
domhandler: 5.0.3
html-dom-parser: 3.1.3
react: 18.2.0
react-property: 2.0.0
style-to-js: 1.1.3
dev: false
/html-tags@3.3.1:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'}
@@ -14595,13 +14705,15 @@ packages:
selderee: 0.10.0
dev: false
/htmlparser2@8.0.1:
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
/html-to-text@9.0.5:
resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==}
engines: {node: '>=14'}
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.1.0
entities: 4.5.0
'@selderee/plugin-htmlparser2': 0.11.0
deepmerge: 4.3.1
dom-serializer: 2.0.0
htmlparser2: 8.0.2
selderee: 0.11.0
dev: false
/htmlparser2@8.0.2:
@@ -16700,16 +16812,8 @@ packages:
dependencies:
yallist: 4.0.0
/lucide-react@0.287.0(react@18.2.0):
resolution: {integrity: sha512-auxP2bTGiMoELzX+6ItTeNzLmhGd/O+PHBsrXV2YwPXYCxarIFJhiMOSzFT9a1GWeYPSZtnWdLr79IVXr/5JqQ==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
dev: false
/lucide-react@0.288.0(react@18.2.0):
resolution: {integrity: sha512-ikhb/9LOkq9orPoLV9lLC4UYyoXQycBhIgH7H59ahOkk0mkcAqkD52m84RXedE/qVqZHW8rEJquInT4xGmsNqw==}
/lucide-react@0.290.0(react@18.2.0):
resolution: {integrity: sha512-CBDPRLOPjdo+bVlxhaa7FVWaB8OrZZQ34mwm0Fsz9ut6JltN/Td55640ur8bRWSJuz6+nX2klKrpBpV7ktwD3Q==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0
dependencies:
@@ -17996,6 +18100,45 @@ packages:
- babel-plugin-macros
dev: false
/next@13.5.6(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==}
engines: {node: '>=16.14.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
sass:
optional: true
dependencies:
'@next/env': 13.5.6
'@swc/helpers': 0.5.2
busboy: 1.6.0
caniuse-lite: 1.0.30001558
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(react@18.2.0)
watchpack: 2.4.0
optionalDependencies:
'@next/swc-darwin-arm64': 13.5.6
'@next/swc-darwin-x64': 13.5.6
'@next/swc-linux-arm64-gnu': 13.5.6
'@next/swc-linux-arm64-musl': 13.5.6
'@next/swc-linux-x64-gnu': 13.5.6
'@next/swc-linux-x64-musl': 13.5.6
'@next/swc-win32-arm64-msvc': 13.5.6
'@next/swc-win32-ia32-msvc': 13.5.6
'@next/swc-win32-x64-msvc': 13.5.6
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/next@14.0.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-J0jHKBJpB9zd4+c153sair0sz44mbaCHxggs8ryVXSFBuBqJ8XdE9/ozoV85xGh2VnSjahwntBZZgsihL9QznA==}
engines: {node: '>=18.17.0'}
@@ -18676,6 +18819,13 @@ packages:
peberminta: 0.8.0
dev: false
/parseley@0.12.1:
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
dependencies:
leac: 0.6.0
peberminta: 0.9.0
dev: false
/parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
@@ -18784,6 +18934,10 @@ packages:
resolution: {integrity: sha512-YYEs+eauIjDH5nUEGi18EohWE0nV2QbGTqmxQcqgZ/0g+laPCQmuIqq7EBLVi9uim9zMgfJv0QBZEnQ3uHw/Tw==}
dev: false
/peberminta@0.9.0:
resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
dev: false
/peek-readable@4.1.0:
resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==}
engines: {node: '>=8'}
@@ -19044,8 +19198,8 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/posthog-js@1.87.2:
resolution: {integrity: sha512-pdxEylfxwEDwwz7g5dunPucvAN51RAOWWQmkcqHsLNHlV5o5bTaTwcAXaWB1IUn3xKPuKYE2lqbdB3vC4H4rFQ==}
/posthog-js@1.87.3:
resolution: {integrity: sha512-aHs7owrIgptnsTZjlPAJG0HRBLIHlThsCGhET0PaYKdcmg2g90pqIRv1Y9hiemEtATXDAHUmwbcCzx4AopaW7g==}
dependencies:
fflate: 0.4.8
dev: false
@@ -19695,10 +19849,6 @@ packages:
- supports-color
dev: false
/react-property@2.0.0:
resolution: {integrity: sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==}
dev: false
/react-radio-group@3.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-MUNRRjZqQ2y+1K6rBuH0zO+gLVmCnWIcc5GnNwr9WNoUwZ9FUAKJ1UfsKXwYS93whR6/qrZKoVgiOltRkbzezw==}
peerDependencies:
@@ -20591,6 +20741,12 @@ packages:
parseley: 0.11.0
dev: false
/selderee@0.11.0:
resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
dependencies:
parseley: 0.12.1
dev: false
/semver-diff@2.1.0:
resolution: {integrity: sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==}
engines: {node: '>=0.10.0'}
@@ -21388,18 +21544,6 @@ packages:
peek-readable: 4.1.0
dev: true
/style-to-js@1.1.3:
resolution: {integrity: sha512-zKI5gN/zb7LS/Vm0eUwjmjrXWw8IMtyA8aPBJZdYiQTXj4+wQ3IucOLIOnF7zCHxvW8UhIGh/uZh/t9zEHXNTQ==}
dependencies:
style-to-object: 0.4.1
dev: false
/style-to-object@0.4.1:
resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==}
dependencies:
inline-style-parser: 0.1.1
dev: false
/style-to-object@0.4.4:
resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==}
dependencies: