diff --git a/README.md b/README.md index b4aa37ff33..edf7fde75e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

snoopForms

- Finally, good open-source forms! + Finally, good open source forms!
Website & Hosted version | Join Discord community

diff --git a/apps/formbricks-com/.eslintrc.js b/apps/formbricks-com/.eslintrc.js new file mode 100644 index 0000000000..bcf4aad3a3 --- /dev/null +++ b/apps/formbricks-com/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["formbricks"], +}; diff --git a/apps/formbricks-com/.gitignore b/apps/formbricks-com/.gitignore new file mode 100644 index 0000000000..4f360c89d2 --- /dev/null +++ b/apps/formbricks-com/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +.vscode diff --git a/apps/formbricks-com/LICENSE b/apps/formbricks-com/LICENSE new file mode 100644 index 0000000000..1e6289856b --- /dev/null +++ b/apps/formbricks-com/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/apps/formbricks-com/README.md b/apps/formbricks-com/README.md new file mode 100644 index 0000000000..c87e0421d2 --- /dev/null +++ b/apps/formbricks-com/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/formbricks-com/components/docs/Layout.tsx b/apps/formbricks-com/components/docs/Layout.tsx new file mode 100644 index 0000000000..b27db230d2 --- /dev/null +++ b/apps/formbricks-com/components/docs/Layout.tsx @@ -0,0 +1,153 @@ +import { useCallback, useEffect, useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import clsx from "clsx"; + +import { Hero } from "@/components/shared/Hero"; +import { Logo, Logomark } from "@/components/shared/Logo"; +import { MobileNavigation } from "@/components/shared/MobileNavigation"; +import { Navigation } from "@/components/shared/Navigation"; +import { Prose } from "@/components/shared/Prose"; +import { Search } from "@/components/shared/Search"; +import { ThemeSelector } from "@/components/shared/ThemeSelector"; +import navigation from "@/lib/docsNavigation"; +import Button from "../shared/Button"; + +function GitHubIcon(props: any) { + return ( + + ); +} + +function Header({ navigation }: any) { + const router = useRouter(); + let [isScrolled, setIsScrolled] = useState(false); + + useEffect(() => { + function onScroll() { + setIsScrolled(window.scrollY > 0); + } + onScroll(); + window.addEventListener("scroll", onScroll, { passive: true }); + return () => { + window.removeEventListener("scroll", onScroll); + }; + }, []); + + return ( +
+
+ +
+
+ + + + +
+ +
+
+
+ +
+
+ + + + +
+
+ ); +} + +interface LayoutProps { + children: React.ReactNode; + meta: { + title: string; + }; +} + +export function Layout({ children, meta }: LayoutProps) { + let router = useRouter(); + let isHomePage = router.pathname === "/"; + let allLinks = navigation.flatMap((section) => section.links); + let linkIndex = allLinks.findIndex((link) => link.href === router.pathname); + let previousPage = allLinks[linkIndex - 1]; + let nextPage = allLinks[linkIndex + 1]; + let section = navigation.find((section) => section.links.find((link) => link.href === router.pathname)); + + return ( + <> +
+ + {isHomePage && } + +
+
+
+
+
+
+ +
+
+
+
+ {(meta.title || section) && ( +
+ {section &&

{section.title}

} + {meta.title && ( +

+ {meta.title} +

+ )} +
+ )} + {children} +
+
+ {previousPage && ( +
+
Previous
+
+ + {previousPage.title} + +
+
+ )} + {nextPage && ( +
+
Next
+
+ + {nextPage.title} + +
+
+ )} +
+
+
+ + ); +} diff --git a/apps/formbricks-com/components/docs/MobileNavigation.jsx b/apps/formbricks-com/components/docs/MobileNavigation.jsx new file mode 100644 index 0000000000..776400817a --- /dev/null +++ b/apps/formbricks-com/components/docs/MobileNavigation.jsx @@ -0,0 +1,69 @@ +import { useEffect, useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { Dialog } from "@headlessui/react"; + +import { Logomark } from "@/components/shared/Logo"; +import { Navigation } from "@/components/shared/Navigation"; + +function MenuIcon(props) { + return ( + + ); +} + +function CloseIcon(props) { + return ( + + ); +} + +export function MobileNavigation({ navigation }) { + let router = useRouter(); + let [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + if (!isOpen) return; + + function onRouteChange() { + setIsOpen(false); + } + + router.events.on("routeChangeComplete", onRouteChange); + router.events.on("routeChangeError", onRouteChange); + + return () => { + router.events.off("routeChangeComplete", onRouteChange); + router.events.off("routeChangeError", onRouteChange); + }; + }, [router, isOpen]); + + return ( + <> + + + +
+ + + + +
+ +
+
+ + ); +} diff --git a/apps/formbricks-com/components/home/Features.tsx b/apps/formbricks-com/components/home/Features.tsx new file mode 100644 index 0000000000..0290553856 --- /dev/null +++ b/apps/formbricks-com/components/home/Features.tsx @@ -0,0 +1,107 @@ +import { + PlusIcon, + SquaresPlusIcon, + ChartBarIcon, + ArrowTrendingUpIcon, + DocumentPlusIcon, + RectangleGroupIcon, +} from "@heroicons/react/24/outline"; +import clsx from "clsx"; +import HeadingCentered from "../shared/HeadingCenetered"; + +const features = [ + { + id: "formCreation", + name: "Fast Form Creation", + description: "Build complex forms with our React Lib. Our data pipes also work with any other form.", + icon: PlusIcon, + }, + { + id: "dataPipelines", + name: "Data Pipelines", + description: "Save your data where you need it. Use webhooks or pre-built integrations.", + icon: SquaresPlusIcon, + }, + { + id: "dataInsights", + name: "Powerful Data Insights", + description: "View and manage your results quicker. Handle submissions in our dahsboard.", + icon: ChartBarIcon, + }, + { + id: "nocodeBuilder", + name: "No-Code Builder", + description: "Let your operators create and change forms. Stick with React to style and embed forms.", + icon: RectangleGroupIcon, + comingSoon: true, + }, + { + id: "analytics", + name: "Built-in Analytics", + description: "Opening rate, drop-offs, conversions. Use privacy-first analytics out of the box.", + icon: ArrowTrendingUpIcon, + comingSoon: true, + }, + { + id: "templates", + name: "Survey Templates", + description: "NPS, CSAT, Employee Surveys. Name your business objective, we have the questions.", + icon: DocumentPlusIcon, + comingSoon: true, + }, +]; +export default function Features() { + return ( +
+
+
+
+
+ + +
    + {features.map((feature) => ( +
  • +
    +
    + +
    +
    +
    +

    {feature.name}

    +
    +
    Description
    +
    {feature.description}
    + {feature.comingSoon && ( +
    + + coming soon + +
    + )} +
    +
    +
  • + ))} +
+
+
+ ); +} diff --git a/apps/formbricks-com/components/home/Hero.tsx b/apps/formbricks-com/components/home/Hero.tsx new file mode 100644 index 0000000000..facba2886f --- /dev/null +++ b/apps/formbricks-com/components/home/Hero.tsx @@ -0,0 +1,28 @@ +import Button from "../shared/Button"; +import HeroAnimation from "../shared/HeroAnimation"; +import HeroTitle from "../shared/HeroTitle"; +import { useRouter } from "next/router"; + +interface Props {} + +export default function Hero({}: Props) { + const router = useRouter(); + return ( +
+ + + + + +
+ ); +} diff --git a/apps/formbricks-com/components/home/Highlights.tsx b/apps/formbricks-com/components/home/Highlights.tsx new file mode 100644 index 0000000000..0ea553b2c5 --- /dev/null +++ b/apps/formbricks-com/components/home/Highlights.tsx @@ -0,0 +1,65 @@ +import Image from "next/image"; +import ImageReactLib from "@/images/react-lib.png"; +import ImageDataPipelines from "@/images/data-pipelines.png"; +import Link from "next/link"; +import Button from "../shared/Button"; +import { useRouter } from "next/router"; + +export default function Highlights({}) { + const router = useRouter(); + return ( + <> +
+
+
+
+

+ Build forms in minutes with our lightweight{" "} + React Form Builder. +

+

+ Loads of question types, validation, multi-page forms, logic jumps, i18n, custom styles - all + the good stuff you want, but don't want to build yourself. +

+

+ Build exactly the form you want in a fraction of the + time. +

+
+ +
+
+ react library +
+
+
+
+
+
+ react library +
+

+ API all the way +

+

+ Your form looks perfect? Time to build integrations... +

+

+ Or use our prebuilt data pipelines. Pipe submissions + right into your database. Set up webhooks, email notifications and 3rd party integrations in + our webUI. +

+
+ +
+
+
+
+
+ + ); +} diff --git a/apps/formbricks-com/components/shared/Button.tsx b/apps/formbricks-com/components/shared/Button.tsx new file mode 100644 index 0000000000..eb31454827 --- /dev/null +++ b/apps/formbricks-com/components/shared/Button.tsx @@ -0,0 +1,149 @@ +import Link, { LinkProps } from "next/link"; +import React, { forwardRef } from "react"; +import clsx from "clsx"; + +type SVGComponent = React.FunctionComponent>; + +export type ButtonBaseProps = { + variant?: "highlight" | "primary" | "secondary" | "minimal" | "warn" | "alert"; + size?: "base" | "sm" | "lg" | "fab" | "icon"; + loading?: boolean; + disabled?: boolean; + onClick?: (event: React.MouseEvent) => void; + StartIcon?: SVGComponent; + startIconClassName?: string; + EndIcon?: SVGComponent; + endIconClassName?: string; + shallow?: boolean; +}; +export type ButtonProps = ButtonBaseProps & + ( + | (Omit & LinkProps) + | (Omit & { href?: never }) + ); + +export const Button = forwardRef(function Button( + props: ButtonProps, + forwardedRef +) { + const { + loading = false, + variant = "primary", + size = "base", + StartIcon, + startIconClassName, + endIconClassName, + EndIcon, + shallow, + // attributes propagated from `HTMLAnchorProps` or `HTMLButtonProps` + ...passThroughProps + } = props; + // Buttons are **always** disabled if we're in a `loading` state + const disabled = props.disabled || loading; + + // If pass an `href`-attr is passed it's ``, otherwise it's a ` +
+
+

Cloud

+

Use our free managed service.

+ +
+
+
+ + ); +} diff --git a/apps/formbricks-com/components/shared/Callout.tsx b/apps/formbricks-com/components/shared/Callout.tsx new file mode 100644 index 0000000000..185043ae28 --- /dev/null +++ b/apps/formbricks-com/components/shared/Callout.tsx @@ -0,0 +1,41 @@ +import clsx from "clsx"; + +import { Icon } from "@/components/shared/Icon"; + +const styles = { + note: { + container: "bg-sky-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10", + title: "text-sky-900 dark:text-sky-400", + body: "text-sky-800 [--tw-prose-background:theme(colors.sky.50)] prose-a:text-sky-900 prose-code:text-sky-900 dark:text-slate-300 dark:prose-code:text-slate-300", + }, + warning: { + container: "bg-amber-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10", + title: "text-amber-900 dark:text-amber-500", + body: "text-amber-800 [--tw-prose-underline:theme(colors.amber.400)] [--tw-prose-background:theme(colors.amber.50)] prose-a:text-amber-900 prose-code:text-amber-900 dark:text-slate-300 dark:[--tw-prose-underline:theme(colors.sky.700)] dark:prose-code:text-slate-300", + }, +}; + +const icons = { + note: (props: any) => , + warning: (props: any) => , +}; + +interface CalloutProps { + type: "note" | "warning"; + title: string; + children: React.ReactNode; +} + +export function Callout({ type = "note", title, children }: CalloutProps) { + let IconComponent = icons[type]; + + return ( +
+ +
+

{title}

+
{children}
+
+
+ ); +} diff --git a/apps/formbricks-com/components/shared/Card.jsx b/apps/formbricks-com/components/shared/Card.jsx new file mode 100644 index 0000000000..0d6e2f0739 --- /dev/null +++ b/apps/formbricks-com/components/shared/Card.jsx @@ -0,0 +1,74 @@ +import Link from "next/link"; +import clsx from "clsx"; + +function ChevronRightIcon(props) { + return ( + + ); +} + +export function Card({ as: Component = "div", className, children }) { + return ( + {children} + ); +} + +Card.Link = function CardLink({ children, ...props }) { + return ( + <> +
+ + + {children} + + + ); +}; + +Card.Title = function CardTitle({ as: Component = "h2", href, children }) { + return ( + + {href ? {children} : children} + + ); +}; + +Card.Description = function CardDescription({ children }) { + return

{children}

; +}; + +Card.Cta = function CardCta({ children }) { + return ( + + ); +}; + +Card.Eyebrow = function CardEyebrow({ + as: Component = "p", + decorate = false, + className, + children, + ...props +}) { + return ( + + {decorate && ( + + ); +}; diff --git a/apps/formbricks-com/components/shared/FeatureCards.tsx b/apps/formbricks-com/components/shared/FeatureCards.tsx new file mode 100644 index 0000000000..5bd48d6161 --- /dev/null +++ b/apps/formbricks-com/components/shared/FeatureCards.tsx @@ -0,0 +1,46 @@ +import clsx from "clsx"; + +interface Props { + features: any[]; +} + +export default function FeatureHighlights({ features }: Props) { + return ( +
    + {features.map((feature: any) => ( +
  • +
    +
    + +
    +
    +
    +

    {feature.name}

    +
    +
    Description
    +
    {feature.description}
    + {feature.comingSoon && ( +
    + + coming soon + +
    + )} +
    +
    +
  • + ))} +
+ ); +} diff --git a/apps/formbricks-com/components/shared/FeatureHighlight.tsx b/apps/formbricks-com/components/shared/FeatureHighlight.tsx new file mode 100644 index 0000000000..9345289428 --- /dev/null +++ b/apps/formbricks-com/components/shared/FeatureHighlight.tsx @@ -0,0 +1,41 @@ +import Button from "./Button"; +import { useRouter } from "next/router"; +import clsx from "clsx"; + +interface Props { + featureTitle: string; + text: string; + img: React.ReactNode; + isImgLeft?: boolean; + cta?: string; + href?: string; +} + +export default function FeatureHighlights({ featureTitle, text, img, isImgLeft, cta, href }: Props) { + const router = useRouter(); + + return ( +
+
+
+
+

+ {featureTitle} +

+
+ {text} +
+
+ {cta && href && ( + + )} +
+
+ {img} +
+
+
+ ); +} diff --git a/apps/formbricks-com/components/shared/Fence.jsx b/apps/formbricks-com/components/shared/Fence.jsx new file mode 100644 index 0000000000..b3befc495e --- /dev/null +++ b/apps/formbricks-com/components/shared/Fence.jsx @@ -0,0 +1,30 @@ +import { Fragment } from 'react' +import Highlight, { defaultProps } from 'prism-react-renderer' + +export function Fence({ children, language }) { + return ( + + {({ className, style, tokens, getTokenProps }) => ( +
+          
+            {tokens.map((line, lineIndex) => (
+              
+                {line
+                  .filter((token) => !token.empty)
+                  .map((token, tokenIndex) => (
+                    
+                  ))}
+                {'\n'}
+              
+            ))}
+          
+        
+ )} +
+ ) +} diff --git a/apps/formbricks-com/components/shared/Footer.tsx b/apps/formbricks-com/components/shared/Footer.tsx new file mode 100644 index 0000000000..ae8a65659a --- /dev/null +++ b/apps/formbricks-com/components/shared/Footer.tsx @@ -0,0 +1,165 @@ +import Link from "next/link"; +import clsx from "clsx"; +import { Logo } from "./Logo"; + +const navigation = { + creation: [ + { name: "React Form Builder", href: "/react-form-builder", status: true }, + { name: "Visual Builder", href: "#", status: false }, + { name: "Templates", href: "#", status: false }, + ], + pipelines: [ + { name: "Core API", href: "/core-api", status: true }, + { name: "Webhooks", href: "/webhooks", status: true }, + { name: "Email", href: "/email", status: true }, + { name: "Integrations", href: "#", status: false }, + ], + insights: [ + { name: "Form HQ", href: "/form-hq", status: true }, + { name: "Reports", href: "#", status: false }, + ], + legal: [ + { name: "Community", href: "/community" }, + { name: "Docs", href: "/docs" }, + { name: "Blog", href: "/blog" }, + ], + social: [ + { + name: "Twitter", + href: "https://twitter.com/formbricks", + icon: (props: any) => ( + + + + ), + }, + { + name: "GitHub", + href: "https://github.com/formbricks/formbricks", + icon: (props: any) => ( + + + + ), + }, + ], +}; + +export default function Footer() { + return ( +
+ +
+
+
+ +

+ The Open Source Forms & Survey Toolbox +

+
+ {navigation.social.map((item) => ( + + {item.name} +
+
+
+
+
+

Form Creation

+
    + {navigation.creation.map((item) => ( +
  • + + {item.name} + +
  • + ))} +
+
+
+

Data Pipelines

+
    + {navigation.pipelines.map((item) => ( +
  • + + {item.name} + +
  • + ))} +
+
+
+
+
+

Data Insights

+
    + {navigation.insights.map((item) => ( +
  • + + {item.name} + +
  • + ))} +
+
+
+

Other

+
    + {navigation.legal.map((item) => ( +
  • + + {item.name} + +
  • + ))} +
+
+
+
+
+
+

+ © 2022 Form Bricks, Inc. All rights reserved. +

+
+
+
+ ); +} diff --git a/apps/formbricks-com/components/shared/Header.tsx b/apps/formbricks-com/components/shared/Header.tsx new file mode 100644 index 0000000000..d1d362e18a --- /dev/null +++ b/apps/formbricks-com/components/shared/Header.tsx @@ -0,0 +1,407 @@ +import { Popover, Transition } from "@headlessui/react"; +import { ChevronDownIcon } from "@heroicons/react/20/solid"; +import { + Bars3Icon, + BoltIcon, + ClipboardDocumentListIcon, + CodeBracketSquareIcon, + CpuChipIcon, + CursorArrowRaysIcon, + CursorArrowRippleIcon, + DocumentChartBarIcon, + EnvelopeIcon, + SquaresPlusIcon, + XMarkIcon, +} from "@heroicons/react/24/outline"; +import clsx from "clsx"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { Fragment } from "react"; +import Button from "./Button"; +import { Logo } from "./Logo"; +import { ThemeSelector } from "./ThemeSelector"; + +const creation = [ + { + name: "React Library", + description: "Build forms with React.js", + href: "/react-form-builder", + icon: CodeBracketSquareIcon, + status: true, + }, + { + name: "No Code Builder", + description: "Notion-like visual builder", + href: "#", + icon: CursorArrowRaysIcon, + status: false, + }, + { + name: "Templates", + description: "CSAT, PMF survey, etc.", + href: "#", + icon: ClipboardDocumentListIcon, + status: false, + }, +]; + +const pipes = [ + { + name: "Core API", + description: "The OS form engine", + href: "/core-api", + icon: CpuChipIcon, + status: true, + }, + { + name: "Webhooks", + description: "Send JSON anywhere", + href: "/webhooks", + icon: BoltIcon, + status: true, + }, + { + name: "Email", + description: "Send data and notifications", + href: "/email", + icon: EnvelopeIcon, + status: true, + }, + { + name: "Integrations", + description: "Connect with 100+ apps", + href: "/integrations", + icon: SquaresPlusIcon, + status: false, + }, +]; + +const insights = [ + { + name: "Form HQ", + description: "Manage submissions easily", + href: "/form-hq", + icon: CursorArrowRippleIcon, + cat: "insights", + status: true, + }, + { + name: "Reports", + description: "Based on Templates", + href: "#", + icon: DocumentChartBarIcon, + cat: "insights", + status: false, + }, +]; + +export default function Header() { + const router = useRouter(); + return ( + +
+
+ + Formbricks + + +
+
+ + Open menu + +
+ + + {({ open }) => ( + <> + + Bricks + + + + +
+
+
+

Form Creation

+ {creation.map((brick) => ( + +
+
+
+

+ {brick.name} +

+

{brick.description}

+
+ + ))} +
+
+

Data Pipelines

+ {pipes.map((brick) => ( + +
+
+
+

+ {brick.name} +

+

{brick.description}

+
+ + ))} +
+
+

Data Insights

+ {insights.map((brick) => ( + +
+
+
+

+ {brick.name} +

+

{brick.description}

+
+ + ))} +
+
+
+
+
+ + )} +
+ + + Community + + + Blog + + + Docs + +
+
+ + + +
+
+ + + +
+
+
+
+ +
+
+ + Close menu + +
+
+ + +
+
+
+ Community + + Blog + + Documentation +
+
+ + +
+
+
+
+
+
+ ); +} + +function GitHubIcon(props: any) { + return ( + + ); +} diff --git a/apps/formbricks-com/components/shared/HeadingCenetered.tsx b/apps/formbricks-com/components/shared/HeadingCenetered.tsx new file mode 100644 index 0000000000..e3bd0a65fb --- /dev/null +++ b/apps/formbricks-com/components/shared/HeadingCenetered.tsx @@ -0,0 +1,22 @@ +import clsx from "clsx"; + +interface Props { + teaser?: string; + heading: string; + subheading?: string; + closer?: boolean; +} + +export default function HeadingCentered({ teaser, heading, subheading, closer }: Props) { + return ( +
+

+ {teaser} +

+

+ {heading} +

+

{subheading}

+
+ ); +} diff --git a/apps/formbricks-com/components/shared/Hero.jsx b/apps/formbricks-com/components/shared/Hero.jsx new file mode 100644 index 0000000000..5f435d2b3d --- /dev/null +++ b/apps/formbricks-com/components/shared/Hero.jsx @@ -0,0 +1,153 @@ +import { Fragment } from "react"; +import Image from "next/image"; +import clsx from "clsx"; +import Highlight, { defaultProps } from "prism-react-renderer"; + +import { Button } from "@/components/shared/Button"; +import { HeroBackground } from "@/components/shared/HeroBackground"; +import blurCyanImage from "@/images/blur-cyan.png"; +import blurIndigoImage from "@/images/blur-indigo.png"; + +const codeLanguage = "javascript"; +const code = `export default { + strategy: 'predictive', + engine: { + cpus: 12, + backups: ['./storage/cache.wtf'], + }, +}`; + +const tabs = [ + { name: "cache-advance.config.js", isActive: true }, + { name: "package.json", isActive: false }, +]; + +function TrafficLightsIcon(props) { + return ( + + ); +} + +export function Hero() { + return ( +
+
+
+
+ +
+

+ Never miss the cache again. +

+

+ Cache every single thing your app could ever do ahead of time, so your code never even has to + run at all. +

+
+ + +
+
+
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+ {tabs.map((tab) => ( +
+
+ {tab.name} +
+
+ ))} +
+
+ + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+                          
+                            {tokens.map((line, lineIndex) => (
+                              
+ {line.map((token, tokenIndex) => ( + + ))} +
+ ))} +
+
+ )} +
+
+
+
+
+
+
+
+
+ ); +} diff --git a/apps/formbricks-com/components/shared/HeroAnimation.tsx b/apps/formbricks-com/components/shared/HeroAnimation.tsx new file mode 100644 index 0000000000..e61764cf1c --- /dev/null +++ b/apps/formbricks-com/components/shared/HeroAnimation.tsx @@ -0,0 +1,28 @@ +import { useEffect, useRef, useState } from "react"; +import type { LottiePlayer } from "lottie-web"; + +export default function HeroAnimation(props: any) { + const ref = useRef(null); + const [lottie, setLottie] = useState(null); + + useEffect(() => { + import("lottie-web").then((Lottie) => setLottie(Lottie.default)); + }, []); + + useEffect(() => { + if (lottie && ref.current) { + const animation = lottie.loadAnimation({ + container: ref.current, + renderer: "svg", + loop: true, + autoplay: true, + // path to your animation file, place it inside public folder + path: "/animations/hero.json", + }); + + return () => animation.destroy(); + } + }, [lottie]); + + return
; +} diff --git a/apps/formbricks-com/components/shared/HeroBackground.jsx b/apps/formbricks-com/components/shared/HeroBackground.jsx new file mode 100644 index 0000000000..486936623e --- /dev/null +++ b/apps/formbricks-com/components/shared/HeroBackground.jsx @@ -0,0 +1,188 @@ +import { useId } from 'react' + +export function HeroBackground(props) { + let id = useId() + + return ( + + ) +} diff --git a/apps/formbricks-com/components/shared/HeroTitle.tsx b/apps/formbricks-com/components/shared/HeroTitle.tsx new file mode 100644 index 0000000000..13898e75b9 --- /dev/null +++ b/apps/formbricks-com/components/shared/HeroTitle.tsx @@ -0,0 +1,25 @@ +interface Props { + headingPt1: string; + headingTeal?: string; + headingPt2?: string; + subheading?: string; + children?: React.ReactNode; +} + +export default function HeroTitle({ headingPt1, headingTeal, headingPt2, subheading, children }: Props) { + return ( +
+

+ {headingPt1}{" "} + + {headingTeal} + {" "} + {headingPt2} +

+

+ {subheading} +

+
{children}
+
+ ); +} diff --git a/apps/formbricks-com/components/shared/Icon.jsx b/apps/formbricks-com/components/shared/Icon.jsx new file mode 100644 index 0000000000..1e99fc032c --- /dev/null +++ b/apps/formbricks-com/components/shared/Icon.jsx @@ -0,0 +1,69 @@ +import { useId } from "react"; +import clsx from "clsx"; + +import { InstallationIcon } from "@/components/shared/icons/InstallationIcon"; +import { LightbulbIcon } from "@/components/shared/icons/LightbulbIcon"; +import { PluginsIcon } from "@/components/shared/icons/PluginsIcon"; +import { PresetsIcon } from "@/components/shared/icons/PresetsIcon"; +import { ThemingIcon } from "@/components/shared/icons/ThemingIcon"; +import { WarningIcon } from "@/components/shared/icons/WarningIcon"; + +const icons = { + installation: InstallationIcon, + presets: PresetsIcon, + plugins: PluginsIcon, + theming: ThemingIcon, + lightbulb: LightbulbIcon, + warning: WarningIcon, +}; + +const iconStyles = { + blue: "[--icon-foreground:theme(colors.slate.900)] [--icon-background:theme(colors.white)]", + amber: "[--icon-foreground:theme(colors.amber.900)] [--icon-background:theme(colors.amber.100)]", +}; + +export function Icon({ color = "blue", icon, className, ...props }) { + let id = useId(); + let IconComponent = icons[icon]; + + return ( + + ); +} + +const gradients = { + blue: [ + { stopColor: "#0EA5E9" }, + { stopColor: "#22D3EE", offset: ".527" }, + { stopColor: "#818CF8", offset: 1 }, + ], + amber: [ + { stopColor: "#FDE68A", offset: ".08" }, + { stopColor: "#F59E0B", offset: ".837" }, + ], +}; + +export function Gradient({ color = "blue", ...props }) { + return ( + + {gradients[color].map((stop, stopIndex) => ( + + ))} + + ); +} + +export function LightMode({ className, ...props }) { + return ; +} + +export function DarkMode({ className, ...props }) { + return ; +} diff --git a/apps/formbricks-com/components/shared/Layout.tsx b/apps/formbricks-com/components/shared/Layout.tsx new file mode 100644 index 0000000000..e610804eb4 --- /dev/null +++ b/apps/formbricks-com/components/shared/Layout.tsx @@ -0,0 +1,22 @@ +import Footer from "./Footer"; +import Header from "./Header"; +import MetaInformation from "./MetaInformation"; + +interface LayoutProps { + children: React.ReactNode; + title: string; + description: string; +} + +export default function Layout({ title, description, children }: LayoutProps) { + return ( + <> + +
+
+ {children} +
+