fix: tailwind imports that were causing prod issues

This commit is contained in:
ShubhamPalriwala
2023-08-28 22:04:22 +05:30
parent 9edf9955ae
commit 25fcac72d8
75 changed files with 547 additions and 422 deletions

View File

@@ -4,7 +4,7 @@ export const meta = {
"Integrate code actions in Formbricks using formbricks.track() to trigger surveys based on user actions, like button clicks, for precise insights. All open-source.",
};
[Actions]()
#### Actions
# Code Actions

View File

@@ -4,7 +4,7 @@ export const meta = {
"Utilize Formbricks' No-Code Actions like Page URL, innerText, and CSS Selector for easy survey triggers and enhanced user insights.",
};
[Actions]()
#### Actions
# No-Code Actions

View File

@@ -4,7 +4,7 @@ export const meta = {
"Actions in Formbricks enable targeted survey displays during specific user journey moments. Enhance user segmentation by tracking actions for granular surveying.",
};
[Actions]()
#### Actions
# What are actions and why are they useful?

View File

@@ -9,7 +9,7 @@ export const meta = {
"Generate, store, and delete personal API keys for secure Formbricks access. Ensure safekeeping to prevent unauthorized account control.",
};
[API]()
#### API
# API Key Setup

View File

@@ -4,7 +4,7 @@ export const meta = {
"Explore Formbricks' APIs: Public Client API for client-side tasks, and User API for account management with secure API Key authentication.",
};
[API]()
#### API
# API Overview

View File

@@ -6,7 +6,7 @@ export const meta = {
"Explore the Formbricks Public Client API for client-side tasks and integration into your website.",
};
[Client API]()
#### Client API
# Responses API

View File

@@ -3,7 +3,7 @@ export const meta = {
description: "Learn how to use the Formbricks Webhook API.",
};
[Webhook API]()
#### Webhook API
# Webhook API

View File

@@ -4,7 +4,7 @@ export const meta = {
"Set attributes in code using setAttribute function. Enhance user segmentation, target surveys effectively, and gather valuable insights for better decisions. All open-source.",
};
[Attributes]()
#### Attributes
# Setting attributes with code

View File

@@ -4,7 +4,7 @@ export const meta = {
"Identify users with Formbricks by setting User ID, email, and custom attributes. Enhance survey targeting and recontacting while maintaining user privacy.",
};
[Attributes]()
#### Attributes
# Identifying Users

View File

@@ -4,7 +4,7 @@ export const meta = {
"How to use attributes for user segmentation, enhancing survey targeting & results. Improve feedback quality and make data-driven decisions.",
};
[Attributes]()
#### Attributes
# What are attributes and why are they useful?

View File

@@ -15,7 +15,7 @@ export const meta = {
description: "To know how to decrease churn, you have to understand it. Use a micro-survey.",
};
[Best Practices]()
#### Best Practices
# Learn from Churn

View File

@@ -15,7 +15,7 @@ export const meta = {
description: "Docs Feedback allows you to measure how clear your documentation is.",
};
[Best Practices]()
#### Best Practices
# Docs Feedback

View File

@@ -14,7 +14,7 @@ export const meta = {
description: "Follow up with users who used a specific feature. Gather feedback and improve your product.",
};
[Best Practices]()
#### Best Practices
# Feature Chaser

View File

@@ -16,7 +16,7 @@ export const meta = {
description: "The Feedback Box gives your users a direct channel to share their feedback and feel heard.",
};
[Best Practices]()
#### Best Practices
# Feedback Box

View File

@@ -14,7 +14,7 @@ export const meta = {
description: "Understand how to improve the trial conversions to get more paying customers.",
};
[Best Practices]()
#### Best Practices
# Improve Trial Conversion

View File

@@ -17,7 +17,7 @@ export const meta = {
description: "Invite only power users to schedule an interview with your product team.",
};
[Best Practices]()
#### Best Practices
# In-app Interview Prompt

View File

@@ -14,7 +14,7 @@ export const meta = {
description: "The Product-Market Fit survey helps you measure, well, Product-Market Fit (PMF).",
};
[Best Practices]()
#### Best Practices
# Product-Market Fit Survey

View File

@@ -7,7 +7,7 @@ export const meta = {
description: "To test in-app surveys, trigger actions and set attributes, you can use the Demo App.",
};
[Contributing]()
#### Contributing
# Demo App

View File

@@ -3,7 +3,7 @@ export const meta = {
description: "How to contribute to Formbricks",
};
[Contributing]()
#### Contributing
# Contribution Guide

View File

@@ -3,7 +3,7 @@ export const meta = {
description: "Setup a development environment for Formbricks.",
};
[Contributing]()
#### Contributing
# Setup Dev Environment

View File

@@ -10,7 +10,7 @@ export const meta = {
"Formbricks is a complex application in constant development. Sometimes, things don't go as planned. Here are some tips to help you troubleshoot.",
};
[Contributing]()
#### Contributing
# Troubleshooting

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -18,7 +18,7 @@ export const meta = {
description: "Get your first in app survey response in 10 minutes.",
};
[Getting Started](#)
#### Getting Started
# Quickstart

View File

@@ -19,7 +19,7 @@ export const meta = {
description: "Wire up Formbricks with Zapier and 5000+ other apps",
};
[Integrations]()
#### Integrations
# Zapier Setup

View File

@@ -4,7 +4,7 @@ export const meta = {
"Master in-product micro-surveys with Formbricks: user-friendly form builder, precise targeting, seamless integration, and insightful analytics for SaaS products.",
};
[Introduction](#)
#### Introduction
# How Formbricks works

View File

@@ -4,7 +4,7 @@ import { HeroPattern } from "@/components/docs/HeroPattern";
import { Button } from "@/components/docs/Button";
export const metadata = {
title: "Formbricks Experience Management for B2B SaaS",
title: "Formbricks Open Source Experience Management",
description:
"Natively embed qualitative user research into your B2B SaaS. Leverage Best Practices for user discovery to increase Product-Market Fit",
};
@@ -16,7 +16,7 @@ export const sections = [
<HeroPattern />
# Formbricks Experience Management for B2B SaaS
# Formbricks Open Source Experience Management
Welcome to Formbricks, your go-to solution for in-product micro-surveys that will supercharge your product experience! 🚀 {{ className: 'lead' }}

View File

@@ -4,7 +4,7 @@ export const meta = {
"The ultimate survey solution for SaaS, offering in-depth micro-surveys, precise targeting, and seamless integrations. Discover the difference today!",
};
[Introduction](#)
#### Introduction
# Why is Formbricks better?

View File

@@ -3,12 +3,12 @@ import glob from "fast-glob";
import { Providers } from "@/app/providers";
import { Layout } from "@/components/docs/Layout";
import { type Metadata } from "next";
import { type Section } from "@/components/docs/SectionProvider";
import { type Metadata } from "next";
export const metadata: Metadata = {
title: {
template: "Formbricks Experience Management for B2B SaaS",
template: "Formbricks Open Source Experience Management",
default: "Formbricks Docs",
},
};

View File

@@ -7,7 +7,7 @@ export const meta = {
description: "Prefill data in your surveys to make it easier for your users to provide feedback.",
};
[Link Surveys]()
#### Link Surveys
# Data Prefilling in Link Surveys

View File

@@ -8,7 +8,7 @@ export const meta = {
"Identify users in link surveys via URL parameter. Connect responses to existing users in Formbricks.",
};
[Link Surveys]()
#### Link Surveys
# User Identification in Link Surveys

View File

@@ -4,7 +4,7 @@ export const meta = {
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
};
[Self-hosting]()
#### Self-Hosting
# Self Hosting Formbricks

View File

@@ -4,7 +4,7 @@ export const meta = {
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
};
[Self-hosting]()
#### Self-Hosting
# Running Formbricks with Docker

View File

@@ -4,7 +4,7 @@ export const meta = {
"Build the Formbricks Image right from the open-sourced codebase and make changes however needed. Deploy it then with the freedom of customizing even the compile-time environment variables.",
};
[Self-hosting]()
#### Self-Hosting
# Building and Running Formbricks from Source

View File

@@ -4,7 +4,7 @@ export const meta = {
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
};
[Self-hosting]()
#### Self-Hosting
# Deploying Formbricks to Production

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -6,9 +6,9 @@ export default function NotFound() {
<>
<HeroPattern />
<div className="mx-auto flex h-full max-w-xl flex-col items-center justify-center py-16 text-center">
<p className="text-sm font-semibold text-zinc-900 dark:text-white">404</p>
<h1 className="mt-2 text-2xl font-bold text-zinc-900 dark:text-white">Page not found</h1>
<p className="mt-2 text-base text-zinc-600 dark:text-zinc-400">
<p className="text-sm font-semibold text-slate-900 dark:text-white">404</p>
<h1 className="mt-2 text-2xl font-bold text-slate-900 dark:text-white">Page not found</h1>
<p className="mt-2 text-base text-slate-600 dark:text-slate-400">
Sorry, we couldnt find the page youre looking for.
</p>
<Button href="/" arrow="right" className="mt-8">

View File

@@ -73,8 +73,8 @@ const bestPractices: Array<BestPractice> = [
function BestPracticeIcon({ icon: Icon }: { icon: BestPractice["icon"] }) {
return (
<div className="dark:bg-white/7.5 dark:ring-white/15 flex h-7 w-7 items-center justify-center rounded-full bg-zinc-900/5 ring-1 ring-zinc-900/25 backdrop-blur-[2px] transition duration-300 group-hover:bg-white/50 group-hover:ring-zinc-900/25 dark:group-hover:bg-emerald-300/10 dark:group-hover:ring-emerald-400">
<Icon className="h-5 w-5 fill-zinc-700/10 stroke-zinc-700 transition-colors duration-300 group-hover:stroke-zinc-900 dark:fill-white/10 dark:stroke-zinc-400 dark:group-hover:fill-emerald-300/10 dark:group-hover:stroke-emerald-400" />
<div className="dark:bg-white/7.5 dark:ring-white/15 flex h-7 w-7 items-center justify-center rounded-full bg-slate-900/5 ring-1 ring-slate-900/25 backdrop-blur-[2px] transition duration-300 group-hover:bg-white/50 group-hover:ring-slate-900/25 dark:group-hover:bg-emerald-300/10 dark:group-hover:ring-emerald-400">
<Icon className="h-5 w-5 fill-slate-700/10 stroke-slate-700 transition-colors duration-300 group-hover:stroke-slate-900 dark:fill-white/10 dark:stroke-slate-400 dark:group-hover:fill-emerald-300/10 dark:group-hover:stroke-emerald-400" />
</div>
);
}
@@ -134,18 +134,18 @@ function BestPractice({ resource }: { resource: BestPractice }) {
<div
key={resource.href}
onMouseMove={onMouseMove}
className="dark:bg-white/2.5 group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:hover:shadow-black/5">
className="dark:bg-white/2.5 group relative flex rounded-2xl bg-slate-50 transition-shadow hover:shadow-md hover:shadow-slate-900/5 dark:hover:shadow-black/5">
<BestPracticePattern {...resource.pattern} mouseX={mouseX} mouseY={mouseY} />
<div className="ring-zinc-900/7.5 absolute inset-0 rounded-2xl ring-1 ring-inset group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" />
<div className="ring-slate-900/7.5 absolute inset-0 rounded-2xl ring-1 ring-inset group-hover:ring-slate-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" />
<div className="relative rounded-2xl px-4 pb-4 pt-16">
<BestPracticeIcon icon={resource.icon} />
<h3 className="mt-4 text-sm font-semibold leading-7 text-zinc-900 dark:text-white">
<h3 className="mt-4 text-sm font-semibold leading-7 text-slate-900 dark:text-white">
<Link href={resource.href}>
<span className="absolute inset-0 rounded-2xl" />
{resource.name}
</Link>
</h3>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">{resource.description}</p>
<p className="mt-1 text-sm text-slate-600 dark:text-slate-400">{resource.description}</p>
</div>
</div>
);
@@ -157,7 +157,7 @@ export function BestPractices() {
<Heading level={2} id="resources">
Best Practices
</Heading>
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-slate-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
{bestPractices.map((resource) => (
<BestPractice key={resource.href} resource={resource} />
))}

View File

@@ -16,13 +16,13 @@ function ArrowIcon(props: React.ComponentPropsWithoutRef<"svg">) {
const variantStyles = {
primary:
"rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-teal-400/10 dark:text-teal-400 dark:ring-1 dark:ring-inset dark:ring-teal-400/20 dark:hover:bg-teal-400/10 dark:hover:text-teal-300 dark:hover:ring-teal-300",
"rounded-full bg-slate-900 py-1 px-3 text-white hover:bg-slate-700 dark:bg-teal-400/10 dark:text-teal-400 dark:ring-1 dark:ring-inset dark:ring-teal-400/20 dark:hover:bg-teal-400/10 dark:hover:text-teal-300 dark:hover:ring-teal-300",
secondary:
"rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300",
"rounded-full bg-slate-100 py-1 px-3 text-slate-900 hover:bg-slate-200 dark:bg-slate-800/40 dark:text-slate-400 dark:ring-1 dark:ring-inset dark:ring-slate-800 dark:hover:bg-slate-800 dark:hover:text-slate-300",
filled:
"rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-teal-500 dark:text-white dark:hover:bg-teal-400",
"rounded-full bg-slate-900 py-1 px-3 text-white hover:bg-slate-700 dark:bg-teal-500 dark:text-white dark:hover:bg-teal-400",
outline:
"rounded-full py-1 px-3 text-zinc-700 ring-1 ring-inset ring-zinc-900/10 hover:bg-zinc-900/2.5 hover:text-zinc-900 dark:text-zinc-400 dark:ring-white/10 dark:hover:bg-white/5 dark:hover:text-white",
"rounded-full py-1 px-3 text-slate-700 ring-1 ring-inset ring-slate-900/10 hover:bg-slate-900/2.5 hover:text-slate-900 dark:text-slate-400 dark:ring-white/10 dark:hover:bg-white/5 dark:hover:text-white",
text: "text-teal-500 hover:text-teal-700 dark:text-teal-400 dark:hover:text-teal-600",
};

View File

@@ -74,10 +74,10 @@ function CopyButton({ code }: { code: string }) {
<span
aria-hidden={copied}
className={clsx(
"pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300",
"pointer-events-none flex items-center gap-0.5 text-slate-400 transition duration-300",
copied && "-translate-y-1.5 opacity-0"
)}>
<ClipboardIcon className="h-5 w-5 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400" />
<ClipboardIcon className="h-5 w-5 fill-slate-500/20 stroke-slate-500 transition-colors group-hover/button:stroke-slate-400" />
Copy
</span>
<span
@@ -98,14 +98,14 @@ function CodePanelHeader({ tag, label }: { tag?: string; label?: string }) {
}
return (
<div className="border-b-white/7.5 bg-white/2.5 dark:bg-white/1 flex h-9 items-center gap-2 border-y border-t-transparent bg-zinc-900 px-4 dark:border-b-white/5">
<div className="border-b-white/7.5 bg-white/2.5 dark:bg-white/1 flex h-9 items-center gap-2 border-y border-t-transparent bg-slate-900 px-4 dark:border-b-white/5">
{tag && (
<div className="dark flex">
<Tag variant="small">{tag}</Tag>
</div>
)}
{tag && label && <span className="h-0.5 w-0.5 rounded-full bg-zinc-500" />}
{label && <span className="font-mono text-xs text-zinc-400">{label}</span>}
{tag && label && <span className="h-0.5 w-0.5 rounded-full bg-slate-500" />}
{label && <span className="font-mono text-xs text-slate-400">{label}</span>}
</div>
);
}
@@ -160,7 +160,7 @@ function CodeGroupHeader({
}
return (
<div className="flex min-h-[calc(theme(spacing.12)+1px)] flex-wrap items-start gap-x-4 border-b border-zinc-700 bg-zinc-800 px-4 dark:border-zinc-800 dark:bg-transparent">
<div className="flex min-h-[calc(theme(spacing.12)+1px)] flex-wrap items-start gap-x-4 border-b border-slate-700 bg-slate-800 px-4 dark:border-slate-800 dark:bg-transparent">
{title && <h3 className="mr-auto pt-3 text-xs font-semibold text-white">{title}</h3>}
{hasTabs && (
<Tab.List className="-mb-px flex gap-4 text-xs font-medium">
@@ -170,7 +170,7 @@ function CodeGroupHeader({
"ui-not-focus-visible:outline-none border-b py-3 transition",
childIndex === selectedIndex
? "border-emerald-500 text-emerald-400"
: "border-transparent text-zinc-400 hover:text-zinc-300"
: "border-transparent text-slate-400 hover:text-slate-300"
)}>
{getPanelTitle(isValidElement(child) ? child.props : {})}
</Tab>
@@ -281,7 +281,7 @@ export function CodeGroup({
let hasTabs = Children.count(children) >= 1;
let containerClassName =
"not-prose my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10";
"not-prose my-6 overflow-hidden rounded-2xl bg-slate-900 shadow-md dark:ring-1 dark:ring-white/10";
let header = (
<CodeGroupHeader title={title} selectedIndex={tabGroupProps.selectedIndex}>
{children}

View File

@@ -24,7 +24,7 @@ function FeedbackButton(props: Omit<React.ComponentPropsWithoutRef<"button">, "t
return (
<button
type="submit"
className="hover:bg-zinc-900/2.5 px-3 text-sm font-medium text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-white/5 dark:hover:text-white"
className="hover:bg-slate-900/2.5 px-3 text-sm font-medium text-slate-600 transition hover:text-slate-900 dark:text-slate-400 dark:hover:bg-white/5 dark:hover:text-white"
{...props}
/>
);
@@ -39,10 +39,10 @@ const FeedbackForm = forwardRef<
ref={ref}
onSubmit={onSubmit}
className="absolute inset-0 flex items-center justify-center gap-6 md:justify-start">
<p className="text-sm text-zinc-600 dark:text-zinc-400">Was this page helpful?</p>
<div className="group grid h-8 grid-cols-[1fr,1px,1fr] overflow-hidden rounded-full border border-zinc-900/10 dark:border-white/10">
<p className="text-sm text-slate-600 dark:text-slate-400">Was this page helpful?</p>
<div className="group grid h-8 grid-cols-[1fr,1px,1fr] overflow-hidden rounded-full border border-slate-900/10 dark:border-white/10">
<FeedbackButton data-response="Yes 👍">Yes 👍</FeedbackButton>
<div className="bg-zinc-900/10 dark:bg-white/10" />
<div className="bg-slate-900/10 dark:bg-white/10" />
<FeedbackButton data-response="No 👎">No 👎</FeedbackButton>
</div>
</form>

View File

@@ -28,7 +28,7 @@ function PageLink({
href={page.href}
tabIndex={-1}
aria-hidden="true"
className="text-base font-semibold text-zinc-900 transition hover:text-zinc-600 dark:text-white dark:hover:text-zinc-300">
className="text-base font-semibold text-slate-900 transition hover:text-slate-600 dark:text-white dark:hover:text-slate-300">
{page.title}
</Link>
</>
@@ -107,15 +107,15 @@ function SocialLink({
return (
<Link href={href} className="group">
<span className="sr-only">{children}</span>
<Icon className="h-5 w-5 fill-zinc-700 transition group-hover:fill-zinc-900 dark:group-hover:fill-zinc-500" />
<Icon className="h-5 w-5 fill-slate-700 transition group-hover:fill-slate-900 dark:group-hover:fill-slate-500" />
</Link>
);
}
function SmallPrint() {
return (
<div className="flex flex-col items-center justify-between gap-5 border-t border-zinc-900/5 pt-8 dark:border-white/5 sm:flex-row">
<p className="text-xs text-zinc-600 dark:text-zinc-400">
<div className="flex flex-col items-center justify-between gap-5 border-t border-slate-900/5 pt-8 dark:border-white/5 sm:flex-row">
<p className="text-xs text-slate-600 dark:text-slate-400">
Formbricks GmbH &copy; 2023. All rights reserved.
</p>
<div className="flex gap-4">

View File

@@ -31,11 +31,11 @@ export function GettingStarted() {
<Heading level={2} id="getting-started">
Quick Resources
</Heading>
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-slate-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
{gettingStarted.map((guide) => (
<div key={guide.href}>
<h3 className="text-sm font-semibold text-zinc-900 dark:text-white">{guide.name}</h3>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">{guide.description}</p>
<h3 className="text-sm font-semibold text-slate-900 dark:text-white">{guide.name}</h3>
<p className="mt-1 text-sm text-slate-600 dark:text-slate-400">{guide.description}</p>
<p className="mt-4">
<Button href={guide.href} variant="text" arrow="right">
Read more

View File

@@ -16,7 +16,7 @@ function TopLevelNavItem({ href, children }: { href: string; children: React.Rea
<li>
<Link
href={href}
className="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white">
className="text-sm leading-5 text-slate-600 transition hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
{children}
</Link>
</li>
@@ -42,8 +42,8 @@ export const Header = forwardRef<React.ElementRef<"div">, { className?: string }
"fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:left-72 lg:z-30 lg:px-8 xl:left-80",
!isInsideMobileNavigation && "backdrop-blur-sm dark:backdrop-blur lg:left-72 xl:left-80",
isInsideMobileNavigation
? "bg-white dark:bg-zinc-900"
: "bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]"
? "bg-white dark:bg-slate-900"
: "bg-white/[var(--bg-opacity-light)] dark:bg-slate-900/[var(--bg-opacity-dark)]"
)}
style={
{
@@ -54,7 +54,7 @@ export const Header = forwardRef<React.ElementRef<"div">, { className?: string }
<div
className={clsx(
"absolute inset-x-0 top-full h-px transition",
(isInsideMobileNavigation || !mobileNavIsOpen) && "bg-zinc-900/7.5 dark:bg-white/7.5"
(isInsideMobileNavigation || !mobileNavIsOpen) && "bg-slate-900/7.5 dark:bg-white/7.5"
)}
/>
<Search />
@@ -74,7 +74,7 @@ export const Header = forwardRef<React.ElementRef<"div">, { className?: string }
<TopLevelNavItem href="https://formbricks.com/discord">Join our Discord</TopLevelNavItem>
</ul>
</nav>
<div className="md:dark:bg-white/15 hidden md:block md:h-5 md:w-px md:bg-zinc-900/10" />
<div className="md:dark:bg-white/15 hidden md:block md:h-5 md:w-px md:bg-slate-900/10" />
<div className="flex gap-4">
<MobileSearch />
<ThemeToggle />

View File

@@ -25,8 +25,8 @@ function Eyebrow({ tag, label }: { tag?: string; label?: string }) {
return (
<div className="flex items-center gap-x-3">
{tag && <Tag>{tag}</Tag>}
{tag && label && <span className="h-0.5 w-0.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />}
{label && <span className="font-mono text-xs text-zinc-400">{label}</span>}
{tag && label && <span className="h-0.5 w-0.5 rounded-full bg-slate-300 dark:bg-slate-600" />}
{label && <span className="font-mono text-xs text-slate-400">{label}</span>}
</div>
);
}
@@ -36,8 +36,8 @@ function Anchor({ id, inView, children }: { id: string; inView: boolean; childre
<Link href={`#${id}`} className="group text-inherit no-underline hover:text-inherit">
{inView && (
<div className="absolute ml-[calc(-1*var(--width))] mt-1 hidden w-[var(--width)] opacity-0 transition [--width:calc(2.625rem+0.5px+50%-min(50%,calc(theme(maxWidth.lg)+theme(spacing.8))))] group-hover:opacity-100 group-focus:opacity-100 md:block lg:z-50 2xl:[--width:theme(spacing.10)]">
<div className="group/anchor block h-5 w-5 rounded-lg bg-zinc-50 ring-1 ring-inset ring-zinc-300 transition hover:ring-zinc-500 dark:bg-zinc-800 dark:ring-zinc-700 dark:hover:bg-zinc-700 dark:hover:ring-zinc-600">
<AnchorIcon className="h-5 w-5 stroke-zinc-500 transition dark:stroke-zinc-400 dark:group-hover/anchor:stroke-white" />
<div className="group/anchor block h-5 w-5 rounded-lg bg-slate-50 ring-1 ring-inset ring-slate-300 transition hover:ring-slate-500 dark:bg-slate-800 dark:ring-slate-700 dark:hover:bg-slate-700 dark:hover:ring-slate-600">
<AnchorIcon className="h-5 w-5 stroke-slate-500 transition dark:stroke-slate-400 dark:group-hover/anchor:stroke-white" />
</div>
</div>
)}

View File

@@ -25,7 +25,7 @@ export function Layout({
<motion.header
layoutScroll
className="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex">
<div className="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pb-8 lg:pt-4 lg:dark:border-white/10 xl:w-80">
<div className="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-slate-900/10 lg:px-6 lg:pb-8 lg:pt-4 lg:dark:border-white/10 xl:w-80">
<div className="hidden lg:flex">
<Link href="/" aria-label="Home">
<FooterLogo className="h-8" />

View File

@@ -37,12 +37,12 @@ const libraries = [
export function Libraries() {
return (
<div className="my-16 xl:max-w-none">
<div className="not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-zinc-900/5 dark:border-white/5 sm:grid-cols-2 xl:max-w-none xl:grid-cols-3">
<div className="not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-slate-900/5 dark:border-white/5 sm:grid-cols-2 xl:max-w-none xl:grid-cols-3">
{libraries.map((library) => (
<div key={library.name} className="flex flex-row-reverse gap-6">
<div className="flex-auto">
<h3 className="text-sm font-semibold text-zinc-900 dark:text-white">{library.name}</h3>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">{library.description}</p>
<h3 className="text-sm font-semibold text-slate-900 dark:text-white">{library.name}</h3>
<p className="mt-1 text-sm text-slate-600 dark:text-slate-400">{library.description}</p>
<p className="mt-4">
<Button href={library.href} variant="text" arrow="right">
Read more

View File

@@ -65,7 +65,7 @@ function MobileNavigationDialog({ isOpen, close }: { isOpen: boolean; close: ()
leave="duration-200 ease-in"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<div className="fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-sm dark:bg-black/40" />
<div className="fixed inset-0 top-14 bg-slate-400/20 backdrop-blur-sm dark:bg-black/40" />
</Transition.Child>
<Dialog.Panel>
@@ -90,7 +90,7 @@ function MobileNavigationDialog({ isOpen, close }: { isOpen: boolean; close: ()
leaveTo="-translate-x-full">
<motion.div
layoutScroll
className="ring-zinc-900/7.5 fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 dark:bg-zinc-900 dark:ring-zinc-800 min-[416px]:max-w-sm sm:px-6 sm:pb-10">
className="ring-slate-900/7.5 fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white px-4 pb-4 pt-6 shadow-lg shadow-slate-900/10 ring-1 dark:bg-slate-900 dark:ring-slate-800 min-[416px]:max-w-sm sm:px-6 sm:pb-10">
<Navigation />
</motion.div>
</Transition.Child>
@@ -125,10 +125,10 @@ export function MobileNavigation() {
<IsInsideMobileNavigationContext.Provider value={true}>
<button
type="button"
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5"
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-slate-900/5 dark:hover:bg-white/5"
aria-label="Toggle navigation"
onClick={toggle}>
<ToggleIcon className="w-2.5 stroke-zinc-900 dark:stroke-white" />
<ToggleIcon className="w-2.5 stroke-slate-900 dark:stroke-white" />
</button>
{!isInsideMobileNavigation && (
<Suspense fallback={null}>

View File

@@ -30,7 +30,7 @@ function TopLevelNavItem({ href, children }: { href: string; children: React.Rea
<li className="md:hidden">
<Link
href={href}
className="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white">
className="block py-1 text-sm text-slate-600 transition hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
{children}
</Link>
</li>
@@ -58,12 +58,12 @@ function NavLink({
"flex justify-between gap-2 py-1 pr-3 text-sm transition",
isAnchorLink ? "pl-7" : "pl-4",
active
? "text-zinc-900 dark:text-white"
: "text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white"
? "text-slate-900 dark:text-white"
: "text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
)}>
<span className="truncate">{children}</span>
{tag && (
<Tag variant="small" color="zinc">
<Tag variant="small" color="slate">
{tag}
</Tag>
)}
@@ -94,7 +94,7 @@ function VisibleSectionHighlight({ group, pathname }: { group: NavGroup; pathnam
initial={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 0.2 } }}
exit={{ opacity: 0 }}
className="bg-zinc-800/2.5 dark:bg-white/2.5 absolute inset-x-0 top-0 will-change-transform"
className="bg-slate-800/2.5 dark:bg-white/2.5 absolute inset-x-0 top-0 will-change-transform"
style={{ borderRadius: 8, height, top }}
/>
);
@@ -131,14 +131,14 @@ function NavigationGroup({ group, className }: { group: NavGroup; className?: st
return (
<li className={clsx("relative mt-6", className)}>
<motion.h2 layout="position" className="text-xs font-semibold text-zinc-900 dark:text-white">
<motion.h2 layout="position" className="text-xs font-semibold text-slate-900 dark:text-white">
{group.title}
</motion.h2>
<div className="relative mt-3 pl-2">
<AnimatePresence initial={!isInsideMobileNavigation}>
{isActiveGroup && <VisibleSectionHighlight group={group} pathname={pathname || "/docs"} />}
</AnimatePresence>
<motion.div layout className="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5" />
<motion.div layout className="absolute inset-y-0 left-2 w-px bg-slate-900/10 dark:bg-white/5" />
<AnimatePresence initial={false}>
{isActiveGroup && <ActivePageMarker group={group} pathname={pathname || "/docs"} />}
</AnimatePresence>

View File

@@ -152,8 +152,8 @@ function SearchResult({
return (
<li
className={clsx(
"group block cursor-default px-4 py-3 aria-selected:bg-zinc-50 dark:aria-selected:bg-zinc-800/50",
resultIndex > 0 && "border-t border-zinc-100 dark:border-zinc-800"
"group block cursor-default px-4 py-3 aria-selected:bg-slate-50 dark:aria-selected:bg-slate-800/50",
resultIndex > 0 && "border-t border-slate-100 dark:border-slate-800"
)}
aria-labelledby={`${id}-hierarchy ${id}-title`}
{...autocomplete.getItemProps({
@@ -163,20 +163,20 @@ function SearchResult({
<div
id={`${id}-title`}
aria-hidden="true"
className="text-sm font-medium text-zinc-900 group-aria-selected:text-emerald-500 dark:text-white">
className="text-sm font-medium text-slate-900 group-aria-selected:text-emerald-500 dark:text-white">
<HighlightQuery text={result.title} query={query} />
</div>
{hierarchy.length > 0 && (
<div
id={`${id}-hierarchy`}
aria-hidden="true"
className="text-2xs mt-1 truncate whitespace-nowrap text-zinc-500">
className="text-2xs mt-1 truncate whitespace-nowrap text-slate-500">
{hierarchy.map((item, itemIndex, items) => (
<Fragment key={itemIndex}>
<HighlightQuery text={item} query={query} />
<span
className={
itemIndex === items.length - 1 ? "sr-only" : "mx-2 text-zinc-300 dark:text-zinc-700"
itemIndex === items.length - 1 ? "sr-only" : "mx-2 text-slate-300 dark:text-slate-700"
}>
/
</span>
@@ -200,10 +200,10 @@ function SearchResults({
if (collection.items.length === 0) {
return (
<div className="p-6 text-center">
<NoResultsIcon className="mx-auto h-5 w-5 stroke-zinc-900 dark:stroke-zinc-600" />
<p className="mt-2 text-xs text-zinc-700 dark:text-zinc-400">
<NoResultsIcon className="mx-auto h-5 w-5 stroke-slate-900 dark:stroke-slate-600" />
<p className="mt-2 text-xs text-slate-700 dark:text-slate-400">
Nothing found for{" "}
<strong className="break-words font-semibold text-zinc-900 dark:text-white">
<strong className="break-words font-semibold text-slate-900 dark:text-white">
&lsquo;{query}&rsquo;
</strong>
. Please try again.
@@ -240,11 +240,11 @@ const SearchInput = forwardRef<
return (
<div className="group relative flex h-12">
<SearchIcon className="pointer-events-none absolute left-3 top-0 h-full w-5 stroke-zinc-500" />
<SearchIcon className="pointer-events-none absolute left-3 top-0 h-full w-5 stroke-slate-500" />
<input
ref={inputRef}
className={clsx(
"flex-auto appearance-none bg-transparent pl-10 text-zinc-900 outline-none placeholder:text-zinc-500 focus:w-full focus:flex-none dark:text-white sm:text-sm [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden [&::-webkit-search-results-button]:hidden [&::-webkit-search-results-decoration]:hidden",
"flex-auto appearance-none bg-transparent pl-10 text-slate-900 outline-none placeholder:text-slate-500 focus:w-full focus:flex-none dark:text-white sm:text-sm [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden [&::-webkit-search-results-button]:hidden [&::-webkit-search-results-decoration]:hidden",
autocompleteState.status === "stalled" ? "pr-11" : "pr-4"
)}
{...inputProps}
@@ -264,7 +264,7 @@ const SearchInput = forwardRef<
/>
{autocompleteState.status === "stalled" && (
<div className="absolute inset-y-0 right-3 flex items-center">
<LoadingIcon className="h-5 w-5 animate-spin stroke-zinc-200 text-zinc-900 dark:stroke-zinc-800 dark:text-emerald-400" />
<LoadingIcon className="h-5 w-5 animate-spin stroke-slate-200 text-slate-900 dark:stroke-slate-800 dark:text-emerald-400" />
</div>
)}
</div>
@@ -325,7 +325,7 @@ function SearchDialog({
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<div className="fixed inset-0 bg-zinc-400/25 backdrop-blur-sm dark:bg-black/40" />
<div className="fixed inset-0 bg-slate-400/25 backdrop-blur-sm dark:bg-black/40" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto px-4 py-4 sm:px-6 sm:py-20 md:py-32 lg:px-8 lg:py-[15vh]">
@@ -337,7 +337,7 @@ function SearchDialog({
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95">
<Dialog.Panel className="ring-zinc-900/7.5 mx-auto transform-gpu overflow-hidden rounded-lg bg-zinc-50 shadow-xl ring-1 dark:bg-zinc-900 dark:ring-zinc-800 sm:max-w-xl">
<Dialog.Panel className="ring-slate-900/7.5 mx-auto transform-gpu overflow-hidden rounded-lg bg-slate-50 shadow-xl ring-1 dark:bg-slate-900 dark:ring-slate-800 sm:max-w-xl">
<div {...autocomplete.getRootProps({})}>
<form
ref={formRef}
@@ -352,7 +352,7 @@ function SearchDialog({
/>
<div
ref={panelRef}
className="dark:bg-white/2.5 border-t border-zinc-200 bg-white empty:hidden dark:border-zinc-100/5"
className="dark:bg-white/2.5 border-t border-slate-200 bg-white empty:hidden dark:border-slate-100/5"
{...autocomplete.getPanelProps({})}>
{autocompleteState.isOpen && (
<SearchResults
@@ -410,11 +410,11 @@ export function Search() {
<div className="hidden lg:block lg:max-w-md lg:flex-auto">
<button
type="button"
className="ui-not-focus-visible:outline-none hidden h-8 w-full items-center gap-2 rounded-full bg-white pl-2 pr-3 text-sm text-zinc-500 ring-1 ring-zinc-900/10 transition hover:ring-zinc-900/20 dark:bg-white/5 dark:text-zinc-400 dark:ring-inset dark:ring-white/10 dark:hover:ring-white/20 lg:flex"
className="ui-not-focus-visible:outline-none hidden h-8 w-full items-center gap-2 rounded-full bg-white pl-2 pr-3 text-sm text-slate-500 ring-1 ring-slate-900/10 transition hover:ring-slate-900/20 dark:bg-white/5 dark:text-slate-400 dark:ring-inset dark:ring-white/10 dark:hover:ring-white/20 lg:flex"
{...buttonProps}>
<SearchIcon className="h-5 w-5 stroke-current" />
Find something...
<kbd className="text-2xs ml-auto text-zinc-400 dark:text-zinc-500">
<kbd className="text-2xs ml-auto text-slate-400 dark:text-slate-500">
<kbd className="font-sans">{modifierKey}</kbd>
<kbd className="font-sans">K</kbd>
</kbd>
@@ -433,10 +433,10 @@ export function MobileSearch() {
<div className="contents lg:hidden">
<button
type="button"
className="ui-not-focus-visible:outline-none flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5 lg:hidden"
className="ui-not-focus-visible:outline-none flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-slate-900/5 dark:hover:bg-white/5 lg:hidden"
aria-label="Find something..."
{...buttonProps}>
<SearchIcon className="h-5 w-5 stroke-zinc-900 dark:stroke-white" />
<SearchIcon className="h-5 w-5 stroke-slate-900 dark:stroke-white" />
</button>
<Suspense fallback={null}>
<SearchDialog className="lg:hidden" {...dialogProps} />

View File

@@ -59,7 +59,7 @@ function useVisibleSections(sectionStore: StoreApi<SectionState>) {
useEffect(() => {
function checkVisibleSections() {
let { innerHeight, scrollY } = window;
let newVisibleSections = [];
let newVisibleSections: string[] = []; // Explicitly type the array here
for (let sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
let { id, headingRef, offsetRem = 0 } = sections[sectionIndex];

View File

@@ -26,10 +26,10 @@ const colorStyles = {
medium:
"ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400",
},
zinc: {
small: "text-zinc-400 dark:text-zinc-500",
slate: {
small: "text-slate-400 dark:text-slate-500",
medium:
"ring-zinc-200 bg-zinc-50 text-zinc-500 dark:ring-zinc-500/20 dark:bg-zinc-400/10 dark:text-zinc-400",
"ring-slate-200 bg-slate-50 text-slate-500 dark:ring-slate-500/20 dark:bg-slate-400/10 dark:text-slate-400",
},
};

View File

@@ -35,10 +35,10 @@ export function ThemeToggle() {
return (
<button
type="button"
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5"
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-slate-900/5 dark:hover:bg-white/5"
aria-label={mounted ? `Switch to ${otherTheme} theme` : "Toggle theme"}
onClick={() => setTheme(otherTheme)}>
<SunIcon className="h-5 w-5 stroke-zinc-900 dark:hidden" />
<SunIcon className="h-5 w-5 stroke-slate-900 dark:hidden" />
<MoonIcon className="hidden h-5 w-5 stroke-white dark:block" />
</button>
);

View File

@@ -70,7 +70,7 @@ export function Properties({ children }: { children: React.ReactNode }) {
<div className="my-6">
<ul
role="list"
className="m-0 max-w-[calc(theme(maxWidth.lg)-theme(spacing.8))] list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/5">
className="m-0 max-w-[calc(theme(maxWidth.lg)-theme(spacing.8))] list-none divide-y divide-slate-900/5 p-0 dark:divide-white/5">
{children}
</ul>
</div>
@@ -96,7 +96,7 @@ export function Property({
{type && (
<>
<dt className="sr-only">Type</dt>
<dd className="font-mono text-xs text-zinc-400 dark:text-zinc-500">{type}</dd>
<dd className="font-mono text-xs text-slate-400 dark:text-slate-500">{type}</dd>
</>
)}
<dt className="sr-only">Description</dt>

View File

@@ -1,3 +1,4 @@
import SlideInBanner from "@/components/shared/SlideInBanner";
import { useEffect } from "react";
import Footer from "./Footer";
import Header from "./Header";
@@ -60,6 +61,7 @@ export default function LayoutMdx({ meta, children }: Props) {
)}
<Prose className="">{children}</Prose>
</article>
<SlideInBanner delay={5000} scrollPercentage={10} UTMSource="learnArticle" />
</main>
<Footer />
</div>

View File

@@ -19,7 +19,7 @@ export default function MetaInformation({
section,
tags,
}: Props) {
const pageTitle = `${title} | Open-Source Experience Management, Privacy-first`;
const pageTitle = `${title}`;
return (
<Head>
<title>{pageTitle}</title>
@@ -27,7 +27,7 @@ export default function MetaInformation({
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={`https://${process.env.VERCEL_URL}/social-image.png`} />
<meta property="og:image:alt" content="Formbricks - Open Source Experience Management, Privacy-first" />
<meta property="og:image:alt" content="Open Source Experience Management, Privacy-first" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

View File

@@ -0,0 +1,57 @@
import LFGLuigi from "@/images/blog/lfg-luigi-200px.webp";
import { Button } from "@formbricks/ui";
import Image from "next/image";
import React, { useEffect, useState } from "react";
interface Props {
delay?: number; // in milliseconds, optional
scrollPercentage?: number; // optional
UTMSource: string; // required
}
const SlideInBanner: React.FC<Props> = ({ delay = 5000, scrollPercentage = 10, UTMSource }) => {
const [showBanner, setShowBanner] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowBanner(true);
}, delay);
const handleScroll = () => {
let currentScrollPercentage =
(window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
if (currentScrollPercentage > scrollPercentage) {
setShowBanner(true);
}
};
window.addEventListener("scroll", handleScroll);
return () => {
clearTimeout(timer);
window.removeEventListener("scroll", handleScroll);
};
}, [delay, scrollPercentage]);
return (
<div
className={`sticky bottom-4 grid grid-cols-7 rounded-lg bg-slate-700 bg-opacity-70 p-4 text-white backdrop-blur-sm transition-all duration-500 ease-out ${
showBanner ? "visible translate-y-0 opacity-100" : "invisible translate-y-full opacity-0"
}`}>
<div className="relative col-span-1 hidden lg:block">
<Image src={LFGLuigi} height={150} className="absolute -bottom-16 left-8" alt="LFG Luigi" />
</div>
<div className="col-span-7 flex items-center space-x-3 lg:col-span-6">
<p>
Did you know? Formbricks is the only open source Experience Management solution: free &
privacy-first!
</p>
<Button size="sm" href={`https://formbricks.com?utm_source=${UTMSource}`}>
Learn more
</Button>
</div>
</div>
);
};
export default SlideInBanner;

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -19,11 +19,11 @@ export function cleanHtml(str: string): string {
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html) {
function removeScripts(html: Element) {
let scripts = html.querySelectorAll("script");
for (let script of scripts) {
scripts.forEach((script) => {
script.remove();
}
});
}
/**
@@ -32,23 +32,33 @@ export function cleanHtml(str: string): string {
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
function isPossiblyDangerous(name, value) {
/**
* Check if the attribute is potentially dangerous
*/
function isPossiblyDangerous(name: string, value: string): boolean {
let val = value.replace(/\s+/g, "").toLowerCase();
if (["src", "href", "xlink:href"].includes(name)) {
if (val.includes("javascript:") || val.includes("data:")) return true;
if (
["src", "href", "xlink:href"].includes(name) &&
(val.includes("javascript:") || val.includes("data:"))
) {
return true;
}
if (name.startsWith("on")) return true;
if (name.startsWith("on")) {
return true;
}
return false;
}
/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem) {
function removeAttributes(elem: Element) {
// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;
for (let { name, value } of atts) {
for (let i = 0; i < atts.length; i++) {
let { name, value } = atts[i];
if (!isPossiblyDangerous(name, value)) continue;
elem.removeAttribute(name);
}
@@ -58,8 +68,12 @@ export function cleanHtml(str: string): string {
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
function clean(html) {
let nodes = html.children;
/**
* Clean the HTML nodes recursively
* @param {Element} html The HTML element
*/
function clean(html: Element) {
let nodes = Array.from(html.children);
for (let node of nodes) {
removeAttributes(node);
clean(node);

View File

@@ -1,4 +1,4 @@
export const handleFeedbackSubmit = async (YesNo, pageUrl) => {
export const handleFeedbackSubmit = async (YesNo: string, pageUrl: string | null) => {
const response_data = {
data: {
isHelpful: YesNo,
@@ -34,7 +34,7 @@ export const handleFeedbackSubmit = async (YesNo, pageUrl) => {
}
};
export const updateFeedback = async (freeText, responseId) => {
export const updateFeedback = async (freeText: string, responseId: string) => {
if (!responseId) {
console.error("No response ID available");
return;

View File

@@ -29,8 +29,6 @@
"@sindresorhus/slugify": "^2.2.1",
"@tailwindcss/typography": "^0.5.9",
"@types/node": "20.5.6",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@types/react-highlight-words": "^0.16.4",
"acorn": "^8.10.0",
"autoprefixer": "^10.4.15",
@@ -62,14 +60,12 @@
"shiki": "^0.14.3",
"simple-functional-loader": "^1.2.1",
"tailwindcss": "^3.3.3",
"typescript": "5.2.2",
"unist-util-filter": "^5.0.0",
"unist-util-visit": "^5.0.0",
"zustand": "^4.4.1"
},
"devDependencies": {
"@formbricks/tsconfig": "workspace:*",
"eslint-config-formbricks": "workspace:*",
"sharp": "^0.32.5"
"eslint-config-formbricks": "workspace:*"
}
}

View File

@@ -133,6 +133,11 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
description: "Webstudio is an open source alternative to Webflow",
href: "https://webstudio.is",
},
{
name: "Spark.NET",
description: "The .NET Web Framework for Makers. Build production ready, full-stack web applications fast without sweating the small stuff.",
href: "https://spark-framework.net",
},
],
});
}

View File

@@ -1,14 +1,13 @@
import HeroTitle from "@/components/shared/HeroTitle";
import Layout from "@/components/shared/Layout";
import Cal, { getCalApi } from "@calcom/embed-react";
import { useEffect } from "react";
import { CheckBadgeIcon } from "@heroicons/react/24/solid";
import { Button } from "@formbricks/ui";
import { useEffect } from "react";
const XMOffer = [
{
step: "1",
header: "Kick-off call (FREE)",
header: "Kick-off call",
description: "Share with our seasoned PMs which areas of customer experience need improvement.",
},
{
@@ -48,10 +47,10 @@ const ConciergePage = () => {
return (
<Layout
title="Community | Formbricks Open Source Forms & Surveys"
description="You're building open source forms and surveys? So are we! Get support for anything your building - or just say hi!">
title="Concierge | Formbricks Open Source Experience Management"
description="We help you get started! Get the worry-free setup with our guidance.">
<HeroTitle
headingPt1="XM"
headingPt1=""
headingTeal="Concierge"
headingPt2="Service"
subheading="Let's set up your system for continuous user discovery together."
@@ -69,10 +68,10 @@ const ConciergePage = () => {
</div>
</div>
))}
<div className="border-b border-t border-slate-300 p-6 text-4xl font-semibold text-slate-800">
{/* <div className="border-b border-t border-slate-300 p-6 text-4xl font-semibold text-slate-800">
<p className="mr-2 font-light">$1.290</p>
</div>
<div className="p-6 text-sm text-slate-800">
{ <div className="border-t border-slate-300 p-6 text-sm text-slate-800">
<p>
<CheckBadgeIcon className="mr-1 inline h-5 w-5 text-slate-800" />
100% Risk-free: Pay after the kick-off call, if you liked it.
@@ -81,14 +80,14 @@ const ConciergePage = () => {
<CheckBadgeIcon className="mr-1 inline h-5 w-5 text-slate-800" />
Money-back: If you&apos;re not happy, get a full refund.
</p>
</div>
</div> */}
<div className="px-6">
<Button
variant="darkCTA"
className="w-full justify-center"
href="https://cal.com/johannes/kick-off"
target="_blank">
Book free Kick-Off call
Schedule free Kick-Off call
</Button>
</div>
</div>

View File

@@ -1,6 +1,8 @@
import LayoutMdx from "@/components/shared/LayoutMdx";
import { FAQPageJsonLd } from "next-seo";
import Head from "next/head";
import Image from "next/image";
import { useRouter } from "next/router";
import fetch from "node-fetch";
import ReactMarkdown from "react-markdown";
@@ -65,7 +67,7 @@ export async function getStaticPaths() {
const articles = (await response.json()) as ArticleResponse;
const paths = articles.data.map((article) => ({
params: { slug: article.attributes.slug },
params: { slug: article.attributes?.slug },
}));
return { paths, fallback: true };
@@ -89,8 +91,12 @@ export async function getStaticProps({ params }) {
}
export default function ArticlePage({ article = {} }: ArticlePageProps) {
const router = useRouter();
if (!article || !article.attributes) return <div>Loading...</div>;
// Generate canonical URL
const canonicalURL = `https://formbricks.com${router.asPath}`;
// Use next/image to render images in markdown
const renderers = {
img: (image) => {
@@ -103,23 +109,23 @@ export default function ArticlePage({ article = {} }: ArticlePageProps) {
author,
publishedAt,
text,
faq,
faq = [],
meta: {
title,
description,
section,
title = "",
description = "",
section = "",
tags = [], // default empty array if tags are not provided
} = {}, // default empty object if meta is not provided
} = {}, // default empty object if attributes are not provided
} = article;
const metaTags = tags.map((tag) => tag.tag);
const metaTags = tags.map((tag) => tag.tag || "").filter((t) => t !== "");
const meta = {
title,
description,
publishedTime: publishedAt,
authors: [author],
publishedTime: publishedAt || Date.now().toString(),
authors: [author || "Formbricks"],
section,
tags: metaTags,
};
@@ -133,7 +139,10 @@ export default function ArticlePage({ article = {} }: ArticlePageProps) {
return (
<LayoutMdx meta={meta}>
<>
<ReactMarkdown components={renderers}>{text}</ReactMarkdown>
<ReactMarkdown components={renderers}>{text as any}</ReactMarkdown>
<Head>
<link rel="canonical" href={canonicalURL} />
</Head>
<FAQPageJsonLd mainEntity={faqEntities} />
</>
</LayoutMdx>

View File

@@ -116,7 +116,7 @@ Lets go through it in detail:
| Multiple Choice / Radio | ✅ | ✅ |
| Checkbox | ✅ | ✅ |
| Dropdown / Select | ✅ | ✅ |
| File Upload | ✅ | |
| File Upload | ✅ | ⚙️ |
| Linear Scale | ✅ | ⚙️ |
| Multiple Choice Grid | ✅ | ⚙️ |
| Checkbox Grid | ✅ | ⚙️ |

View File

@@ -3,12 +3,12 @@
--shiki-color-text: theme("colors.white");
--shiki-token-constant: theme("colors.emerald.300");
--shiki-token-string: theme("colors.emerald.300");
--shiki-token-comment: theme("colors.zinc.500");
--shiki-token-comment: theme("colors.slate.500");
--shiki-token-keyword: theme("colors.sky.300");
--shiki-token-parameter: theme("colors.pink.300");
--shiki-token-function: theme("colors.violet.300");
--shiki-token-string-expression: theme("colors.emerald.300");
--shiki-token-punctuation: theme("colors.zinc.200");
--shiki-token-punctuation: theme("colors.slate.200");
}
[inert] ::-webkit-scrollbar {

View File

@@ -1,5 +1,6 @@
import headlessuiPlugin from "@headlessui/tailwindcss";
import typographyPlugin from "@tailwindcss/typography";
import forms from "@tailwindcss/forms";
import { type Config } from "tailwindcss";
import defaultTheme from "tailwindcss/defaultTheme";
import typographyStyles from "./typography";
@@ -10,15 +11,15 @@ export default {
theme: {
fontSize: {
"2xs": ["0.75rem", { lineHeight: "1.25rem" }],
xs: ["0.8125rem", { lineHeight: "1.5rem" }],
xs: ["0.75rem", { lineHeight: "1rem" }],
sm: ["0.875rem", { lineHeight: "1.5rem" }],
base: ["1rem", { lineHeight: "1.75rem" }],
base: ["1rem", { lineHeight: "2rem" }],
lg: ["1.125rem", { lineHeight: "1.75rem" }],
xl: ["1.25rem", { lineHeight: "1.75rem" }],
"2xl": ["1.5rem", { lineHeight: "2rem" }],
"3xl": ["1.875rem", { lineHeight: "2.25rem" }],
"4xl": ["2.25rem", { lineHeight: "2.5rem" }],
"5xl": ["3rem", { lineHeight: "1" }],
xl: ["1.25rem", { lineHeight: "2rem" }],
"2xl": ["1.5rem", { lineHeight: "2.5rem" }],
"3xl": ["2rem", { lineHeight: "2.5rem" }],
"4xl": ["2.5rem", { lineHeight: "3rem" }],
"5xl": ["3rem", { lineHeight: "3.5rem" }],
"6xl": ["3.75rem", { lineHeight: "1" }],
"7xl": ["4.5rem", { lineHeight: "1" }],
"8xl": ["6rem", { lineHeight: "1" }],
@@ -26,6 +27,19 @@ export default {
},
typography: typographyStyles,
extend: {
boxShadow: {
glow: "0 0 4px rgb(0 0 0 / 0.1)",
},
dropShadow: {
card: "0px 4px 12px rgba(0, 0, 0, 0.5);",
},
maxWidth: {
lg: "33rem",
"2xl": "40rem",
"3xl": "50rem",
"5xl": "66rem",
"8xl": "88rem",
},
colors: {
brand: {
DEFAULT: "#00C4B8",
@@ -44,16 +58,6 @@ export default {
screens: {
xs: "430px",
},
dropShadow: {
card: "0px 4px 12px rgba(0, 0, 0, 0.5);",
},
maxWidth: {
lg: "33rem",
"2xl": "40rem",
"3xl": "50rem",
"5xl": "66rem",
"8xl": "88rem",
},
opacity: {
1: "0.01",
2.5: "0.025",
@@ -62,5 +66,5 @@ export default {
},
},
},
plugins: [typographyPlugin, headlessuiPlugin],
plugins: [typographyPlugin, headlessuiPlugin, forms],
} satisfies Config;

View File

@@ -1,28 +1,24 @@
{
"extends": "@formbricks/tsconfig/nextjs.json",
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"../../packages/types/*.d.ts"
],
"compilerOptions": {
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
"@/*": [
"./*"
]
},
"strictNullChecks": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"plugins": [
{
"name": "next"
}
]
}

View File

@@ -4,41 +4,41 @@ export default function typographyStyles({ theme }: PluginUtils) {
return {
DEFAULT: {
css: {
"--tw-prose-body": theme("colors.zinc.700"),
"--tw-prose-headings": theme("colors.zinc.900"),
"--tw-prose-body": theme("colors.slate.700"),
"--tw-prose-headings": theme("colors.slate.900"),
"--tw-prose-links": theme("colors.teal.500"),
"--tw-prose-links-hover": theme("colors.teal.600"),
"--tw-prose-links-underline": theme("colors.teal.500 / 0.3"),
"--tw-prose-bold": theme("colors.zinc.900"),
"--tw-prose-counters": theme("colors.zinc.500"),
"--tw-prose-bullets": theme("colors.zinc.300"),
"--tw-prose-hr": theme("colors.zinc.900 / 0.05"),
"--tw-prose-quotes": theme("colors.zinc.900"),
"--tw-prose-quote-borders": theme("colors.zinc.200"),
"--tw-prose-captions": theme("colors.zinc.500"),
"--tw-prose-code": theme("colors.zinc.900"),
"--tw-prose-code-bg": theme("colors.zinc.100"),
"--tw-prose-code-ring": theme("colors.zinc.300"),
"--tw-prose-th-borders": theme("colors.zinc.300"),
"--tw-prose-td-borders": theme("colors.zinc.200"),
"--tw-prose-bold": theme("colors.slate.900"),
"--tw-prose-counters": theme("colors.slate.500"),
"--tw-prose-bullets": theme("colors.slate.300"),
"--tw-prose-hr": theme("colors.slate.900 / 0.05"),
"--tw-prose-quotes": theme("colors.slate.900"),
"--tw-prose-quote-borders": theme("colors.slate.200"),
"--tw-prose-captions": theme("colors.slate.500"),
"--tw-prose-code": theme("colors.slate.900"),
"--tw-prose-code-bg": theme("colors.slate.100"),
"--tw-prose-code-ring": theme("colors.slate.300"),
"--tw-prose-th-borders": theme("colors.slate.300"),
"--tw-prose-td-borders": theme("colors.slate.200"),
"--tw-prose-invert-body": theme("colors.zinc.400"),
"--tw-prose-invert-body": theme("colors.slate.400"),
"--tw-prose-invert-headings": theme("colors.white"),
"--tw-prose-invert-links": theme("colors.teal.400"),
"--tw-prose-invert-links-hover": theme("colors.teal.500"),
"--tw-prose-invert-links-underline": theme("colors.teal.500 / 0.3"),
"--tw-prose-invert-bold": theme("colors.white"),
"--tw-prose-invert-counters": theme("colors.zinc.400"),
"--tw-prose-invert-bullets": theme("colors.zinc.600"),
"--tw-prose-invert-counters": theme("colors.slate.400"),
"--tw-prose-invert-bullets": theme("colors.slate.600"),
"--tw-prose-invert-hr": theme("colors.white / 0.05"),
"--tw-prose-invert-quotes": theme("colors.zinc.100"),
"--tw-prose-invert-quote-borders": theme("colors.zinc.700"),
"--tw-prose-invert-captions": theme("colors.zinc.400"),
"--tw-prose-invert-quotes": theme("colors.slate.100"),
"--tw-prose-invert-quote-borders": theme("colors.slate.700"),
"--tw-prose-invert-captions": theme("colors.slate.400"),
"--tw-prose-invert-code": theme("colors.white"),
"--tw-prose-invert-code-bg": theme("colors.zinc.700 / 0.15"),
"--tw-prose-invert-code-bg": theme("colors.slate.700 / 0.15"),
"--tw-prose-invert-code-ring": theme("colors.white / 0.1"),
"--tw-prose-invert-th-borders": theme("colors.zinc.600"),
"--tw-prose-invert-td-borders": theme("colors.zinc.700"),
"--tw-prose-invert-th-borders": theme("colors.slate.600"),
"--tw-prose-invert-td-borders": theme("colors.slate.700"),
// Base
color: "var(--tw-prose-body)",
@@ -183,7 +183,7 @@ export default function typographyStyles({ theme }: PluginUtils) {
// Headings
h1: {
color: "var(--tw-prose-headings)",
fontWeight: "700",
fontWeight: "500",
fontSize: theme("fontSize.2xl")[0],
...theme("fontSize.2xl")[1],
marginBottom: theme("spacing.2"),
@@ -205,6 +205,15 @@ export default function typographyStyles({ theme }: PluginUtils) {
marginBottom: theme("spacing.2"),
},
h4: {
color: "var(--tw-prose-body)",
fontSize: theme("fontSize.base")[0],
...theme("fontSize.base")[1],
fontWeight: "300",
marginTop: theme("spacing.10"),
marginBottom: theme("spacing.2"),
},
// Media
"img, video, figure": {
marginTop: theme("spacing.8"),

View File

@@ -1,7 +1,6 @@
"use client";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import { useEnvironment } from "@/lib/environments/environments";
import { useProduct } from "@/lib/products/products";
import { useSurvey } from "@/lib/surveys/surveys";
import type { Survey } from "@formbricks/types/surveys";
@@ -12,20 +11,25 @@ import QuestionsAudienceTabs from "./QuestionsSettingsTabs";
import QuestionsView from "./QuestionsView";
import SettingsView from "./SettingsView";
import SurveyMenuBar from "./SurveyMenuBar";
import { TEnvironment } from "@formbricks/types/v1/environment";
interface SurveyEditorProps {
environmentId: string;
surveyId: string;
environment: TEnvironment;
}
export default function SurveyEditor({ environmentId, surveyId }: SurveyEditorProps): JSX.Element {
export default function SurveyEditor({
environmentId,
surveyId,
environment,
}: SurveyEditorProps): JSX.Element {
const [activeView, setActiveView] = useState<"questions" | "settings">("questions");
const [activeQuestionId, setActiveQuestionId] = useState<string | null>(null);
const [localSurvey, setLocalSurvey] = useState<Survey | null>();
const [invalidQuestions, setInvalidQuestions] = useState<String[] | null>(null);
const { survey, isLoadingSurvey, isErrorSurvey } = useSurvey(environmentId, surveyId, true);
const { product, isLoadingProduct, isErrorProduct } = useProduct(environmentId);
const { environment, isLoadingEnvironment, isErrorEnvironment } = useEnvironment(environmentId);
useEffect(() => {
if (survey) {
@@ -37,11 +41,11 @@ export default function SurveyEditor({ environmentId, surveyId }: SurveyEditorPr
}
}, [survey]);
if (isLoadingSurvey || isLoadingProduct || isLoadingEnvironment || !localSurvey) {
if (isLoadingSurvey || isLoadingProduct || !localSurvey) {
return <LoadingSpinner />;
}
if (isErrorSurvey || isErrorProduct || isErrorEnvironment) {
if (isErrorSurvey || isErrorProduct) {
return <ErrorComponent />;
}
@@ -52,6 +56,7 @@ export default function SurveyEditor({ environmentId, surveyId }: SurveyEditorPr
localSurvey={localSurvey}
survey={survey}
environmentId={environmentId}
environment={environment}
activeId={activeView}
setActiveId={setActiveView}
setInvalidQuestions={setInvalidQuestions}

View File

@@ -14,12 +14,14 @@ import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { validateQuestion } from "./Validation";
import { TEnvironment } from "@formbricks/types/v1/environment";
interface SurveyMenuBarProps {
localSurvey: Survey;
survey: Survey;
setLocalSurvey: (survey: Survey) => void;
environmentId: string;
environment: TEnvironment;
activeId: "questions" | "settings";
setActiveId: (id: "questions" | "settings") => void;
setInvalidQuestions: (invalidQuestions: String[]) => void;
@@ -29,6 +31,7 @@ export default function SurveyMenuBar({
localSurvey,
survey,
environmentId,
environment,
setLocalSurvey,
activeId,
setActiveId,
@@ -159,101 +162,110 @@ export default function SurveyMenuBar({
};
return (
<div className="border-b border-slate-200 bg-white px-5 py-3 sm:flex sm:items-center sm:justify-between">
<div className="flex items-center space-x-2 whitespace-nowrap">
<Button
variant="secondary"
StartIcon={ArrowLeftIcon}
onClick={() => {
handleBack();
}}>
Back
</Button>
<p className="hidden pl-4 font-semibold md:block">{product.name} / </p>
<Input
defaultValue={localSurvey.name}
onChange={(e) => {
const updatedSurvey = { ...localSurvey, name: e.target.value };
setLocalSurvey(updatedSurvey);
}}
className="w-72 border-white hover:border-slate-200 "
/>
</div>
{!!localSurvey?.responseRate && (
<div className="mx-auto flex items-center rounded-full border border-amber-200 bg-amber-100 p-2 text-amber-700 shadow-sm">
<ExclamationTriangleIcon className=" h-5 w-5 text-amber-400" />
<p className="max-w-[90%] pl-1 text-xs lg:text-sm">
This survey received responses. To keep the data consistent, make changes with caution.
</p>
</div>
<>
{environment?.type === "development" && (
<nav className="top-0 z-10 w-full border-b border-slate-200 bg-white">
<div className="h-6 w-full bg-[#A33700] p-0.5 text-center text-sm text-white">
You&apos;re in development mode. Use it to test surveys, actions and attributes.
</div>
</nav>
)}
<div className="mt-3 flex sm:ml-4 sm:mt-0">
<div className="mr-4 flex items-center">
<SurveyStatusDropdown
surveyId={localSurvey.id}
environmentId={environmentId}
updateLocalSurveyStatus={updateLocalSurveyStatus}
<div className="border-b border-slate-200 bg-white px-5 py-3 sm:flex sm:items-center sm:justify-between">
<div className="flex items-center space-x-2 whitespace-nowrap">
<Button
variant="secondary"
StartIcon={ArrowLeftIcon}
onClick={() => {
handleBack();
}}>
Back
</Button>
<p className="hidden pl-4 font-semibold md:block">{product.name} / </p>
<Input
defaultValue={localSurvey.name}
onChange={(e) => {
const updatedSurvey = { ...localSurvey, name: e.target.value };
setLocalSurvey(updatedSurvey);
}}
className="w-72 border-white hover:border-slate-200 "
/>
</div>
<Button
variant={localSurvey.status === "draft" ? "secondary" : "darkCTA"}
className="mr-3"
loading={isMutatingSurvey}
onClick={() => saveSurveyAction()}>
Save
</Button>
{localSurvey.status === "draft" && audiencePrompt && (
<Button
variant="darkCTA"
onClick={() => {
setAudiencePrompt(false);
setActiveId("settings");
}}
EndIcon={Cog8ToothIcon}>
Continue to Settings
</Button>
{!!localSurvey?.responseRate && (
<div className="mx-auto flex items-center rounded-full border border-amber-200 bg-amber-100 p-2 text-amber-700 shadow-sm">
<ExclamationTriangleIcon className=" h-5 w-5 text-amber-400" />
<p className="max-w-[90%] pl-1 text-xs lg:text-sm">
This survey received responses. To keep the data consistent, make changes with caution.
</p>
</div>
)}
{localSurvey.status === "draft" && !audiencePrompt && (
<div className="mt-3 flex sm:ml-4 sm:mt-0">
<div className="mr-4 flex items-center">
<SurveyStatusDropdown
surveyId={localSurvey.id}
environmentId={environmentId}
updateLocalSurveyStatus={updateLocalSurveyStatus}
/>
</div>
<Button
disabled={
localSurvey.type === "web" &&
localSurvey.triggers &&
(localSurvey.triggers[0] === "" || localSurvey.triggers.length === 0)
}
variant="darkCTA"
variant={localSurvey.status === "draft" ? "secondary" : "darkCTA"}
className="mr-3"
loading={isMutatingSurvey}
onClick={async () => {
if (!validateSurvey(localSurvey)) {
return;
}
await triggerSurveyMutate({ ...localSurvey, status: "inProgress" });
router.push(`/environments/${environmentId}/surveys/${localSurvey.id}/summary?success=true`);
}}>
Publish
onClick={() => saveSurveyAction()}>
Save
</Button>
)}
{localSurvey.status === "draft" && audiencePrompt && (
<Button
variant="darkCTA"
onClick={() => {
setAudiencePrompt(false);
setActiveId("settings");
}}
EndIcon={Cog8ToothIcon}>
Continue to Settings
</Button>
)}
{localSurvey.status === "draft" && !audiencePrompt && (
<Button
disabled={
localSurvey.type === "web" &&
localSurvey.triggers &&
(localSurvey.triggers[0] === "" || localSurvey.triggers.length === 0)
}
variant="darkCTA"
loading={isMutatingSurvey}
onClick={async () => {
if (!validateSurvey(localSurvey)) {
return;
}
await triggerSurveyMutate({ ...localSurvey, status: "inProgress" });
router.push(`/environments/${environmentId}/surveys/${localSurvey.id}/summary?success=true`);
}}>
Publish
</Button>
)}
</div>
<DeleteDialog
deleteWhat="Draft"
open={isDeleteDialogOpen}
setOpen={setDeleteDialogOpen}
onDelete={() => deleteSurveyAction(localSurvey)}
text="Do you want to delete this draft?"
useSaveInsteadOfCancel={true}
onSave={() => saveSurveyAction(true)}
/>
<AlertDialog
confirmWhat="Survey changes"
open={isConfirmDialogOpen}
setOpen={setConfirmDialogOpen}
onDiscard={() => {
setConfirmDialogOpen(false);
router.back();
}}
text="You have unsaved changes in your survey. Would you like to save them before leaving?"
useSaveInsteadOfCancel={true}
onSave={() => saveSurveyAction(true)}
/>
</div>
<DeleteDialog
deleteWhat="Draft"
open={isDeleteDialogOpen}
setOpen={setDeleteDialogOpen}
onDelete={() => deleteSurveyAction(localSurvey)}
text="Do you want to delete this draft?"
useSaveInsteadOfCancel={true}
onSave={() => saveSurveyAction(true)}
/>
<AlertDialog
confirmWhat="Survey changes"
open={isConfirmDialogOpen}
setOpen={setConfirmDialogOpen}
onDiscard={() => {
setConfirmDialogOpen(false);
router.back();
}}
text="You have unsaved changes in your survey. Would you like to save them before leaving?"
useSaveInsteadOfCancel={true}
onSave={() => saveSurveyAction(true)}
/>
</div>
</>
);
}

View File

@@ -1,5 +1,9 @@
import SurveyEditor from "./SurveyEditor";
import { getEnvironment } from "@formbricks/lib/services/environment";
export default function SurveysEditPage({ params }) {
return <SurveyEditor environmentId={params.environmentId} surveyId={params.surveyId} />;
export default async function SurveysEditPage({ params }) {
const environment = await getEnvironment(params.environmentId);
return (
<SurveyEditor environmentId={params.environmentId} surveyId={params.surveyId} environment={environment} />
);
}

View File

@@ -19,7 +19,7 @@ export function cleanHtml(str: string): string {
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html) {
function removeScripts(html: Element) {
let scripts = html.querySelectorAll("script");
for (let script of scripts) {
script.remove();
@@ -32,19 +32,28 @@ export function cleanHtml(str: string): string {
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
function isPossiblyDangerous(name, value) {
/**
* Check if the attribute is potentially dangerous
*/
function isPossiblyDangerous(name: string, value: string): boolean {
let val = value.replace(/\s+/g, "").toLowerCase();
if (["src", "href", "xlink:href"].includes(name)) {
if (val.includes("javascript:") || val.includes("data:")) return true;
if (
["src", "href", "xlink:href"].includes(name) &&
(val.includes("javascript:") || val.includes("data:"))
) {
return true;
}
if (name.startsWith("on")) return true;
if (name.startsWith("on")) {
return true;
}
return false;
}
/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem) {
function removeAttributes(elem: Element) {
// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;
@@ -58,7 +67,11 @@ export function cleanHtml(str: string): string {
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
function clean(html) {
/**
* Clean the HTML nodes recursively
* @param {Element} html The HTML element
*/
function clean(html: Element) {
let nodes = html.children;
for (let node of nodes) {
removeAttributes(node);

View File

@@ -19,11 +19,11 @@ export function cleanHtml(str: string): string {
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html) {
function removeScripts(html: Element) {
let scripts = html.querySelectorAll("script");
for (let script of scripts) {
scripts.forEach((script) => {
script.remove();
}
});
}
/**
@@ -32,23 +32,33 @@ export function cleanHtml(str: string): string {
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
function isPossiblyDangerous(name, value) {
/**
* Check if the attribute is potentially dangerous
*/
function isPossiblyDangerous(name: string, value: string): boolean {
let val = value.replace(/\s+/g, "").toLowerCase();
if (["src", "href", "xlink:href"].includes(name)) {
if (val.includes("javascript:") || val.includes("data:")) return true;
if (
["src", "href", "xlink:href"].includes(name) &&
(val.includes("javascript:") || val.includes("data:"))
) {
return true;
}
if (name.startsWith("on")) return true;
if (name.startsWith("on")) {
return true;
}
return false;
}
/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem) {
function removeAttributes(elem: Element) {
// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;
for (let { name, value } of atts) {
for (let i = 0; i < atts.length; i++) {
let { name, value } = atts[i];
if (!isPossiblyDangerous(name, value)) continue;
elem.removeAttribute(name);
}
@@ -58,8 +68,12 @@ export function cleanHtml(str: string): string {
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
function clean(html) {
let nodes = html.children;
/**
* Clean the HTML nodes recursively
* @param {Element} html The HTML element
*/
function clean(html: Element) {
let nodes = Array.from(html.children);
for (let node of nodes) {
removeAttributes(node);
clean(node);

View File

@@ -18,4 +18,4 @@
},
"include": ["src", "next-env.d.ts"],
"exclude": ["node_modules"]
}
}

152
pnpm-lock.yaml generated
View File

@@ -96,12 +96,6 @@ importers:
'@types/node':
specifier: 20.5.6
version: 20.5.6
'@types/react':
specifier: 18.2.21
version: 18.2.21
'@types/react-dom':
specifier: 18.2.7
version: 18.2.7
'@types/react-highlight-words':
specifier: ^0.16.4
version: 0.16.4
@@ -195,9 +189,6 @@ importers:
tailwindcss:
specifier: ^3.3.3
version: 3.3.3
typescript:
specifier: 5.2.2
version: 5.2.2
unist-util-filter:
specifier: ^5.0.0
version: 5.0.0
@@ -264,13 +255,13 @@ importers:
version: 7.61.0(next@13.4.10)(react@18.2.0)
'@t3-oss/env-nextjs':
specifier: ^0.6.0
version: 0.6.0(typescript@5.2.2)(zod@3.21.4)
version: 0.6.0(typescript@5.1.6)(zod@3.21.4)
bcryptjs:
specifier: ^2.4.3
version: 2.4.3
eslint-config-next:
specifier: ^13.4.12
version: 13.4.12(eslint@8.46.0)(typescript@5.2.2)
version: 13.4.12(eslint@8.46.0)(typescript@5.1.6)
jsonwebtoken:
specifier: ^9.0.1
version: 9.0.1
@@ -430,7 +421,7 @@ importers:
version: 8.46.0
eslint-config-next:
specifier: ^13.4.12
version: 13.4.12(eslint@8.46.0)(typescript@5.2.2)
version: 13.4.12(eslint@8.46.0)(typescript@5.1.6)
eslint-config-prettier:
specifier: ^8.10.0
version: 8.10.0(eslint@8.46.0)
@@ -463,10 +454,10 @@ importers:
version: 29.5.3
'@typescript-eslint/eslint-plugin':
specifier: ^6.2.1
version: 6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.2.2)
version: 6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.1.6)
'@typescript-eslint/parser':
specifier: ^6.2.1
version: 6.2.1(eslint@8.46.0)(typescript@5.2.2)
version: 6.2.1(eslint@8.46.0)(typescript@5.1.6)
autoprefixer:
specifier: ^10.4.14
version: 10.4.14(postcss@8.4.27)
@@ -481,7 +472,7 @@ importers:
version: link:../eslint-config-formbricks
eslint-config-preact:
specifier: ^1.3.0
version: 1.3.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.2.2)
version: 1.3.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.1.6)
isomorphic-fetch:
specifier: ^3.0.0
version: 3.0.0
@@ -505,7 +496,7 @@ importers:
version: 10.16.0
preact-cli:
specifier: ^3.5.0
version: 3.5.0(eslint@8.46.0)(preact-render-to-string@6.2.0)(preact@10.16.0)(typescript@5.2.2)
version: 3.5.0(eslint@8.46.0)(preact-render-to-string@6.2.0)(preact@10.16.0)(typescript@5.1.6)
preact-render-to-string:
specifier: ^6.2.0
version: 6.2.0(preact@10.16.0)
@@ -5634,24 +5625,24 @@ packages:
dependencies:
defer-to-connect: 1.1.3
/@t3-oss/env-core@0.6.0(typescript@5.2.2)(zod@3.21.4):
/@t3-oss/env-core@0.6.0(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-3FkPAba069WRZVVab/sB1m3eSGn/rZeypx5k+sWEu1d+k0OQdRDnvFS+7MtxYgqVrwaRk3b7yVnX2dgSPVmWPQ==}
peerDependencies:
typescript: '>=4.7.2'
zod: ^3.0.0
dependencies:
typescript: 5.2.2
typescript: 5.1.6
zod: 3.21.4
dev: false
/@t3-oss/env-nextjs@0.6.0(typescript@5.2.2)(zod@3.21.4):
/@t3-oss/env-nextjs@0.6.0(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-SpzcGNIbUYcQw4zPPFeRJqCC1560zL7QmB0puIqOnuCsmykPkqHPX+n9CNZLXVQerboHzfvb7Kd+jAdouk72Vw==}
peerDependencies:
typescript: '>=4.7.2'
zod: ^3.0.0
dependencies:
'@t3-oss/env-core': 0.6.0(typescript@5.2.2)(zod@3.21.4)
typescript: 5.2.2
'@t3-oss/env-core': 0.6.0(typescript@5.1.6)(zod@3.21.4)
typescript: 5.1.6
zod: 3.21.4
dev: false
@@ -5989,6 +5980,7 @@ packages:
resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==}
dependencies:
'@types/react': 18.2.21
dev: true
/@types/react-highlight-words@0.16.4:
resolution: {integrity: sha512-KITBX3xzheQLu2s3bUgLmRE7ekmhc52zRjRTwkKayQARh30L4fjEGzGm7ULK9TuX2LgxWWavZqyQGDGjAHbL3w==}
@@ -6130,7 +6122,7 @@ packages:
'@types/yargs-parser': 21.0.0
dev: true
/@typescript-eslint/eslint-plugin@6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/eslint-plugin@6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-iZVM/ALid9kO0+I81pnp1xmYiFyqibAHzrqX4q5YvvVEyJqY+e6rfTXSCsc2jUxGNqJqTfFSSij/NFkZBiBzLw==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
@@ -6142,10 +6134,10 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.6.2
'@typescript-eslint/parser': 6.2.1(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/parser': 6.2.1(eslint@8.46.0)(typescript@5.1.6)
'@typescript-eslint/scope-manager': 6.2.1
'@typescript-eslint/type-utils': 6.2.1(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/utils': 6.2.1(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/type-utils': 6.2.1(eslint@8.46.0)(typescript@5.1.6)
'@typescript-eslint/utils': 6.2.1(eslint@8.46.0)(typescript@5.1.6)
'@typescript-eslint/visitor-keys': 6.2.1
debug: 4.3.4
eslint: 8.46.0
@@ -6154,26 +6146,26 @@ packages:
natural-compare: 1.4.0
natural-compare-lite: 1.4.0
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.2.2)
typescript: 5.2.2
ts-api-utils: 1.0.1(typescript@5.1.6)
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/experimental-utils@5.54.0(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/experimental-utils@5.54.0(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-rRYECOTh5V3iWsrOzXi7h1jp3Bi9OkJHrb3wECi3DVqMGTilo9wAYmCbT+6cGdrzUY3MWcAa2mESM6FMik6tVw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
'@typescript-eslint/utils': 5.54.0(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/utils': 5.54.0(eslint@8.46.0)(typescript@5.1.6)
eslint: 8.46.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/parser@5.59.9(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/parser@5.59.9(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6185,14 +6177,14 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 5.59.9
'@typescript-eslint/types': 5.59.9
'@typescript-eslint/typescript-estree': 5.59.9(typescript@5.2.2)
'@typescript-eslint/typescript-estree': 5.59.9(typescript@5.1.6)
debug: 4.3.4
eslint: 8.46.0
typescript: 5.2.2
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
/@typescript-eslint/parser@6.2.1(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/parser@6.2.1(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
@@ -6204,11 +6196,11 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 6.2.1
'@typescript-eslint/types': 6.2.1
'@typescript-eslint/typescript-estree': 6.2.1(typescript@5.2.2)
'@typescript-eslint/typescript-estree': 6.2.1(typescript@5.1.6)
'@typescript-eslint/visitor-keys': 6.2.1
debug: 4.3.4
eslint: 8.46.0
typescript: 5.2.2
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
dev: true
@@ -6236,7 +6228,7 @@ packages:
'@typescript-eslint/visitor-keys': 6.2.1
dev: true
/@typescript-eslint/type-utils@6.2.1(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/type-utils@6.2.1(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-fTfCgomBMIgu2Dh2Or3gMYgoNAnQm3RLtRp+jP7A8fY+LJ2+9PNpi5p6QB5C4RSP+U3cjI0vDlI3mspAkpPVbQ==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
@@ -6246,12 +6238,12 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 6.2.1(typescript@5.2.2)
'@typescript-eslint/utils': 6.2.1(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/typescript-estree': 6.2.1(typescript@5.1.6)
'@typescript-eslint/utils': 6.2.1(eslint@8.46.0)(typescript@5.1.6)
debug: 4.3.4
eslint: 8.46.0
ts-api-utils: 1.0.1(typescript@5.2.2)
typescript: 5.2.2
ts-api-utils: 1.0.1(typescript@5.1.6)
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
dev: true
@@ -6270,7 +6262,7 @@ packages:
engines: {node: ^16.0.0 || >=18.0.0}
dev: true
/@typescript-eslint/typescript-estree@5.54.0(typescript@5.2.2):
/@typescript-eslint/typescript-estree@5.54.0(typescript@5.1.6):
resolution: {integrity: sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6285,13 +6277,13 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
tsutils: 3.21.0(typescript@5.2.2)
typescript: 5.2.2
tsutils: 3.21.0(typescript@5.1.6)
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/typescript-estree@5.59.9(typescript@5.2.2):
/@typescript-eslint/typescript-estree@5.59.9(typescript@5.1.6):
resolution: {integrity: sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6306,12 +6298,12 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
tsutils: 3.21.0(typescript@5.2.2)
typescript: 5.2.2
tsutils: 3.21.0(typescript@5.1.6)
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
/@typescript-eslint/typescript-estree@6.2.1(typescript@5.2.2):
/@typescript-eslint/typescript-estree@6.2.1(typescript@5.1.6):
resolution: {integrity: sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
@@ -6326,13 +6318,13 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.2.2)
typescript: 5.2.2
ts-api-utils: 1.0.1(typescript@5.1.6)
typescript: 5.1.6
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/utils@5.54.0(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/utils@5.54.0(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6342,7 +6334,7 @@ packages:
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 5.54.0
'@typescript-eslint/types': 5.54.0
'@typescript-eslint/typescript-estree': 5.54.0(typescript@5.2.2)
'@typescript-eslint/typescript-estree': 5.54.0(typescript@5.1.6)
eslint: 8.46.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0(eslint@8.46.0)
@@ -6352,7 +6344,7 @@ packages:
- typescript
dev: true
/@typescript-eslint/utils@6.2.1(eslint@8.46.0)(typescript@5.2.2):
/@typescript-eslint/utils@6.2.1(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-eBIXQeupYmxVB6S7x+B9SdBeB6qIdXKjgQBge2J+Ouv8h9Cxm5dHf/gfAZA6dkMaag+03HdbVInuXMmqFB/lKQ==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
@@ -6363,7 +6355,7 @@ packages:
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 6.2.1
'@typescript-eslint/types': 6.2.1
'@typescript-eslint/typescript-estree': 6.2.1(typescript@5.2.2)
'@typescript-eslint/typescript-estree': 6.2.1(typescript@5.1.6)
eslint: 8.46.0
semver: 7.5.4
transitivePeerDependencies:
@@ -10065,7 +10057,7 @@ packages:
source-map: 0.6.1
dev: true
/eslint-config-next@13.4.12(eslint@8.46.0)(typescript@5.2.2):
/eslint-config-next@13.4.12(eslint@8.46.0)(typescript@5.1.6):
resolution: {integrity: sha512-ZF0r5vxKaVazyZH/37Au/XItiG7qUOBw+HaH3PeyXltIMwXorsn6bdrl0Nn9N5v5v9spc+6GM2ryjugbjF6X2g==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
@@ -10076,7 +10068,7 @@ packages:
dependencies:
'@next/eslint-plugin-next': 13.4.12
'@rushstack/eslint-patch': 1.2.0
'@typescript-eslint/parser': 5.59.9(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/parser': 5.59.9(eslint@8.46.0)(typescript@5.1.6)
eslint: 8.46.0
eslint-import-resolver-node: 0.3.6
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.26.0)(eslint@8.46.0)
@@ -10084,12 +10076,12 @@ packages:
eslint-plugin-jsx-a11y: 6.6.1(eslint@8.46.0)
eslint-plugin-react: 7.33.1(eslint@8.46.0)
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.46.0)
typescript: 5.2.2
typescript: 5.1.6
transitivePeerDependencies:
- eslint-import-resolver-webpack
- supports-color
/eslint-config-preact@1.3.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.2.2):
/eslint-config-preact@1.3.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.1.6):
resolution: {integrity: sha512-yHYXg5qNzEJd3D/30AmsIW0W8MuY858KpApXp7xxBF08IYUljSKCOqMx+dVucXHQnAm7+11wOnMkgVHIBAechw==}
peerDependencies:
eslint: 6.x || 7.x || 8.x
@@ -10101,7 +10093,7 @@ packages:
'@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.22.9)
eslint: 8.46.0
eslint-plugin-compat: 4.1.2(eslint@8.46.0)
eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.2.2)
eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.1.6)
eslint-plugin-react: 7.33.1(eslint@8.46.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.46.0)
transitivePeerDependencies:
@@ -10177,7 +10169,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.59.9(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/parser': 5.59.9(eslint@8.46.0)(typescript@5.1.6)
debug: 3.2.7
eslint: 8.46.0
eslint-import-resolver-node: 0.3.6
@@ -10211,7 +10203,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.59.9(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/parser': 5.59.9(eslint@8.46.0)(typescript@5.1.6)
array-includes: 3.1.6
array.prototype.flat: 1.3.1
debug: 2.6.9
@@ -10231,7 +10223,7 @@ packages:
- eslint-import-resolver-webpack
- supports-color
/eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.2.2):
/eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@6.2.1)(eslint@8.46.0)(jest@29.6.2)(typescript@5.1.6):
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
peerDependencies:
@@ -10244,8 +10236,8 @@ packages:
jest:
optional: true
dependencies:
'@typescript-eslint/eslint-plugin': 6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/experimental-utils': 5.54.0(eslint@8.46.0)(typescript@5.2.2)
'@typescript-eslint/eslint-plugin': 6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.1.6)
'@typescript-eslint/experimental-utils': 5.54.0(eslint@8.46.0)(typescript@5.1.6)
eslint: 8.46.0
jest: 29.6.2
transitivePeerDependencies:
@@ -10928,7 +10920,7 @@ packages:
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
dev: true
/fork-ts-checker-webpack-plugin@4.1.6(eslint@8.46.0)(typescript@5.2.2)(webpack@4.46.0):
/fork-ts-checker-webpack-plugin@4.1.6(eslint@8.46.0)(typescript@5.1.6)(webpack@4.46.0):
resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==}
engines: {node: '>=6.11.5', yarn: '>=1.0.0'}
peerDependencies:
@@ -10949,7 +10941,7 @@ packages:
minimatch: 3.1.2
semver: 5.7.1
tapable: 1.1.3
typescript: 5.2.2
typescript: 5.1.6
webpack: 4.46.0
worker-rpc: 0.1.1
transitivePeerDependencies:
@@ -15934,11 +15926,11 @@ packages:
resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==}
dev: true
/pnp-webpack-plugin@1.7.0(typescript@5.2.2):
/pnp-webpack-plugin@1.7.0(typescript@5.1.6):
resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==}
engines: {node: '>=6'}
dependencies:
ts-pnp: 1.2.0(typescript@5.2.2)
ts-pnp: 1.2.0(typescript@5.1.6)
transitivePeerDependencies:
- typescript
dev: true
@@ -17030,7 +17022,7 @@ packages:
- debug
dev: false
/preact-cli@3.5.0(eslint@8.46.0)(preact-render-to-string@6.2.0)(preact@10.16.0)(typescript@5.2.2):
/preact-cli@3.5.0(eslint@8.46.0)(preact-render-to-string@6.2.0)(preact@10.16.0)(typescript@5.1.6):
resolution: {integrity: sha512-x6B3p4aTzI9T1nWrMlXe0itfzJ2h8oC8S5kTJydzikcW/0ORDBSX1M8vT1FmjeLPDlyGvqlrRx1RI17XuxWIOA==}
engines: {node: '>=12'}
hasBin: true
@@ -17081,7 +17073,7 @@ packages:
envinfo: 7.8.1
esm: 3.2.25
file-loader: 6.2.0(webpack@4.46.0)
fork-ts-checker-webpack-plugin: 4.1.6(eslint@8.46.0)(typescript@5.2.2)(webpack@4.46.0)
fork-ts-checker-webpack-plugin: 4.1.6(eslint@8.46.0)(typescript@5.1.6)(webpack@4.46.0)
get-port: 5.1.1
gittar: 0.1.1
glob: 8.1.0
@@ -17096,7 +17088,7 @@ packages:
native-url: 0.3.4
optimize-css-assets-webpack-plugin: 6.0.1(webpack@4.46.0)
ora: 5.4.1
pnp-webpack-plugin: 1.7.0(typescript@5.2.2)
pnp-webpack-plugin: 1.7.0(typescript@5.1.6)
postcss: 8.4.27
postcss-load-config: 3.1.4(postcss@8.4.27)
postcss-loader: 4.3.0(postcss@8.4.27)(webpack@4.46.0)
@@ -17115,7 +17107,7 @@ packages:
stack-trace: 0.0.10
style-loader: 2.0.0(webpack@4.46.0)
terser-webpack-plugin: 4.2.3(webpack@4.46.0)
typescript: 5.2.2
typescript: 5.1.6
update-notifier: 5.1.0
url-loader: 4.1.1(file-loader@6.2.0)(webpack@4.46.0)
validate-npm-package-name: 4.0.0
@@ -20155,13 +20147,13 @@ packages:
resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==}
dev: false
/ts-api-utils@1.0.1(typescript@5.2.2):
/ts-api-utils@1.0.1(typescript@5.1.6):
resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
engines: {node: '>=16.13.0'}
peerDependencies:
typescript: '>=4.2.0'
dependencies:
typescript: 5.2.2
typescript: 5.1.6
dev: true
/ts-easing@0.2.0:
@@ -20182,7 +20174,7 @@ packages:
resolution: {integrity: sha512-sFHQYD4KoysBi7e7a2mzDPvRBeqA4w+vEyRE+P5MU9VLq8eEYxgKCgD9RNEAT+itGRWUTYN+hry94GDPLb1/Yw==}
dev: true
/ts-pnp@1.2.0(typescript@5.2.2):
/ts-pnp@1.2.0(typescript@5.1.6):
resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==}
engines: {node: '>=6'}
peerDependencies:
@@ -20191,7 +20183,7 @@ packages:
typescript:
optional: true
dependencies:
typescript: 5.2.2
typescript: 5.1.6
dev: true
/tsconfig-paths@3.14.1:
@@ -20246,14 +20238,14 @@ packages:
- ts-node
dev: true
/tsutils@3.21.0(typescript@5.2.2):
/tsutils@3.21.0(typescript@5.1.6):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
typescript: 5.2.2
typescript: 5.1.6
/tsx@3.12.7:
resolution: {integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==}
@@ -20440,12 +20432,6 @@ packages:
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/typescript@5.2.2:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
hasBin: true
/ua-parser-js@1.0.35:
resolution: {integrity: sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==}