mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 02:10:12 -06:00
fix: tailwind imports that were causing prod issues
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export const meta = {
|
||||
description: "Learn how to use the Formbricks Webhook API.",
|
||||
};
|
||||
|
||||
[Webhook API]()
|
||||
#### Webhook API
|
||||
|
||||
# Webhook API
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export const meta = {
|
||||
description: "How to contribute to Formbricks",
|
||||
};
|
||||
|
||||
[Contributing]()
|
||||
#### Contributing
|
||||
|
||||
# Contribution Guide
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ export const meta = {
|
||||
description: "Setup a development environment for Formbricks.",
|
||||
};
|
||||
|
||||
[Contributing]()
|
||||
#### Contributing
|
||||
|
||||
# Setup Dev Environment
|
||||
|
||||
|
||||
@@ -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 |
@@ -18,7 +18,7 @@ export const meta = {
|
||||
description: "Get your first in app survey response in 10 minutes.",
|
||||
};
|
||||
|
||||
[Getting Started](#)
|
||||
#### Getting Started
|
||||
|
||||
# Quickstart
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export const meta = {
|
||||
description: "Wire up Formbricks with Zapier and 5000+ other apps",
|
||||
};
|
||||
|
||||
[Integrations]()
|
||||
#### Integrations
|
||||
|
||||
# Zapier Setup
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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' }}
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
@@ -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 couldn’t find the page you’re looking for.
|
||||
</p>
|
||||
<Button href="/" arrow="right" className="mt-8">
|
||||
|
||||
@@ -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} />
|
||||
))}
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 © 2023. All rights reserved.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
‘{query}’
|
||||
</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} />
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
57
apps/formbricks-com/components/shared/SlideInBanner.tsx
Normal file
57
apps/formbricks-com/components/shared/SlideInBanner.tsx
Normal 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;
|
||||
BIN
apps/formbricks-com/images/blog/lfg-luigi-200px.webp
Normal file
BIN
apps/formbricks-com/images/blog/lfg-luigi-200px.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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'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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -116,7 +116,7 @@ Let’s go through it in detail:
|
||||
| Multiple Choice / Radio | ✅ | ✅ |
|
||||
| Checkbox | ✅ | ✅ |
|
||||
| Dropdown / Select | ✅ | ✅ |
|
||||
| File Upload | ✅ | ✅ |
|
||||
| File Upload | ✅ | ⚙️ |
|
||||
| Linear Scale | ✅ | ⚙️ |
|
||||
| Multiple Choice Grid | ✅ | ⚙️ |
|
||||
| Checkbox Grid | ✅ | ⚙️ |
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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'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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
},
|
||||
"include": ["src", "next-env.d.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
}
|
||||
152
pnpm-lock.yaml
generated
152
pnpm-lock.yaml
generated
@@ -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==}
|
||||
|
||||
Reference in New Issue
Block a user