UI tweaks (#216)

* preview hint, warnings, survey status indicator

* add event in survey builder, add surveystatusdropdown

* update wording, add survey status toasts, update LP

---------

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Johannes
2023-04-05 11:28:46 +02:00
committed by GitHub
parent 5b1c4f7208
commit 072a5947cf
35 changed files with 537 additions and 379 deletions

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Matthias Nannt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -13,16 +13,18 @@ interface EventDetailModalProps {
export default function AddNoCodeEventModalDummy({ open, setOpen }: EventDetailModalProps) {
return (
<Modal open={open} setOpen={setOpen} noPadding closeOnOutsideClick={false}>
<div className="flex h-full flex-col rounded-lg">
<div className="rounded-t-lg bg-slate-100">
<Modal open={open} setOpen={setOpen} noPadding>
<div className="flex h-full flex-col rounded-lg bg-slate-50 dark:bg-slate-800">
<div className="rounded-t-lg bg-slate-100 dark:bg-slate-700">
<div className="flex items-center justify-between p-6">
<div className="flex items-center space-x-2">
<div className="mr-1.5 h-6 w-6 text-slate-500">
<CursorArrowRaysIcon />
</div>
<div>
<div className="text-xl font-medium text-slate-700">Add No-Code Event</div>
<div className="text-xl font-medium text-slate-700 dark:text-slate-300">
Add No-Code Event
</div>
<div className="text-sm text-slate-500">
Create a new no-code event to filter your user base with.
</div>
@@ -36,21 +38,21 @@ export default function AddNoCodeEventModalDummy({ open, setOpen }: EventDetailM
<div>
<Label>Select By</Label>
<RadioGroup className="grid grid-cols-2 gap-1 md:grid-cols-3" defaultValue="pageUrl">
<div className="flex items-center space-x-2 rounded-lg border border-slate-200 p-3">
<div className="flex items-center space-x-2 rounded-lg border border-slate-200 p-3 dark:border-slate-500">
<RadioGroupItem value="pageUrl" id="pageUrl" className="bg-slate-50" />
<Label htmlFor="pageUrl" className="cursor-pointer">
<Label htmlFor="pageUrl" className="cursor-pointer dark:text-slate-200">
Page URL
</Label>
</div>
<div className="flex items-center space-x-2 rounded-lg bg-slate-50 p-3">
<div className="flex items-center space-x-2 rounded-lg bg-slate-50 p-3 dark:bg-slate-600">
<RadioGroupItem disabled value="innerHtml" id="innerHtml" className="bg-slate-50" />
<Label
htmlFor="innerHtml"
className="flex cursor-not-allowed items-center text-slate-500">
className="flex cursor-not-allowed items-center text-slate-500 dark:text-slate-400">
Inner Text
</Label>
</div>
<div className="hidden items-center space-x-2 rounded-lg bg-slate-50 p-3 md:flex">
<div className="hidden items-center space-x-2 rounded-lg bg-slate-50 p-3 dark:bg-slate-600 md:flex">
<RadioGroupItem disabled value="cssSelector" id="cssSelector" className="bg-slate-50" />
<Label
htmlFor="cssSelector"
@@ -73,10 +75,9 @@ export default function AddNoCodeEventModalDummy({ open, setOpen }: EventDetailM
<div className="grid w-full grid-cols-3 gap-x-8">
<div className="col-span-1">
<Label>URL</Label>
<Select defaultValue="endsWith">
<SelectTrigger
className="w-[110px] md:w-[180px]"
className="w-[110px] dark:text-slate-200 md:w-[180px]"
onClick={(e) => e.preventDefault()}
disabled>
<SelectValue placeholder="Select match type" />
@@ -98,7 +99,7 @@ export default function AddNoCodeEventModalDummy({ open, setOpen }: EventDetailM
</div>
</div>
</div>
<div className="flex justify-end border-t border-slate-200 p-6">
<div className="flex justify-end border-t border-slate-200 p-6 dark:border-slate-700">
<div className="flex space-x-2">
<Button
variant="minimal"

View File

@@ -290,7 +290,7 @@ export const templates: Template[] = [
id: createId(),
type: "multipleChoiceSingle",
headline: "What's on your mind, boss?",
subheader: "Thanks for sharing feedback. We'll get back to you asap.",
subheader: "Thanks for sharing. We'll get back to you asap.",
required: true,
choices: [
{
@@ -301,10 +301,6 @@ export const templates: Template[] = [
id: createId(),
label: "Feature Request 💡",
},
{
id: createId(),
label: "Share some love 🤍",
},
],
},
{

View File

@@ -35,7 +35,7 @@ export default function Hero({}: Props) {
<div className="mx-auto mt-5 max-w-lg items-center space-x-8 sm:flex sm:justify-center md:mt-8">
<p className="hidden whitespace-nowrap text-sm text-slate-400 dark:text-slate-500 md:block">
Used by
Trusted by
</p>
<div className="grid grid-cols-3 gap-8 pt-2">
<Image

View File

@@ -51,7 +51,7 @@ export default function Highlights({}) {
<h2 className="xs:text-3xl text-2xl font-bold tracking-tight text-slate-800 dark:text-slate-100 sm:text-3xl">
Spray and pray never worked.
<br />
<span className="font-light">Segment users, granularly.</span>
<span className="font-light">Pre-segment users, granularly.</span>
</h2>
<p className="text-md mt-6 max-w-md leading-7 text-slate-500 dark:text-slate-400">
Pre-segment who sees your survey based on custom attributes. Keep the signal, cancel out the

View File

@@ -21,7 +21,7 @@ export default function BreakerCTA({ inverted = false, teaser, headline, subhead
inverted
? "from-slate-800 via-slate-800 to-slate-700 dark:from-slate-200 dark:to-slate-300"
: "from-slate-200 to-slate-300 dark:from-slate-800 dark:via-slate-800 dark:to-slate-700",
"xs:mx-auto xs:w-full mx-4 my-4 max-w-6xl rounded-xl bg-gradient-to-br md:mb-0 "
"xs:mx-auto xs:w-full mx-4 my-4 mt-28 max-w-6xl rounded-xl bg-gradient-to-br md:mb-0 "
)}>
<div className="relative px-4 py-8 sm:px-6 sm:pt-8 sm:pb-12 lg:px-8 lg:pt-12">
<div className="xs:block xs:absolute xs:right-10 hidden md:top-1/2 md:-translate-y-1/2">

View File

@@ -35,7 +35,7 @@ export default function Header() {
</Link>
<Link
href="#pricing"
href="https://formbricks.com/#pricing"
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
Pricing
</Link>

View File

@@ -53,7 +53,7 @@ const Modal: React.FC<Modal> = ({
<div className="absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="rounded-md bg-white text-slate-400 hover:text-slate-500 focus:outline-none focus:ring-0 focus:ring-offset-2"
className="rounded-md bg-white text-slate-400 hover:text-slate-500 focus:outline-none focus:ring-0 focus:ring-offset-2 dark:bg-slate-900"
onClick={() => setOpen(false)}>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />

View File

@@ -4,8 +4,7 @@ import remarkGfm from "remark-gfm";
import rehypePrism from "@mapbox/rehype-prism";
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
output: "standalone",
transpilePackages: ["@formbricks/ui"],
pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"],
async redirects() {

View File

@@ -13,7 +13,7 @@
"@docsearch/react": "^3.3.3",
"@formbricks/ui": "workspace:*",
"@headlessui/react": "^1.7.13",
"@heroicons/react": "^2.0.16",
"@heroicons/react": "^2.0.17",
"@mapbox/rehype-prism": "^0.8.0",
"@mdx-js/loader": "^2.3.0",
"@mdx-js/react": "^2.3.0",
@@ -23,29 +23,28 @@
"lottie-web": "^5.10.2",
"next": "13.2.4",
"next-plausible": "^3.7.2",
"next-sitemap": "^4.0.5",
"next-sitemap": "^4.0.6",
"prism-react-renderer": "^1.3.5",
"prismjs": "^1.29.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.43.5",
"react-hook-form": "^7.43.9",
"react-responsive-embed": "^2.1.0",
"remark-gfm": "^3.0.1",
"sharp": "^0.31.3"
"sharp": "^0.32.0"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.9",
"@types/node": "18.15.3",
"@types/node": "18.15.11",
"@types/prismjs": "^1.26.0",
"@types/react": "18.0.28",
"@types/react": "18.0.33",
"@types/react-dom": "18.0.11",
"@types/react-responsive-embed": "^2.1.0",
"autoprefixer": "^10.4.14",
"eslint": "8.36.0",
"eslint": "8.37.0",
"eslint-config-formbricks": "workspace:*",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.7",
"typescript": "4.9.5"
"tailwindcss": "^3.3.1",
"typescript": "5.0.3"
}
}

View File

@@ -1,8 +1,8 @@
import Layout from "@/components/shared/Layout";
import Hero from "@/components/home/Hero";
import Features from "@/components/home/Features";
import Highlights from "@/components/home/Highlights"; /*
import BreakerCTA from "@/components/shared/BreakerCTA"; */
import Highlights from "@/components/home/Highlights";
import BreakerCTA from "@/components/shared/BreakerCTA";
import Steps from "@/components/home/Steps";
import Pricing from "@/components/shared/Pricing";
@@ -12,24 +12,23 @@ const IndexPage = () => (
description="Build qualitative user research into your product. Leverage Best practices to increase Product-Market Fit.">
<Hero />
<Highlights />
{/* <BreakerCTA
teaser="Curious?"
headline="Find out more"
subheadline="Dive into the project on GitHub."
cta="View on GitHub"
href="https://www.formbricks.com/github"
/> */}
<BreakerCTA
teaser="READY?"
headline="It's free to get started."
subheadline="Dont take our word for it, try it yourself."
cta="Start for free"
href="https://app.formbricks.com/auth/signup"
/>
<Features />
<Steps />
{/* <BreakerCTA
<BreakerCTA
teaser="Curious?"
headline="Get access now"
subheadline="Were onboarding design partners regularly. Sign up to get early access."
cta="Get access"
href="/waitlist"
headline="Give it a squeeze 🍋"
subheadline="Formbricks is free to get started. Give it a go!"
cta="Get started"
href="https://app.formbricks.com/auth/signup"
inverted
/> */}
/>
<Pricing />
</Layout>
);

View File

@@ -5,6 +5,7 @@ import { signOut } from "next-auth/react";
import { redirect } from "next/navigation";
import { useEffect } from "react";
import useSWR from "swr";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
export function HomeRedirect() {
const { data, error } = useSWR(`/api/v1/environments/find-first`, fetcher);
@@ -24,5 +25,9 @@ export function HomeRedirect() {
return <div>There was an error with your current Session. You are getting redirected to the login.</div>;
}
return <div>Loading environment...</div>;
return (
<div>
<LoadingSpinner />
</div>
);
}

View File

@@ -1,17 +1,24 @@
"use client";
import Modal from "@/components/shared/Modal";
import { Button } from "@formbricks/ui";
import { Input } from "@formbricks/ui";
import { Label } from "@formbricks/ui";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui";
import { CursorArrowRaysIcon } from "@heroicons/react/24/solid";
import { useState } from "react";
import { testURLmatch } from "./testURLmatch";
import clsx from "clsx";
import { useForm, Controller } from "react-hook-form";
import { createEventClass } from "@/lib/eventClasses/eventClasses";
import {
Button,
Input,
Label,
RadioGroup,
RadioGroupItem,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@formbricks/ui";
import { CursorArrowRaysIcon } from "@heroicons/react/24/solid";
import clsx from "clsx";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { testURLmatch } from "./testURLmatch";
interface EventDetailModalProps {
environmentId: string;

View File

@@ -0,0 +1,41 @@
"use client";
import { ErrorComponent } from "@/../../packages/ui";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import { useEnvironment } from "@/lib/environments/environments";
import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
import { useRouter } from "next/navigation";
export default function EnvironmentNotice({ environmentId }: { environmentId: string }) {
const { environment, isErrorEnvironment, isLoadingEnvironment } = useEnvironment(environmentId);
const router = useRouter();
const changeEnvironment = (environmentType: string) => {
const newEnvironmentId = environment.product.environments.find((e) => e.type === environmentType)?.id;
router.push(`/environments/${newEnvironmentId}/`);
};
if (isLoadingEnvironment) {
return <LoadingSpinner />;
}
if (isErrorEnvironment) {
return <ErrorComponent />;
}
return (
<div>
{environment.type === "production" && !environment.widgetSetupCompleted && (
<div className="flex items-center rounded-lg border border-amber-200 bg-amber-100 p-6 text-slate-900 shadow-sm">
<ExclamationTriangleIcon className="mr-3 h-6 w-6 text-amber-400" />
You&apos;re currently in the Production environment.
<a
onClick={() => changeEnvironment("development")}
className="ml-1 cursor-pointer font-medium underline">
Set up Development environment?
</a>
</div>
)}
</div>
);
}

View File

@@ -27,7 +27,7 @@ export default function SetupInstructions({ environmentId }) {
{activeTab === "npm" ? (
<div className="prose prose-slate">
<p className="text-lg font-semibold text-slate-800">Step 1: NPM Install</p>
<CodeBlock language="sh">npm install @formbricks/js</CodeBlock>
<CodeBlock language="sh">npm install @formbricks/js --save</CodeBlock>
<p className="pt-4 text-lg font-semibold text-slate-800">Step 2: Initialize widget</p>
<p>Import Formbricks and initialize the widget in your Component (e.g. App.tsx):</p>
<CodeBlock language="js">{`import formbricks from "@formbricks/js";
@@ -102,9 +102,11 @@ if (typeof window !== "undefined") {
<p>
Insert this code into the <code>{`<head>`}</code> tag of your website:
</p>
<CodeBlock language="js">{`<script type="text/javascript">
<CodeBlock language="js">{`<!-- START Formbricks Surveys -->
<script type="text/javascript">
!function(){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src="https://unpkg.com/@formbricks/js@0.1.2/dist/index.umd.js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks=window.js;window.formbricks.init({environmentId: "${environmentId}", apiHost: "${window.location.protocol}//${window.location.host}"})},500)}();
</script>`}</CodeBlock>
</script>
<!-- END Formbricks Surveys -->`}</CodeBlock>
<p className="text-lg font-semibold text-slate-800">You&apos;re done 🎉</p>
<p>
Your app now communicates with Formbricks - sending events, and loading surveys automatically!

View File

@@ -1,23 +1,24 @@
import WidgetStatusIndicator from "@/components/shared/WidgetStatusIndicator";
import SettingsCard from "../SettingsCard";
import SettingsTitle from "../SettingsTitle";
import EnvironmentNotice from "./EnvironmentNotice";
import SetupInstructions from "./SetupInstructions";
export default function ProfileSettingsPage({ params }) {
return (
<div>
<div className="space-y-4">
<SettingsTitle title="Setup Checklist" />
<SettingsCard title="Widget Status" description="Check if the Formbricks widget is alive and kicking.">
<WidgetStatusIndicator environmentId={params.environmentId} type="large" />
</SettingsCard>
<div className="mt-10">
<SettingsCard
title="How to setup"
description="Follow these steps to setup the Formbricks widget within your app"
noPadding>
<SetupInstructions environmentId={params.environmentId} />
</SettingsCard>
</div>
<EnvironmentNotice environmentId={params.environmentId} />
<SettingsCard
title="How to setup"
description="Follow these steps to setup the Formbricks widget within your app"
noPadding>
<SetupInstructions environmentId={params.environmentId} />
</SettingsCard>
</div>
);
}

View File

@@ -1,5 +1,6 @@
"use client";
import { cn } from "@formbricks/lib/cn";
import { Badge } from "@formbricks/ui";
import { Label } from "@formbricks/ui";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui";
@@ -11,7 +12,6 @@ import {
LinkIcon,
} from "@heroicons/react/24/solid";
import * as Collapsible from "@radix-ui/react-collapsible";
import clsx from "clsx";
import { useState } from "react";
const options = [
@@ -48,20 +48,23 @@ const options = [
interface HowToSendCardProps {}
export default function HowToSendCard({}: HowToSendCardProps) {
const [open, setOpen] = useState(false);
const [open, setOpen] = useState(true);
return (
<Collapsible.Root
open={open}
onOpenChange={setOpen}
className="w-full space-y-2 rounded-lg border border-slate-300 bg-white">
className={cn(
open ? "" : "hover:bg-slate-50",
"w-full space-y-2 rounded-lg border border-slate-300 bg-white "
)}>
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
<div className="inline-flex px-4 py-6">
<div className="flex items-center pr-5 pl-2">
<CheckCircleIcon className="h-8 w-8 text-green-400" />
</div>
<div>
<p className="font-semibold text-slate-800">How to send</p>
<p className="font-semibold text-slate-800">How to ask</p>
<p className="mt-1 truncate text-sm text-slate-500">
Choose how you want to reach your audience.
</p>
@@ -76,7 +79,7 @@ export default function HowToSendCard({}: HowToSendCardProps) {
<Label
key={option.id}
htmlFor={option.id}
className={clsx(
className={cn(
"flex w-full items-center rounded-lg border bg-slate-50 p-4",
option.comingSoon
? "border-slate-200 bg-slate-50/50"
@@ -93,7 +96,7 @@ export default function HowToSendCard({}: HowToSendCardProps) {
<div>
<div className="inline-flex items-center">
<p
className={clsx(
className={cn(
"font-semibold",
option.comingSoon ? "text-slate-500" : "text-slate-800"
)}>

View File

@@ -9,6 +9,7 @@ import { CheckCircleIcon } from "@heroicons/react/24/solid";
import * as Collapsible from "@radix-ui/react-collapsible";
import Link from "next/link";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
interface DisplayOption {
id: "displayOnce" | "displayMultiple" | "respondMultiple";
@@ -19,18 +20,18 @@ interface DisplayOption {
const displayOptions: DisplayOption[] = [
{
id: "displayOnce",
name: "Show once",
description: "If person doesn't respond the survey won't be shown again.",
name: "Only once, even if they do not respond",
description: "The survey won't be shown again, if person doesn't respond.",
},
{
id: "displayMultiple",
name: "Display until responded",
name: "Until they submit a response",
description: "If you really want that answer, ask until you get it.",
},
{
id: "respondMultiple",
name: "Always display when conditions match",
description: "This is useful for e.g. Feedback Boxes. Can cause survey fatigue.",
name: "Always, when the conditions match",
description: "Even after they submitted a response (e.g. Feedback Box)",
},
];
@@ -73,7 +74,10 @@ export default function RecontactOptionsCard({
<Collapsible.Root
open={open}
onOpenChange={setOpen}
className=" w-full space-y-2 rounded-lg border border-slate-300 bg-white">
className={cn(
open ? "" : "hover:bg-slate-50",
"w-full space-y-2 rounded-lg border border-slate-300 bg-white "
)}>
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
<div className="inline-flex px-4 py-6">
<div className="flex items-center pr-5 pl-2">

View File

@@ -4,9 +4,8 @@ import { Label } from "@formbricks/ui";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui";
import { CheckCircleIcon } from "@heroicons/react/24/solid";
import * as Collapsible from "@radix-ui/react-collapsible";
import { Badge } from "@formbricks/ui";
import clsx from "clsx";
import { cn } from "@formbricks/lib/cn";
import { useState } from "react";
const options = [
@@ -33,7 +32,10 @@ export default function ResponseOptionsCard({}: ResponseOptionsCardProps) {
<Collapsible.Root
open={open}
onOpenChange={setOpen}
className="w-full space-y-2 rounded-lg border border-slate-300 bg-white">
className={cn(
open ? "" : "hover:bg-slate-50",
"w-full space-y-2 rounded-lg border border-slate-300 bg-white "
)}>
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
<div className="inline-flex px-4 py-6">
<div className="flex items-center pr-5 pl-2">
@@ -59,7 +61,7 @@ export default function ResponseOptionsCard({}: ResponseOptionsCardProps) {
<Label
key={option.id}
htmlFor={option.id}
className={clsx(
className={cn(
"flex w-full items-center rounded-lg border bg-slate-50 p-4",
option.disabled
? "border-slate-200 bg-slate-50/50"
@@ -73,11 +75,7 @@ export default function ResponseOptionsCard({}: ResponseOptionsCardProps) {
/>
<div>
<div className="inline-flex items-center">
<p
className={clsx(
"font-semibold",
option.disabled ? "text-slate-500" : "text-slate-800"
)}>
<p className={cn("font-semibold", option.disabled ? "text-slate-500" : "text-slate-800")}>
{option.name}
</p>
{option.disabled && <Badge text="coming soon" size="normal" type="warning" />}

View File

@@ -1,10 +1,11 @@
"use client";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import { ErrorComponent } from "@formbricks/ui";
import { useProduct } from "@/lib/products/products";
import { useSurvey } from "@/lib/surveys/surveys";
import type { Survey } from "@formbricks/types/surveys";
import { ErrorComponent } from "@formbricks/ui";
import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
import { useEffect, useState } from "react";
import PreviewSurvey from "../../PreviewSurvey";
import AudienceView from "./AudienceView";
@@ -56,6 +57,13 @@ export default function SurveyEditor({ environmentId, surveyId }: SurveyEditorPr
/>
<div className="relative z-0 flex flex-1 overflow-hidden">
<main className="relative z-0 flex-1 overflow-y-auto focus:outline-none">
{survey.status !== "draft" && (
<div className="flex items-center border border-red-200 bg-red-100 p-2 text-sm text-slate-700 shadow-sm">
<ExclamationTriangleIcon className="mr-3 h-6 w-6 text-red-400" />
You&apos;re editing a published survey. Be cautious when making changes, they might mess up the
data.
</div>
)}
<QuestionsAudienceTabs activeId={activeView} setActiveId={setActiveView} />
{activeView === "questions" ? (
<QuestionsView

View File

@@ -1,12 +1,13 @@
"use client";
import { Button } from "@formbricks/ui";
import { Input } from "@formbricks/ui";
import SurveyStatusDropdown from "@/components/shared/SurveyStatusDropdown";
import { useSurveyMutation } from "@/lib/surveys/mutateSurveys";
import type { Survey } from "@formbricks/types/surveys";
import { Button, Input } from "@formbricks/ui";
import { UserGroupIcon } from "@heroicons/react/24/solid";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
interface SurveyMenuBarProps {
localSurvey: Survey;
@@ -33,18 +34,33 @@ export default function SurveyMenuBar({
}
}, [activeId, audiencePrompt]);
// write a function which updates the local survey status
const updateLocalSurveyStatus = (status: Survey["status"]) => {
const updatedSurvey = { ...localSurvey, status };
setLocalSurvey(updatedSurvey);
};
return (
<div className="border-b border-slate-200 bg-white py-3 px-5 sm:flex sm:items-center sm:justify-between">
<Input
defaultValue={localSurvey.name}
onChange={(e) => {
const updatedSurvey = { ...localSurvey, name: e.target.value };
setLocalSurvey(updatedSurvey);
}}
className="max-w-md"
/>
<div className="flex space-x-2 whitespace-nowrap">
<Input
defaultValue={localSurvey.name}
onChange={(e) => {
const updatedSurvey = { ...localSurvey, name: e.target.value };
setLocalSurvey(updatedSurvey);
}}
className="min-w-sm max-w-md"
/>
<div className="flex items-center">
<SurveyStatusDropdown
surveyId={localSurvey.id}
environmentId={environmentId}
updateLocalSurveyStatus={updateLocalSurveyStatus}
/>
</div>
</div>
<div className="mt-3 flex sm:mt-0 sm:ml-4">
<Button variant="minimal" className="mr-3" href={`/environments/${environmentId}/surveys/`}>
<Button variant="minimal" className="mr-3" onClick={() => router.back()}>
Cancel
</Button>
<Button
@@ -52,12 +68,18 @@ export default function SurveyMenuBar({
className="mr-3"
loading={isMutatingSurvey}
onClick={() => {
triggerSurveyMutate({ ...localSurvey });
triggerSurveyMutate({ ...localSurvey })
.then(() => {
toast.success("Changes saved.");
})
.catch((error) => {
toast.error(`Error: ${error.message}`);
});
if (localSurvey.status !== "draft") {
router.push(`/environments/${environmentId}/surveys/${localSurvey.id}/summary?success=true`);
router.push(`/environments/${environmentId}/surveys/${localSurvey.id}/summary`);
}
}}>
Save changes
Save Changes
</Button>
{localSurvey.status === "draft" && audiencePrompt && (
<Button

View File

@@ -1,13 +1,14 @@
"use client";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import { Button } from "@formbricks/ui";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui";
import { useEventClasses } from "@/lib/eventClasses/eventClasses";
import { cn } from "@formbricks/lib/cn";
import type { Survey } from "@formbricks/types/surveys";
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui";
import { CheckCircleIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/solid";
import * as Collapsible from "@radix-ui/react-collapsible";
import { useState } from "react";
import AddNoCodeEventModal from "../../../events/AddNoCodeEventModal";
interface WhenToSendCardProps {
localSurvey: Survey;
@@ -17,7 +18,9 @@ interface WhenToSendCardProps {
export default function WhenToSendCard({ environmentId, localSurvey, setLocalSurvey }: WhenToSendCardProps) {
const [open, setOpen] = useState(false);
const { eventClasses, isLoadingEventClasses, isErrorEventClasses } = useEventClasses(environmentId);
const { eventClasses, isLoadingEventClasses, isErrorEventClasses, mutateEventClasses } =
useEventClasses(environmentId);
const [isAddEventModalOpen, setAddEventModalOpen] = useState(false);
if (isLoadingEventClasses) {
return <LoadingSpinner />;
@@ -45,64 +48,89 @@ export default function WhenToSendCard({ environmentId, localSurvey, setLocalSur
setLocalSurvey(updatedSurvey);
};
return (
<Collapsible.Root
open={open}
onOpenChange={setOpen}
className="w-full space-y-2 rounded-lg border border-slate-300 bg-white">
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
<div className="inline-flex px-4 py-6">
<div className="flex items-center pr-5 pl-2">
{localSurvey.triggers.length === 0 || !localSurvey.triggers[0] ? (
<div className="h-7 w-7 rounded-full border border-slate-400" />
) : (
<CheckCircleIcon className="h-8 w-8 text-green-400" />
)}
</div>
/* // If there are no trigger events, set default to first event class in the eventClasses object
if (localSurvey.triggers.length === 0 && eventClasses.length > 0) {
setTriggerEvent(0, eventClasses[0].id);
} */
<div>
<p className="font-semibold text-slate-800">When to send</p>
<p className="mt-1 truncate text-sm text-slate-500">
Choose the events when you want the survey to trigger.
</p>
</div>
</div>
</Collapsible.CollapsibleTrigger>
<Collapsible.CollapsibleContent className="">
<hr className="py-1 text-slate-600" />
{localSurvey.triggers.map((triggerEventClassId, idx) => (
<div className="mt-2" key={idx}>
<div className="inline-flex items-center">
<p className="mr-2 w-14 text-right text-sm">{idx === 0 ? "When" : "or"}</p>
<Select
value={triggerEventClassId}
onValueChange={(eventClassId) => setTriggerEvent(idx, eventClassId)}>
<SelectTrigger className="w-[180px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{eventClasses.map((eventClass) => (
<SelectItem value={eventClass.id}>{eventClass.name}</SelectItem>
))}
</SelectContent>
</Select>
<button onClick={() => removeTriggerEvent(idx)}>
<TrashIcon className="ml-3 h-4 w-4 text-slate-400" />
</button>
return (
<>
{" "}
<Collapsible.Root
open={open}
onOpenChange={setOpen}
className={cn(
open ? "" : "hover:bg-slate-50",
"w-full space-y-2 rounded-lg border border-slate-300 bg-white "
)}>
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
<div className="inline-flex px-4 py-6">
<div className="flex items-center pr-5 pl-2">
{localSurvey.triggers.length === 0 || !localSurvey.triggers[0] ? (
<div className="h-7 w-7 rounded-full border border-slate-400" />
) : (
<CheckCircleIcon className="h-8 w-8 text-green-400" />
)}
</div>
<div>
<p className="font-semibold text-slate-800">When to ask</p>
<p className="mt-1 truncate text-sm text-slate-500">
Choose the events which trigger the survey.
</p>
</div>
</div>
))}
<div className="p-3">
<Button
variant="secondary"
onClick={() => {
addTriggerEvent();
}}>
<PlusIcon className="mr-2 h-4 w-4" />
Add event
</Button>
</div>
</Collapsible.CollapsibleContent>
</Collapsible.Root>
</Collapsible.CollapsibleTrigger>
<Collapsible.CollapsibleContent className="">
<hr className="py-1 text-slate-600" />
{localSurvey.triggers.map((triggerEventClassId, idx) => (
<div className="mt-2" key={idx}>
<div className="inline-flex items-center">
<p className="mr-2 w-14 text-right text-sm">{idx === 0 ? "When" : "or"}</p>
<Select
value={triggerEventClassId}
onValueChange={(eventClassId) => setTriggerEvent(idx, eventClassId)}>
<SelectTrigger className="w-[180px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{eventClasses.map((eventClass) => (
<SelectItem value={eventClass.id}>{eventClass.name}</SelectItem>
))}
</SelectContent>
</Select>
<p className="mx-2 text-sm">event is triggered</p>
<button onClick={() => removeTriggerEvent(idx)}>
<TrashIcon className="ml-3 h-4 w-4 text-slate-400" />
</button>
</div>
</div>
))}
<div className="p-3">
<Button
variant="secondary"
onClick={() => {
addTriggerEvent();
}}>
<PlusIcon className="mr-2 h-4 w-4" />
Add condition
</Button>
<Button
variant="minimal"
onClick={() => {
setAddEventModalOpen(true);
}}>
Create event
</Button>
</div>
</Collapsible.CollapsibleContent>
</Collapsible.Root>
<AddNoCodeEventModal
environmentId={environmentId}
open={isAddEventModalOpen}
setOpen={setAddEventModalOpen}
mutateEventClasses={mutateEventClasses}
/>
</>
);
}

View File

@@ -5,7 +5,7 @@ import { Label } from "@formbricks/ui";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui";
import { CheckCircleIcon } from "@heroicons/react/24/solid";
import * as Collapsible from "@radix-ui/react-collapsible";
import clsx from "clsx";
import { cn } from "@formbricks/lib/cn";
import { useState } from "react";
const options = [
@@ -32,14 +32,17 @@ export default function WhoToSendToCard({}: WhoToSendToCardProps) {
<Collapsible.Root
open={open}
onOpenChange={setOpen}
className="w-full space-y-2 rounded-lg border border-slate-300 bg-white">
className={cn(
open ? "" : "hover:bg-slate-50",
"w-full space-y-2 rounded-lg border border-slate-300 bg-white "
)}>
<Collapsible.CollapsibleTrigger asChild className="h-full w-full cursor-pointer">
<div className="inline-flex px-4 py-6">
<div className="flex items-center pr-5 pl-2">
<CheckCircleIcon className="h-8 w-8 text-green-400" />
</div>
<div>
<p className="font-semibold text-slate-800">Who to send to</p>
<p className="font-semibold text-slate-800">Who to ask</p>
<p className="mt-1 truncate text-sm text-slate-500">
Decide which group of you users can be surveyed.
</p>
@@ -54,7 +57,7 @@ export default function WhoToSendToCard({}: WhoToSendToCardProps) {
<Label
key={option.id}
htmlFor={option.id}
className={clsx(
className={cn(
"flex w-full items-center rounded-lg border bg-slate-50 p-4",
option.disabled
? "border-slate-200 bg-slate-50/50"
@@ -68,11 +71,7 @@ export default function WhoToSendToCard({}: WhoToSendToCardProps) {
/>
<div>
<div className="inline-flex items-center">
<p
className={clsx(
"font-semibold",
option.disabled ? "text-slate-500" : "text-slate-800"
)}>
<p className={cn("font-semibold", option.disabled ? "text-slate-500" : "text-slate-800")}>
{option.name}
</p>
{option.disabled && <Badge text="coming soon" size="normal" type="warning" />}

View File

@@ -1,27 +1,24 @@
"use client";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import SurveyStatusIndicator from "@/components/shared/SurveyStatusIndicator";
import { Button } from "@formbricks/ui";
import { ErrorComponent } from "@formbricks/ui";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui";
import { useEnvironment } from "@/lib/environments/environments";
import { useResponses } from "@/lib/responses/responses";
import { useSurveyMutation } from "@/lib/surveys/mutateSurveys";
import { useSurvey } from "@/lib/surveys/surveys";
import {
CheckCircleIcon,
PauseCircleIcon,
PencilSquareIcon,
PlayCircleIcon,
} from "@heroicons/react/24/solid";
Button,
ErrorComponent,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@formbricks/ui";
import { PencilSquareIcon } from "@heroicons/react/24/solid";
import { useMemo } from "react";
import SurveyStatusDropdown from "@/components/shared/SurveyStatusDropdown";
export default function SummaryMetadata({ surveyId, environmentId }) {
const { responses, isLoadingResponses, isErrorResponses } = useResponses(environmentId, surveyId);
const { survey, isLoadingSurvey, isErrorSurvey } = useSurvey(environmentId, surveyId);
const { triggerSurveyMutate } = useSurveyMutation(environmentId, surveyId);
const { environment, isLoadingEnvironment, isErrorEnvironment } = useEnvironment(environmentId);
const completionRate = useMemo(() => {
@@ -95,48 +92,7 @@ export default function SummaryMetadata({ surveyId, environmentId }) {
<div className=""></div>
<div className="flex justify-end">
{environment.widgetSetupCompleted && (
<>
{survey.status === "draft" || survey.status === "archived" ? (
<div className="flex items-center">
<SurveyStatusIndicator status={survey.status} environmentId={environmentId} />
<span className="mr-3 italic text-slate-500">
{survey.status === "draft" && "Survey drafted"}
{survey.status === "archived" && "Survey archived"}
</span>
</div>
) : (
<Select onValueChange={(value) => triggerSurveyMutate({ status: value })}>
<SelectTrigger className="w-[200px] bg-white py-1.5">
<SelectValue>
<div className="flex items-center">
<SurveyStatusIndicator status={survey.status} environmentId={environmentId} />
<span className="ml-2 text-sm text-slate-700">
{survey.status === "draft" && "Survey drafted"}
{survey.status === "inProgress" && "Collecting insights"}
{survey.status === "paused" && "Survey paused"}
{survey.status === "completed" && "Survey complete"}
{survey.status === "archived" && "Survey archived"}
</span>
</div>
</SelectValue>
</SelectTrigger>
<SelectContent className="bg-white">
<SelectItem className="group font-normal hover:text-slate-900" value="inProgress">
<PlayCircleIcon className="mr-1 -mt-1 inline h-5 w-5 text-slate-500 group-hover:text-slate-800" />
Collect insights
</SelectItem>
<SelectItem className="group font-normal hover:text-slate-900" value="paused">
<PauseCircleIcon className="mr-1 -mt-1 inline h-5 w-5 text-slate-500 group-hover:text-slate-800" />
Pause Survey
</SelectItem>
<SelectItem className="group font-normal hover:text-slate-900" value="completed">
<CheckCircleIcon className="mr-1 -mt-1 inline h-5 w-5 text-slate-500 group-hover:text-slate-800" />
End Survey
</SelectItem>
</SelectContent>
</Select>
)}
</>
<SurveyStatusDropdown surveyId={surveyId} environmentId={environmentId} />
)}
<Button className="ml-1.5 h-full" href={`/environments/${environmentId}/surveys/${surveyId}/edit`}>
<PencilSquareIcon className="mr-2 h-5 w-5 text-white" /> Edit Survey

View File

@@ -603,7 +603,7 @@ export const templates: Template[] = [
id: createId(),
type: "multipleChoiceSingle",
headline: "What's on your mind, boss?",
subheader: "Thanks for sharing feedback. We'll get back to you asap.",
subheader: "Thanks for sharing. We'll get back to you asap.",
required: true,
choices: [
{
@@ -614,10 +614,6 @@ export const templates: Template[] = [
id: createId(),
label: "Feature Request 💡",
},
{
id: createId(),
label: "Share some love 🤍",
},
],
},
{

View File

@@ -1,6 +1,6 @@
export default function Headline({ headline, questionId }: { headline: string; questionId: string }) {
return (
<label htmlFor={questionId} className="block text-base font-semibold leading-6 text-slate-900">
<label htmlFor={questionId} className="mb-1.5 block text-base font-semibold leading-6 text-slate-900">
{headline}
</label>
);

View File

@@ -11,11 +11,12 @@ export default function Modal({ children, isOpen }: { children: ReactNode; isOpe
<div
aria-live="assertive"
className="pointer-events-none absolute inset-0 flex items-end px-4 py-6 sm:p-6">
<div className="flex w-full flex-col items-center space-y-4 sm:items-end">
<div className="flex w-full flex-col items-center sm:items-end">
<div className="mr-6 rounded-t bg-amber-400 px-3 text-sm font-semibold text-white">Preview</div>
<div
className={cn(
show ? "translate-x-0 opacity-100" : "translate-x-28 opacity-0",
"pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white px-4 py-6 shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-500 ease-in-out sm:p-6"
"pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg border-2 border-amber-400 bg-white px-4 py-6 shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-500 ease-in-out sm:p-6"
)}>
{children}
</div>

View File

@@ -1,6 +1,6 @@
export default function Subheader({ subheader, questionId }: { subheader?: string; questionId: string }) {
return (
<label htmlFor={questionId} className="block text-sm font-normal leading-6 text-slate-600">
<label htmlFor={questionId} className="block text-sm font-normal leading-6 text-slate-500">
{subheader}
</label>
);

View File

@@ -1,8 +1,8 @@
// components/ui/CodeBlock.tsx
import React, { useEffect } from "react";
import { DocumentDuplicateIcon } from "@heroicons/react/24/outline";
import Prism from "prismjs";
import "prismjs/themes/prism.css";
import { DocumentDuplicateIcon } from "@heroicons/react/24/outline";
import React, { useEffect } from "react";
import toast from "react-hot-toast";
interface CodeBlockProps {
@@ -16,7 +16,7 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, language }) => {
}, [children]);
return (
<div className="group relative mt-4 rounded-md font-light text-slate-200">
<div className="group relative mt-4 rounded-md text-sm text-slate-200">
<DocumentDuplicateIcon
className="absolute top-4 right-4 z-20 h-5 w-5 cursor-pointer text-slate-600 opacity-0 transition-all duration-150 group-hover:opacity-60"
onClick={() => {

View File

@@ -0,0 +1,102 @@
"use client";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import SurveyStatusIndicator from "@/components/shared/SurveyStatusIndicator";
import { useSurveyMutation } from "@/lib/surveys/mutateSurveys";
import { useSurvey } from "@/lib/surveys/surveys";
import {
ErrorComponent,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@formbricks/ui";
import { CheckCircleIcon, PauseCircleIcon, PlayCircleIcon } from "@heroicons/react/24/solid";
import toast from "react-hot-toast";
export default function SurveyStatusDropdown({
surveyId,
environmentId,
updateLocalSurveyStatus,
}: {
surveyId: string;
environmentId: string;
updateLocalSurveyStatus?: (status: "draft" | "inProgress" | "paused" | "completed" | "archived") => void;
}) {
const { survey, isLoadingSurvey, isErrorSurvey } = useSurvey(environmentId, surveyId);
const { triggerSurveyMutate } = useSurveyMutation(environmentId, surveyId);
if (isLoadingSurvey) {
return <LoadingSpinner />;
}
if (isErrorSurvey) {
return <ErrorComponent />;
}
return (
<>
{survey.status === "draft" || survey.status === "archived" ? (
<div className="flex items-center">
<SurveyStatusIndicator status={survey.status} environmentId={environmentId} />
<span className="mr-3 italic text-slate-500">
{survey.status === "draft" && "Survey drafted"}
{survey.status === "archived" && "Survey archived"}
</span>
</div>
) : (
<Select
onValueChange={(value) => {
triggerSurveyMutate({ status: value })
.then(() => {
toast.success(
value === "inProgress"
? "Survey live"
: value === "paused"
? "Survey paused"
: value === "completed"
? "Survey completed"
: ""
);
})
.catch((error) => {
toast.error(`Error: ${error.message}`);
});
if (updateLocalSurveyStatus)
updateLocalSurveyStatus(value as "draft" | "inProgress" | "paused" | "completed" | "archived");
}}>
<SelectTrigger className="w-[200px] bg-white py-1.5">
<SelectValue>
<div className="flex items-center">
<SurveyStatusIndicator status={survey.status} environmentId={environmentId} />
<span className="ml-2 text-sm text-slate-700">
{survey.status === "draft" && "Survey drafted"}
{survey.status === "inProgress" && "Collecting insights"}
{survey.status === "paused" && "Survey paused"}
{survey.status === "completed" && "Survey complete"}
{survey.status === "archived" && "Survey archived"}
</span>
</div>
</SelectValue>
</SelectTrigger>
<SelectContent className="bg-white">
<SelectItem className="group font-normal hover:text-slate-900" value="inProgress">
<PlayCircleIcon className="mr-1 -mt-1 inline h-5 w-5 text-slate-500 group-hover:text-slate-800" />
Collect insights
</SelectItem>
<SelectItem className="group font-normal hover:text-slate-900" value="paused">
<PauseCircleIcon className="mr-1 -mt-1 inline h-5 w-5 text-slate-500 group-hover:text-slate-800" />
Pause Survey
</SelectItem>
<SelectItem className="group font-normal hover:text-slate-900" value="completed">
<CheckCircleIcon className="mr-1 -mt-1 inline h-5 w-5 text-slate-500 group-hover:text-slate-800" />
Complete Survey
</SelectItem>
</SelectContent>
</Select>
)}
</>
);
}

View File

@@ -5,7 +5,6 @@ const { createId } = require("@paralleldrive/cuid2");
const nextConfig = {
experimental: {
appDir: true,
serverComponentsExternalPackages: ["@tremor/react"],
},
output: "standalone",
transpilePackages: ["@formbricks/database", "@formbricks/ee", "@formbricks/ui", "@formbricks/lib"],

View File

@@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...pr
return (
<input
className={cn(
"focus:border-brand flex h-10 w-full rounded-md border border-slate-300 bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"focus:border-brand flex h-10 w-full rounded-md border border-slate-300 bg-transparent py-2 px-3 text-sm text-slate-800 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-500 dark:text-slate-300",
className
)}
ref={ref}

View File

@@ -12,7 +12,7 @@ const Label = React.forwardRef<
<LabelPrimitive.Root
ref={ref}
className={cn(
"text-sm font-medium leading-none text-slate-800 disabled:opacity-70 peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
"text-sm font-medium leading-none text-slate-800 disabled:opacity-70 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-slate-400 dark:peer-disabled:opacity-70",
className
)}
{...props}

View File

@@ -37,7 +37,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
"animate-in fade-in-80 relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-slate-50 text-slate-700 shadow-md ",
"animate-in fade-in-80 relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-slate-50 text-slate-700 shadow-md dark:text-slate-300 ",
className
)}
{...props}>
@@ -53,7 +53,7 @@ const SelectLabel = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pr-2 pl-8 text-sm font-semibold text-slate-900 ", className)}
className={cn("py-1.5 pr-2 pl-8 text-sm font-semibold text-slate-900 dark:text-slate-200 ", className)}
{...props}
/>
));
@@ -66,7 +66,7 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm font-medium outline-none hover:text-slate-500 focus:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 ",
"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pr-2 pl-2 text-sm font-medium outline-none hover:text-slate-500 focus:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 ",
className
)}
{...props}>

231
pnpm-lock.yaml generated
View File

@@ -70,7 +70,7 @@ importers:
dependencies:
'@docsearch/react':
specifier: ^3.3.3
version: 3.3.3(@algolia/client-search@4.14.2)(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
version: 3.3.3(@algolia/client-search@4.14.2)(@types/react@18.0.33)(react-dom@18.2.0)(react@18.2.0)
'@formbricks/ui':
specifier: workspace:*
version: link:../../packages/ui
@@ -78,8 +78,8 @@ importers:
specifier: ^1.7.13
version: 1.7.13(react-dom@18.2.0)(react@18.2.0)
'@heroicons/react':
specifier: ^2.0.16
version: 2.0.16(react@18.2.0)
specifier: ^2.0.17
version: 2.0.17(react@18.2.0)
'@mapbox/rehype-prism':
specifier: ^0.8.0
version: 0.8.0
@@ -108,8 +108,8 @@ importers:
specifier: ^3.7.2
version: 3.7.2(next@13.2.4)(react-dom@18.2.0)(react@18.2.0)
next-sitemap:
specifier: ^4.0.5
version: 4.0.5(@next/env@13.2.4)(next@13.2.4)
specifier: ^4.0.6
version: 4.0.6(@next/env@13.2.4)(next@13.2.4)
prism-react-renderer:
specifier: ^1.3.5
version: 1.3.5(react@18.2.0)
@@ -123,8 +123,8 @@ importers:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
react-hook-form:
specifier: ^7.43.5
version: 7.43.5(react@18.2.0)
specifier: ^7.43.9
version: 7.43.9(react@18.2.0)
react-responsive-embed:
specifier: ^2.1.0
version: 2.1.0(prop-types@15.8.1)(react@18.2.0)
@@ -132,36 +132,33 @@ importers:
specifier: ^3.0.1
version: 3.0.1
sharp:
specifier: ^0.31.3
version: 0.31.3
specifier: ^0.32.0
version: 0.32.0
devDependencies:
'@tailwindcss/forms':
specifier: ^0.5.3
version: 0.5.3(tailwindcss@3.2.7)
version: 0.5.3(tailwindcss@3.3.1)
'@tailwindcss/typography':
specifier: ^0.5.9
version: 0.5.9(tailwindcss@3.2.7)
version: 0.5.9(tailwindcss@3.3.1)
'@types/node':
specifier: 18.15.3
version: 18.15.3
specifier: 18.15.11
version: 18.15.11
'@types/prismjs':
specifier: ^1.26.0
version: 1.26.0
'@types/react':
specifier: 18.0.28
version: 18.0.28
specifier: 18.0.33
version: 18.0.33
'@types/react-dom':
specifier: 18.0.11
version: 18.0.11
'@types/react-responsive-embed':
specifier: ^2.1.0
version: 2.1.0
autoprefixer:
specifier: ^10.4.14
version: 10.4.14(postcss@8.4.21)
eslint:
specifier: 8.36.0
version: 8.36.0
specifier: 8.37.0
version: 8.37.0
eslint-config-formbricks:
specifier: workspace:*
version: link:../../packages/eslint-config-formbricks
@@ -169,11 +166,11 @@ importers:
specifier: ^8.4.21
version: 8.4.21
tailwindcss:
specifier: ^3.2.7
version: 3.2.7(postcss@8.4.21)
specifier: ^3.3.1
version: 3.3.1(postcss@8.4.21)
typescript:
specifier: 4.9.5
version: 4.9.5
specifier: 5.0.3
version: 5.0.3
apps/web:
dependencies:
@@ -2310,7 +2307,7 @@ packages:
resolution: {integrity: sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==}
dev: false
/@docsearch/react@3.3.3(@algolia/client-search@4.14.2)(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
/@docsearch/react@3.3.3(@algolia/client-search@4.14.2)(@types/react@18.0.33)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==}
peerDependencies:
'@types/react': '>= 16.8.0 < 19.0.0'
@@ -2327,7 +2324,7 @@ packages:
'@algolia/autocomplete-core': 1.7.4
'@algolia/autocomplete-preset-algolia': 1.7.4(@algolia/client-search@4.14.2)(algoliasearch@4.14.2)
'@docsearch/css': 3.3.3
'@types/react': 18.0.28
'@types/react': 18.0.33
algoliasearch: 4.14.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -2579,7 +2576,7 @@ packages:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
eslint: 8.36.0
eslint-visitor-keys: 3.3.0
eslint-visitor-keys: 3.4.0
/@eslint-community/eslint-utils@4.2.0(eslint@8.37.0):
resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==}
@@ -2617,7 +2614,7 @@ packages:
dependencies:
ajv: 6.12.6
debug: 4.3.4
espree: 9.5.0
espree: 9.5.1
globals: 13.19.0
ignore: 5.2.1
import-fresh: 3.3.0
@@ -2634,7 +2631,7 @@ packages:
dependencies:
ajv: 6.12.6
debug: 4.3.4
espree: 9.5.0
espree: 9.5.1
globals: 13.19.0
ignore: 5.2.1
import-fresh: 3.3.0
@@ -2645,22 +2642,6 @@ packages:
- supports-color
dev: true
/@eslint/eslintrc@2.0.1:
resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
espree: 9.5.0
globals: 13.19.0
ignore: 5.2.1
import-fresh: 3.3.0
js-yaml: 4.1.0
minimatch: 3.1.2
strip-json-comments: 3.1.1
transitivePeerDependencies:
- supports-color
/@eslint/eslintrc@2.0.2:
resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2733,6 +2714,14 @@ packages:
react: 18.2.0
dev: false
/@heroicons/react@2.0.17(react@18.2.0):
resolution: {integrity: sha512-90GMZktkA53YbNzHp6asVEDevUQCMtxWH+2UK2S8OpnLEu7qckTJPhNxNQG52xIR1WFTwFqtH6bt7a60ZNcLLA==}
peerDependencies:
react: '>= 16'
dependencies:
react: 18.2.0
dev: false
/@humanwhocodes/config-array@0.11.7:
resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==}
engines: {node: '>=10.10.0'}
@@ -3102,7 +3091,7 @@ packages:
react: '>=16'
dependencies:
'@types/mdx': 2.0.3
'@types/react': 18.0.32
'@types/react': 18.0.33
react: 18.2.0
dev: false
@@ -4399,16 +4388,13 @@ packages:
tailwindcss: 3.3.0(postcss@8.4.21)
dev: true
/@tailwindcss/typography@0.5.9(tailwindcss@3.2.7):
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==}
/@tailwindcss/forms@0.5.3(tailwindcss@3.3.1):
resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.2.7(postcss@8.4.21)
mini-svg-data-uri: 1.4.4
tailwindcss: 3.3.1(postcss@8.4.21)
dev: true
/@tailwindcss/typography@0.5.9(tailwindcss@3.3.0):
@@ -4423,6 +4409,18 @@ packages:
tailwindcss: 3.3.0(postcss@8.4.21)
dev: true
/@tailwindcss/typography@0.5.9(tailwindcss@3.3.1):
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.3.1(postcss@8.4.21)
dev: true
/@tootallnate/once@1.1.2:
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'}
@@ -4524,7 +4522,7 @@ packages:
resolution: {integrity: sha512-xryQlOEIe1TduDWAOphR0ihfebKFSWOXpIsk+70JskCfRfW+xALdnJ0r1ZOTo85F9Qsjk6vtlU7edTYHbls9tA==}
dependencies:
'@types/cheerio': 0.22.31
'@types/react': 18.0.32
'@types/react': 18.0.33
dev: true
/@types/eslint-scope@3.7.4:
@@ -4590,7 +4588,7 @@ packages:
/@types/hoist-non-react-statics@3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
'@types/react': 18.0.32
'@types/react': 18.0.33
hoist-non-react-statics: 3.3.2
dev: false
@@ -4677,10 +4675,6 @@ packages:
/@types/node@18.15.11:
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
/@types/node@18.15.3:
resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==}
dev: true
/@types/normalize-package-data@2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
dev: true
@@ -4715,29 +4709,23 @@ packages:
/@types/react-dom@18.0.10:
resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==}
dependencies:
'@types/react': 18.0.32
'@types/react': 18.0.33
dev: true
/@types/react-dom@18.0.11:
resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
dependencies:
'@types/react': 18.0.32
'@types/react': 18.0.33
/@types/react-redux@7.1.25:
resolution: {integrity: sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 18.0.32
'@types/react': 18.0.33
hoist-non-react-statics: 3.3.2
redux: 4.2.1
dev: false
/@types/react-responsive-embed@2.1.0:
resolution: {integrity: sha512-VZ921rT7Kf5Zq2GyUxaFXWXQd/YqSMwpwOO5BRJJyzcZoH34YbeX442Dk/nDwXL5QQkz1Zp7hmD80Ris1OHPCA==}
dependencies:
'@types/react': 18.0.32
dev: true
/@types/react@18.0.27:
resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==}
dependencies:
@@ -4746,13 +4734,6 @@ packages:
csstype: 3.1.1
dev: true
/@types/react@18.0.28:
resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
csstype: 3.1.1
/@types/react@18.0.29:
resolution: {integrity: sha512-wXHktgUABxplw1+UnljseDq4+uztQyp2tlWZRIxHlpchsCFqiYkvaDS8JR7eKOQm8wziTH/el5qL7D6gYNkYcw==}
dependencies:
@@ -4768,8 +4749,8 @@ packages:
'@types/scheduler': 0.16.2
csstype: 3.1.1
/@types/react@18.0.32:
resolution: {integrity: sha512-gYGXdtPQ9Cj0w2Fwqg5/ak6BcK3Z15YgjSqtyDizWUfx7mQ8drs0NBUzRRsAdoFVTO8kJ8L2TL8Skm7OFPnLUw==}
/@types/react@18.0.33:
resolution: {integrity: sha512-sHxzVxeanvQyQ1lr8NSHaj0kDzcNiGpILEVt69g9S31/7PfMvNCKLKcsHw4lYKjs3cGNJjXSP4mYzX43QlnjNA==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
@@ -6662,10 +6643,6 @@ packages:
lodash.uniq: 4.5.0
dev: true
/caniuse-lite@1.0.30001435:
resolution: {integrity: sha512-kdCkUTjR+v4YAJelyiDTqiu82BDr4W4CP5sgTA0ZBmqn30XfS2ZghPLMowik9TPhS+psWJiUNxsqLyurDbmutA==}
dev: false
/caniuse-lite@1.0.30001466:
resolution: {integrity: sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==}
@@ -9271,6 +9248,7 @@ packages:
/eslint-visitor-keys@3.3.0:
resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false
/eslint-visitor-keys@3.4.0:
resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==}
@@ -9390,9 +9368,9 @@ packages:
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
eslint-utils: 3.0.0(eslint@8.33.0)
eslint-visitor-keys: 3.3.0
espree: 9.4.1
esquery: 1.4.0
eslint-visitor-keys: 3.4.0
espree: 9.5.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
@@ -9428,7 +9406,7 @@ packages:
dependencies:
'@eslint-community/eslint-utils': 4.2.0(eslint@8.36.0)
'@eslint-community/regexpp': 4.4.0
'@eslint/eslintrc': 2.0.1
'@eslint/eslintrc': 2.0.2
'@eslint/js': 8.36.0
'@humanwhocodes/config-array': 0.11.8
'@humanwhocodes/module-importer': 1.0.1
@@ -9440,8 +9418,8 @@ packages:
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
eslint-visitor-keys: 3.3.0
espree: 9.5.0
eslint-visitor-keys: 3.4.0
espree: 9.5.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@@ -9537,15 +9515,8 @@ packages:
dependencies:
acorn: 8.8.1
acorn-jsx: 5.3.2(acorn@8.8.1)
eslint-visitor-keys: 3.3.0
/espree@9.5.0:
resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
acorn: 8.8.1
acorn-jsx: 5.3.2(acorn@8.8.1)
eslint-visitor-keys: 3.3.0
eslint-visitor-keys: 3.4.0
dev: false
/espree@9.5.1:
resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==}
@@ -9565,6 +9536,7 @@ packages:
engines: {node: '>=0.10'}
dependencies:
estraverse: 5.3.0
dev: false
/esquery@1.5.0:
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
@@ -13996,8 +13968,8 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/next-sitemap@4.0.5(@next/env@13.2.4)(next@13.2.4):
resolution: {integrity: sha512-kIABX4n8wJqY2sttRjivBF2r1MgQ1pg12y87qGT+5meippq0ivxaNSRldMDOj8pIGhXW6uFAsCxyH/JftqkwWQ==}
/next-sitemap@4.0.6(@next/env@13.2.4)(next@13.2.4):
resolution: {integrity: sha512-pZ9tynYe6mRR189qZqcOlWVgM1Gxo07BJQW0AjerKmLwQOt+6FQMdaDgifgCt6jDT3Y3EG/+NUDDZRcd0gbPkA==}
engines: {node: '>=14.18'}
hasBin: true
peerDependencies:
@@ -14030,7 +14002,7 @@ packages:
dependencies:
'@next/env': 13.1.6
'@swc/helpers': 0.4.14
caniuse-lite: 1.0.30001435
caniuse-lite: 1.0.30001466
postcss: 8.4.14
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -14114,8 +14086,8 @@ packages:
semver: 7.3.8
dev: false
/node-addon-api@5.1.0:
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
/node-addon-api@6.0.0:
resolution: {integrity: sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA==}
dev: false
/node-fetch@2.6.7:
@@ -16199,8 +16171,8 @@ packages:
scheduler: 0.23.0
dev: false
/react-hook-form@7.43.5(react@18.2.0):
resolution: {integrity: sha512-YcaXhuFHoOPipu5pC7ckxrLrialiOcU91pKu8P+isAcXZyMgByUK9PkI9j5fENO4+6XU5PwWXRGMIFlk9u9UBQ==}
/react-hook-form@7.43.8(react@18.2.0):
resolution: {integrity: sha512-BQm+Ge5KjTk1EchDBRhdP8Pkb7MArO2jFF+UWYr3rtvh6197khi22uloLqlWeuY02ItlCzPunPsFt1/q9wQKnw==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
@@ -16208,8 +16180,8 @@ packages:
react: 18.2.0
dev: false
/react-hook-form@7.43.8(react@18.2.0):
resolution: {integrity: sha512-BQm+Ge5KjTk1EchDBRhdP8Pkb7MArO2jFF+UWYr3rtvh6197khi22uloLqlWeuY02ItlCzPunPsFt1/q9wQKnw==}
/react-hook-form@7.43.9(react@18.2.0):
resolution: {integrity: sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
@@ -17232,14 +17204,14 @@ packages:
kind-of: 6.0.3
dev: true
/sharp@0.31.3:
resolution: {integrity: sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==}
/sharp@0.32.0:
resolution: {integrity: sha512-yLAypVcqj1toSAqRSwbs86nEzfyZVDYqjuUX8grhFpeij0DDNagKJXELS/auegDBRDg1XBtELdOGfo2X1cCpeA==}
engines: {node: '>=14.15.0'}
requiresBuild: true
dependencies:
color: 4.2.3
detect-libc: 2.0.1
node-addon-api: 5.1.0
node-addon-api: 6.0.0
prebuild-install: 7.1.1
semver: 7.3.8
simple-get: 4.0.1
@@ -18134,6 +18106,41 @@ packages:
- ts-node
dev: true
/tailwindcss@3.3.1(postcss@8.4.21):
resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==}
engines: {node: '>=12.13.0'}
hasBin: true
peerDependencies:
postcss: ^8.0.9
dependencies:
arg: 5.0.2
chokidar: 3.5.3
color-name: 1.1.4
didyoumean: 1.2.2
dlv: 1.1.3
fast-glob: 3.2.12
glob-parent: 6.0.2
is-glob: 4.0.3
jiti: 1.18.2
lilconfig: 2.0.6
micromatch: 4.0.5
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.0
postcss: 8.4.21
postcss-import: 14.1.0(postcss@8.4.21)
postcss-js: 4.0.0(postcss@8.4.21)
postcss-load-config: 3.1.4(postcss@8.4.21)
postcss-nested: 6.0.0(postcss@8.4.21)
postcss-selector-parser: 6.0.11
postcss-value-parser: 4.2.0
quick-lru: 5.1.1
resolve: 1.22.1
sucrase: 3.29.0
transitivePeerDependencies:
- ts-node
dev: true
/tapable@1.1.3:
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
engines: {node: '>=6'}
@@ -18888,6 +18895,12 @@ packages:
engines: {node: '>=12.20'}
hasBin: true
/typescript@5.0.3:
resolution: {integrity: sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==}
engines: {node: '>=12.20'}
hasBin: true
dev: true
/uglify-js@3.4.10:
resolution: {integrity: sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==}
engines: {node: '>=0.8.0'}