Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddec12740d | ||
|
|
09356bbded | ||
|
|
ad17ea957d | ||
|
|
6ec3878c77 | ||
|
|
6fd11fb54c | ||
|
|
47605493e9 | ||
|
|
5b273cc194 | ||
|
|
c81bfadb66 | ||
|
|
96ac6a2a6f | ||
|
|
97e5398adf | ||
|
|
29064b3bde | ||
|
|
7abe1d877a | ||
|
|
7eefdd336c | ||
|
|
1a9400139c | ||
|
|
501ce56de6 | ||
|
|
9eb3f2e605 | ||
|
|
1b251b8ad0 | ||
|
|
e7e4406e91 | ||
|
|
f6ea9f6375 | ||
|
|
90eaab9c9e | ||
|
|
9a494a3f8a | ||
|
|
5e6ac0a141 | ||
|
|
d889775c56 | ||
|
|
ce0179a2c7 | ||
|
|
d835e78d92 | ||
|
|
8352d05d04 | ||
|
|
fbc1c74046 | ||
|
|
48bac2128e | ||
|
|
d15d062581 | ||
|
|
72c6ea6f39 | ||
|
|
3b5ab29669 | ||
|
|
793aff4585 | ||
|
|
8734127143 | ||
|
|
d2ee0d6a9a | ||
|
|
c9f5f8cd16 | ||
|
|
392bbfd911 | ||
|
|
a7fd1fc754 | ||
|
|
c118fbb0bd | ||
|
|
d6868c3d40 | ||
|
|
8ef8c93f3d | ||
|
|
324a150a90 | ||
|
|
1ec4dcb02d | ||
|
|
d470b6576f | ||
|
|
3778ed80d4 | ||
|
|
87ee687d51 | ||
|
|
a917668b3a | ||
|
|
e6d091d35e | ||
|
|
7ee8cb98e1 | ||
|
|
3a2c697736 | ||
|
|
b28fad2ffa | ||
|
|
6024cad566 | ||
|
|
9e1a17a3ef | ||
|
|
da998d9820 | ||
|
|
1bcdcee799 | ||
|
|
1c58b0c95c | ||
|
|
0bbb9ad3d7 | ||
|
|
5163f36e53 | ||
|
|
529bf4611c | ||
|
|
29cf117fda | ||
|
|
50435413c3 | ||
|
|
e614e4bd00 | ||
|
|
3c31e5fe84 | ||
|
|
6cce82d9bf | ||
|
|
1bb5fcfcb2 | ||
|
|
4ddb09cd39 | ||
|
|
0a6a12cacb | ||
|
|
76a4d49c2d | ||
|
|
08229c1761 | ||
|
|
a0baa25864 | ||
|
|
bc771bc188 | ||
|
|
957a7b4b69 | ||
|
|
64afd6ed00 | ||
|
|
4f7fb96e3f | ||
|
|
065c0d352c | ||
|
|
418bd956eb | ||
|
|
c510da3879 | ||
|
|
68cd7a9a81 | ||
|
|
357628b0d6 | ||
|
|
edc149417a | ||
|
|
028d1631bd | ||
|
|
b62eaa7bb2 | ||
|
|
a934fa3897 | ||
|
|
01a637d0f6 | ||
|
|
0560526b53 | ||
|
|
d12bb98f8c | ||
|
|
1cb1687958 | ||
|
|
fb404a8452 | ||
|
|
6e761b0a91 | ||
|
|
5111efdfdf | ||
|
|
764e16a1d8 | ||
|
|
3bbc4b7742 | ||
|
|
76276a7938 | ||
|
|
8436e3d826 | ||
|
|
4e18b3f4e0 | ||
|
|
3f4e53c5e3 | ||
|
|
55cf24a1c8 | ||
|
|
46e6a32a3a | ||
|
|
7802e1fbca | ||
|
|
a328e7902e | ||
|
|
072a5947cf | ||
|
|
5b1c4f7208 | ||
|
|
070d69f094 | ||
|
|
1d39373117 | ||
|
|
06233dd1bd | ||
|
|
ca981ff7c2 | ||
|
|
d11456b9b0 | ||
|
|
54a6061748 | ||
|
|
951b4e8d99 | ||
|
|
2d8fbbabfb | ||
|
|
fdacc73c0c | ||
|
|
ebc53e4bc8 | ||
|
|
3780225a42 | ||
|
|
6d768913e3 | ||
|
|
295b300654 | ||
|
|
71f29406d8 | ||
|
|
afb985b233 | ||
|
|
d2f8c1ebd8 | ||
|
|
48e2026ef0 | ||
|
|
db4c50d834 | ||
|
|
ce977aa927 | ||
|
|
1051c9f779 | ||
|
|
26a4e2f710 | ||
|
|
afa055f004 | ||
|
|
5968d764ca | ||
|
|
40fb28f5e1 | ||
|
|
a8377c9b24 |
@@ -20,7 +20,7 @@
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// This can be used to network with other containers or with the host.
|
||||
// "forwardPorts": [3000, 5432],
|
||||
"forwardPorts": [3000, 5432, 8025],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "pnpm install",
|
||||
|
||||
@@ -37,5 +37,16 @@ services:
|
||||
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
|
||||
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
||||
|
||||
mailhog:
|
||||
image: mailhog/mailhog
|
||||
network_mode: service:app
|
||||
logging:
|
||||
driver: "none" # disable saving logs
|
||||
# ports:
|
||||
# - 8025:8025 # web ui
|
||||
# 1025:1025 # smtp server
|
||||
|
||||
|
||||
|
||||
volumes:
|
||||
postgres-data: null
|
||||
|
||||
33
.dockerignore
Normal file
@@ -0,0 +1,33 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
.pnpm-store/
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# next.js
|
||||
.next/
|
||||
out/
|
||||
build
|
||||
|
||||
# node
|
||||
dist/
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# turbo
|
||||
.turbo
|
||||
|
||||
# nixos stuff
|
||||
.direnv
|
||||
@@ -11,8 +11,8 @@ NEXTAUTH_SECRET=RANDOM_STRING
|
||||
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# This should always be localhost:3000 (or whatever port your app is running on)
|
||||
NEXTAUTH_URL_INTERNAL=http://localhost:3000
|
||||
# If you encounter NEXT_AUTH URL problems this should always be localhost:3000 (or whatever port your app is running on)
|
||||
# NEXTAUTH_URL_INTERNAL=http://localhost:3000
|
||||
|
||||
DATABASE_URL='postgresql://postgres:postgres@postgres:5432/postgres?schema=public'
|
||||
|
||||
@@ -54,6 +54,11 @@ NEXT_PUBLIC_PASSWORD_RESET_DISABLED=1
|
||||
# Other #
|
||||
##########
|
||||
|
||||
# Display privacy policy, imprint and terms of service links in the footer of signup & public pages.
|
||||
NEXT_PUBLIC_PRIVACY_URL=
|
||||
NEXT_PUBLIC_TERMS_URL=
|
||||
NEXT_PUBLIC_IMPRINT_URL=
|
||||
|
||||
# Disable Sentry warning
|
||||
SENTRY_IGNORE_API_RESOLUTION_ERROR=1
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ NEXTAUTH_SECRET=RANDOM_STRING
|
||||
# Set this to your public-facing URL, e.g., https://example.com
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# This should always be localhost:3000 (or whatever port your app is running on)
|
||||
NEXTAUTH_URL_INTERNAL=http://localhost:3000
|
||||
# If you encounter NEXT_AUTH URL problems this should always be localhost:3000 (or whatever port your app is running on)
|
||||
# NEXTAUTH_URL_INTERNAL=http://localhost:3000
|
||||
|
||||
DATABASE_URL='postgresql://postgres:postgres@localhost:5432/postgres?schema=public'
|
||||
|
||||
@@ -61,6 +61,11 @@ SMTP_PASSWORD=smtpPassword
|
||||
# Other #
|
||||
##########
|
||||
|
||||
# Display privacy policy, imprint and terms of service links in the footer of signup & public pages.
|
||||
NEXT_PUBLIC_PRIVACY_URL=
|
||||
NEXT_PUBLIC_TERMS_URL=
|
||||
NEXT_PUBLIC_IMPRINT_URL=
|
||||
|
||||
# Disable Sentry warning
|
||||
SENTRY_IGNORE_API_RESOLUTION_ERROR=1
|
||||
|
||||
|
||||
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,13 +1,11 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Something not working as expected? Let us look into it
|
||||
title: ""
|
||||
about: "Found a bug? Please fill out the sections below. \U0001F44D"
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
Found a bug? Please fill out the sections below. 👍
|
||||
|
||||
### Issue Summary
|
||||
|
||||
<!--
|
||||
@@ -18,27 +16,36 @@ A summary of the issue. This needs to be a clear detailed-rich summary.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
<!--
|
||||
1. (for example) Went to ...
|
||||
2. Clicked on...
|
||||
3. ...
|
||||
|
||||
Any other relevant information. For example, why do you consider this a bug and what did you expect to happen instead?
|
||||
-->
|
||||
### Expected behavior
|
||||
|
||||
(Write your answer here.)
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
## Environment
|
||||
### Other information
|
||||
|
||||
#### Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
#### Environment
|
||||
|
||||
- [ ] Formbricks Cloud (app.formbricks.com)
|
||||
- [ ] self-hosted snoopForms, version/commit: [please provide]
|
||||
- [ ] self-hosted Formbricks, version/commit: [please provide]
|
||||
|
||||
### Additional Context
|
||||
#### Desktop (please complete the following information):
|
||||
|
||||
<!--
|
||||
- Browser version, screen recording, console logs, network requests: You can make a recording with [Bird Eats Bug](https://birdeatsbug.com/).
|
||||
- Node.js version
|
||||
- Anything else that you think could be an issue.
|
||||
-->
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
(Write your answer here.)
|
||||
#### Node.JS version
|
||||
|
||||
[e.g. v18.15.0]
|
||||
|
||||
#### Anything else?
|
||||
|
||||
- Screen recording, console logs, network requests: You can make a recording with [Loom](https://www.loom.com).
|
||||
- Anything else that you think could be an issue?
|
||||
|
||||
44
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,41 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature or idea
|
||||
title: ""
|
||||
labels: feature
|
||||
about: "Suggest an idea for this project \U0001F680"
|
||||
title: "[FEATURE]"
|
||||
labels: enhancement
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### Is your proposal related to a problem?
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
<!--
|
||||
Provide a clear and concise description of what the problem is.
|
||||
For example, "I'm always frustrated when..."
|
||||
-->
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
(Write your answer here.)
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
### Describe the solution you'd like
|
||||
|
||||
<!--
|
||||
Provide a clear and concise description of what you want to happen.
|
||||
-->
|
||||
|
||||
(Describe your proposed solution here.)
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
<!--
|
||||
Let us know about other solutions you've tried or researched.
|
||||
-->
|
||||
|
||||
(Write your answer here.)
|
||||
|
||||
### Additional context
|
||||
|
||||
<!--
|
||||
Is there anything else you can add about the proposal?
|
||||
You might want to link to related issues here, if you haven't already.
|
||||
-->
|
||||
|
||||
(Write your answer here.)
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
8
.github/workflows/checks.yml
vendored
@@ -13,16 +13,16 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js 16.x
|
||||
- name: Setup Node.js 18.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.2.2
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
6
.github/workflows/release.yml
vendored
@@ -18,16 +18,16 @@ jobs:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js 16.x
|
||||
- name: Setup Node.js 18.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.2.2
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
|
||||
2
.gitignore
vendored
@@ -39,3 +39,5 @@ yarn-error.log*
|
||||
|
||||
# nixos stuff
|
||||
.direnv
|
||||
|
||||
Zone.Identifier
|
||||
21
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch localhost:3002",
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"url": "http://localhost:3002/",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "firefox",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
45
README.md
@@ -5,34 +5,42 @@
|
||||
<h3 align="center">Formbricks</h3>
|
||||
|
||||
<p align="center">
|
||||
The Open Source Experience Management solution for fast growing companies
|
||||
The Open Source Survey & Experience Management solution for fast growing companies
|
||||
<br />
|
||||
<a href="https://formbricks.com/">Website</a> | <a href="https://formbricks.com/discord">Join Discord community</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/formbricks/formbricks/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-purple" alt="License"></a> <a href="https://formbricks.com/discord"><img src="https://img.shields.io/discord/979077669410979880?label=Discord&logo=discord&logoColor=%23fff" alt="Join Formbricks Discord"></a> <a href="https://github.com/formbricks/formbricks/stargazers"><img src="https://img.shields.io/github/stars/formbricks/formbricks?logo=github" alt="Github Stars"></a>
|
||||
<a href="https://github.com/formbricks/formbricks/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPL-purple" alt="License"></a> <a href="https://formbricks.com/discord"><img src="https://img.shields.io/discord/979077669410979880?label=Discord&logo=discord&logoColor=%23fff" alt="Join Formbricks Discord"></a> <a href="https://github.com/formbricks/formbricks/stargazers"><img src="https://img.shields.io/github/stars/formbricks/formbricks?logo=github" alt="Github Stars"></a>
|
||||
<a href="https://news.ycombinator.com/item?id=32303986"><img src="https://img.shields.io/badge/Hacker%20News-122-%23FF6600" alt="Hacker News"></a>
|
||||
<a href="https://www.producthunt.com/products/snoopforms"><img src="https://img.shields.io/badge/Product%20Hunt-%232%20Product%20of%20the%20Day-orange?logo=producthunt&logoColor=%23fff" alt="Product Hunt"></a>
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
> :octocat: Are you looking for snoopForms - the Open Source Typeform Alternative? We're building the next stage of the snoopForms evolution here with Formbricks - focused on experience management for fast growing companies. If you still are looking for the code of snoopForms you can find it in the [snoopforms branch](https://github.com/formbricks/formbricks/tree/snoopforms).
|
||||
|
||||
> :warning: Repository still in progress `#buildinpublic`
|
||||
|
||||
## About Formbricks
|
||||
|
||||
<img width="1527" alt="formbricks-sneak" src="https://user-images.githubusercontent.com/675065/227726212-6ebf930e-6a20-4ffa-b966-56cd41bdf363.png">
|
||||
|
||||
Formbricks productizes best practices for qualitative in-app user discovery. Use micro-surveys to target the right users at the right time without making surveys annoying.
|
||||
|
||||
**Try it out in the cloud at [formbricks.com](https://formbricks.com)**
|
||||
|
||||
### Mission: Base your decisions on qualitative data.
|
||||
|
||||
Formbricks helps you apply best practices from data-driven work and experience management to make better business decisions. Use Formbricks to collect and manage insights from your users; run a product market fit survey to know which audience to focus on and whether your value proposition is being recognized.
|
||||
|
||||
### Features
|
||||
|
||||
- 📲 Create in-product surveys with our no code editor with multiple question types
|
||||
- 📚 Choose from a variety of best-practice templates
|
||||
- 👩🏻 Launch and target your surveys to specific user groups without changing your application code
|
||||
- 🔗 Create shareable link surveys
|
||||
- 👨👩👦 Invite your team members to collaborate on your surveys
|
||||
- 🔌 Integrate Formbricks with Slack, Posthog, Zapier and more
|
||||
- 🔒 All open source, transparent and self-hostable
|
||||
|
||||
### Built With
|
||||
|
||||
- [Typescript](https://www.typescriptlang.org/)
|
||||
@@ -41,9 +49,30 @@ Formbricks helps you apply best practices from data-driven work and experience m
|
||||
- [TailwindCSS](https://tailwindcss.com/)
|
||||
- [Prisma](https://prisma.io/)
|
||||
|
||||
### Upcoming Features
|
||||
|
||||
| | Feature |
|
||||
| --- | --------------------------------------------- |
|
||||
| 👷 | Rating Scale (Numbers + Emojis) Question Type |
|
||||
| 👷 | Zapier, Slack & Posthog Integration |
|
||||
| 👷 | Filter Audience by Attributes |
|
||||
| 🗒️ | Multi-Language Functionality |
|
||||
| 🗒️ | Auto-complete Surveys after at x responses |
|
||||
| 🗒️ | Pre-Fill Link-Surveys |
|
||||
| 🗒️ | E-Mail Surveys |
|
||||
|
||||
_👷 In Progress | 🗒️ Up Next_
|
||||
|
||||
## Cloud vs. self-hosted
|
||||
|
||||
Formbricks is available Open-Source under AGPLv3 license. You can host Formbricks on your own servers without a subscription. Check out our [docs](https://formbricks.com/docs/formbricks-hq/self-hosting) to see how to self-host Formbricks.
|
||||
We will soon offer a cloud version of Formbricks which saves you the hassle of maintaining your own servers. We will update this Readme once the cloud version is available.
|
||||
Formbricks is available Open-Source under AGPLv3 license. You can host Formbricks on your own servers without a subscription. Check out our [docs](https://formbricks.com/docs/self-hosting/deployment) to see how to self-host Formbricks.
|
||||
|
||||
We also have a hosted cloud offering with a generous free plan to get you up and running as quickly as possible. For more information, please visit [formbricks.com](https://formbricks.com)
|
||||
|
||||
(In the future we may develop additional features that aren't in the free Open-Source version)
|
||||
|
||||
## Contributing
|
||||
|
||||
We are very happy if you are interested in contributing to Formbricks 🤗
|
||||
|
||||
There are many ways to contribute to Formbricks with writing Issues, fixing bugs, building new features or updating the docs. Please check out [our contribution guide](https://formbricks.com/docs/contributing/introduction) for more information.
|
||||
|
||||
@@ -27,7 +27,7 @@ const secondaryNavigation = [
|
||||
|
||||
export default function Sidebar({}) {
|
||||
return (
|
||||
<div className="flex flex-grow flex-col overflow-y-auto bg-cyan-700 pt-5 pb-4">
|
||||
<div className="flex flex-grow flex-col overflow-y-auto bg-cyan-700 pb-4 pt-5">
|
||||
<nav
|
||||
className="mt-5 flex flex-1 flex-col divide-y divide-cyan-800 overflow-y-auto"
|
||||
aria-label="Sidebar">
|
||||
|
||||
@@ -10,21 +10,21 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@heroicons/react": "^2.0.16",
|
||||
"@types/node": "18.15.10",
|
||||
"@types/react": "18.0.29",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@types/node": "18.15.11",
|
||||
"@types/react": "18.0.33",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"eslint": "8.36.0",
|
||||
"eslint": "8.37.0",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"next": "13.2.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"typescript": "5.0.2"
|
||||
"typescript": "5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.7"
|
||||
"tailwindcss": "^3.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { AppProps } from "next/app";
|
||||
import formbricks from "@formbricks/js";
|
||||
import { useEffect } from "react";
|
||||
import type { AppProps } from "next/app";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
import "../styles/globals.css";
|
||||
|
||||
declare const window: any;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
|
||||
formbricks.init({
|
||||
@@ -11,6 +13,7 @@ if (typeof window !== "undefined") {
|
||||
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
|
||||
logLevel: "debug",
|
||||
});
|
||||
window.formbricks = formbricks;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
MagnifyingGlassIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import Image from "next/image";
|
||||
import formbricks from "@formbricks/js";
|
||||
|
||||
const cards = [{ name: "Account balance", href: "#", icon: ScaleIcon, amount: "$30,659.45" }];
|
||||
const transactions = [
|
||||
@@ -64,9 +65,30 @@ export default function AppPage({}) {
|
||||
</form>
|
||||
</div>
|
||||
<div className="ml-4 flex items-center md:ml-6">
|
||||
<button className="mr-2 flex max-w-xs items-center rounded-full bg-white text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 lg:rounded-md lg:p-2 lg:hover:bg-slate-50">
|
||||
<button
|
||||
className="mr-2 flex max-w-xs items-center rounded-full bg-white text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 lg:rounded-md lg:p-2 lg:hover:bg-slate-50"
|
||||
onClick={() => {
|
||||
formbricks.track("Feedback Button Click");
|
||||
}}>
|
||||
Feedback
|
||||
</button>
|
||||
<button className="mr-2 flex max-w-xs items-center rounded-full bg-white text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 lg:rounded-md lg:p-2 lg:hover:bg-slate-50">
|
||||
No Code Feedback Btn Click
|
||||
</button>
|
||||
<button
|
||||
className="mr-2 flex max-w-xs items-center rounded-full bg-white text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 lg:rounded-md lg:p-2 lg:hover:bg-slate-50"
|
||||
onClick={() => {
|
||||
formbricks.setEmail("test@web.com");
|
||||
}}>
|
||||
Set Email
|
||||
</button>
|
||||
<button
|
||||
className="mr-2 flex max-w-xs items-center rounded-full bg-white text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 lg:rounded-md lg:p-2 lg:hover:bg-slate-50"
|
||||
onClick={() => {
|
||||
formbricks.setUserId("ASDASDAAAAAASSSSSSSASDASD");
|
||||
}}>
|
||||
Set Long UserID
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-full bg-white p-1 text-slate-400 hover:text-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2">
|
||||
@@ -146,7 +168,7 @@ export default function AppPage({}) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex space-x-3 md:mt-0 md:ml-4">
|
||||
<div className="mt-6 flex space-x-3 md:ml-4 md:mt-0">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2">
|
||||
@@ -325,8 +347,9 @@ export default function AppPage({}) {
|
||||
<div className="flex flex-1 justify-between sm:justify-end">
|
||||
<a
|
||||
href="#"
|
||||
id="test-css"
|
||||
className="relative inline-flex items-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">
|
||||
Previous
|
||||
CSS ID Test
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function SiginPage() {
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<div className="bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
|
||||
<form className="space-y-6" onSubmit={submitAction}>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium leading-6 text-gray-900">
|
||||
@@ -84,7 +84,7 @@ export default function SiginPage() {
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full justify-center rounded-md bg-indigo-500 py-2 px-3 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
|
||||
className="flex w-full justify-center rounded-md bg-indigo-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
@@ -104,7 +104,7 @@ export default function SiginPage() {
|
||||
<div>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex w-full justify-center rounded-md bg-white py-2 px-4 text-gray-500 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0">
|
||||
className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-gray-500 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0">
|
||||
<span className="sr-only">Sign in with Facebook</span>
|
||||
<svg className="h-5 w-5" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
@@ -119,7 +119,7 @@ export default function SiginPage() {
|
||||
<div>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex w-full justify-center rounded-md bg-white py-2 px-4 text-gray-500 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0">
|
||||
className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-gray-500 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0">
|
||||
<span className="sr-only">Sign in with Twitter</span>
|
||||
<svg className="h-5 w-5" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84" />
|
||||
@@ -130,7 +130,7 @@ export default function SiginPage() {
|
||||
<div>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex w-full justify-center rounded-md bg-white py-2 px-4 text-gray-500 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0">
|
||||
className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-gray-500 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0">
|
||||
<span className="sr-only">Sign in with GitHub</span>
|
||||
<svg className="h-5 w-5" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
|
||||
3
apps/formbricks-com/.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
NEXT_PUBLIC_FORMBRICKS_COM_API_HOST=http://localhost:3000
|
||||
NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID=
|
||||
NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID=
|
||||
5
apps/formbricks-com/.gitignore
vendored
@@ -23,7 +23,6 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
@@ -35,6 +34,4 @@ yarn-error.log*
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
.vscode
|
||||
|
||||
public/sitemap*.xml
|
||||
public/sitemap*.xml
|
||||
@@ -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.
|
||||
@@ -8,6 +8,8 @@ First, run the development server:
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
@@ -18,6 +20,8 @@ You can start editing the page by modifying `pages/index.tsx`. The page auto-upd
|
||||
|
||||
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.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
70
apps/formbricks-com/components/docs/DocsFeedback.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { useState } from "react";
|
||||
import { handleFeedbackSubmit, updateFeedback } from "../../lib/handleFeedbackSubmit";
|
||||
import { Popover, PopoverTrigger, PopoverContent, Button } from "@formbricks/ui";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
export default function DocsFeedback() {
|
||||
const router = useRouter();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [sharedFeedback, setSharedFeedback] = useState(false);
|
||||
const [responseId, setResponseId] = useState(null);
|
||||
const [freeText, setFreeText] = useState("");
|
||||
|
||||
if (
|
||||
!process.env.NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID ||
|
||||
!process.env.NEXT_PUBLIC_FORMBRICKS_COM_API_HOST ||
|
||||
!process.env.NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-6 inline-flex cursor-default items-center rounded-md border border-slate-200 bg-white p-4 text-slate-800 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300">
|
||||
{!sharedFeedback ? (
|
||||
<div>
|
||||
Was this page helpful?
|
||||
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
||||
<div className="ml-4 inline-flex space-x-3">
|
||||
{["Yes 👍", " No 👎"].map((option) => (
|
||||
<PopoverTrigger
|
||||
className="rounded border border-slate-200 bg-slate-50 px-4 py-2 text-slate-900 hover:bg-slate-100 hover:text-slate-600 focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1 dark:border-slate-700 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600 dark:hover:text-slate-300"
|
||||
onClick={async () => {
|
||||
const id = await handleFeedbackSubmit(option, router.asPath);
|
||||
setResponseId(id);
|
||||
}}>
|
||||
{option}
|
||||
</PopoverTrigger>
|
||||
))}
|
||||
</div>
|
||||
<PopoverContent className="border-slate-300 bg-white dark:border-slate-500 dark:bg-slate-700">
|
||||
<form>
|
||||
<textarea
|
||||
value={freeText}
|
||||
onChange={(e) => setFreeText(e.target.value)}
|
||||
placeholder="Please explain why..."
|
||||
className="w-full rounded-md bg-white text-sm text-slate-900 dark:bg-slate-600 dark:text-slate-200 dark:placeholder:text-slate-200"
|
||||
/>
|
||||
<div className="text-right">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
updateFeedback(freeText, responseId);
|
||||
setIsOpen(false);
|
||||
setFreeText("");
|
||||
setSharedFeedback(true);
|
||||
}}>
|
||||
Send
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
) : (
|
||||
<div>Thanks a lot, boss! 🤝</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import Script from "next/script";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
formbricks: any;
|
||||
}
|
||||
}
|
||||
|
||||
export default function FeedbackButton() {
|
||||
const plausible = usePlausible();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const feedbackRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Close the feedback form if the user clicks outside of it
|
||||
function handleClickOutside(event: any) {
|
||||
if (feedbackRef.current && !feedbackRef.current.contains(event.target)) {
|
||||
if (isOpen) setIsOpen(false);
|
||||
}
|
||||
}
|
||||
// Bind the event listener
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
// Unbind the event listener on clean up
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [feedbackRef, isOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Script src="https://cdn.jsdelivr.net/npm/@formbricks/feedback@0.3/dist/index.umd.js" defer />
|
||||
|
||||
<Script id="feedback-setup">{`
|
||||
window.formbricks = {
|
||||
...window.formbricks,
|
||||
config: {
|
||||
formbricksUrl: "https://app.formbricks.com",
|
||||
formId: "cle2pg7no0000nu0hjefwy3w7",
|
||||
containerId: "formbricks-feedback-wrapper",
|
||||
contact: {
|
||||
name: "Matti",
|
||||
position: "Co-Founder",
|
||||
imgUrl: "https://avatars.githubusercontent.com/u/675065?s=128&v=4",
|
||||
},
|
||||
},
|
||||
};`}</Script>
|
||||
<div
|
||||
className={clsx(
|
||||
"xs:flex-row xs:w-[18rem] xs:-translate-y-1/2 z-50 h-fit w-full transition-all duration-500 ease-in-out",
|
||||
isOpen ? "xs:-translate-x-0 translate-y-0" : "xs:translate-x-full xs:-mr-1 translate-y-full"
|
||||
)}>
|
||||
<div
|
||||
className="xs:flex-row flex h-full flex-col"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
ref={feedbackRef}>
|
||||
<button
|
||||
className="xs:-rotate-90 xs:top-1/2 xs:-left-[5.75rem] xs:-translate-y-1/2 xs:-translate-x-0 xs:w-32 xs:p-4 bg-brand-dark absolute left-1/2 w-28 -translate-x-1/2 -translate-y-full rounded-t-lg p-3 font-medium text-white"
|
||||
onClick={() => {
|
||||
if (!isOpen) {
|
||||
plausible("openFeedback");
|
||||
if (window) {
|
||||
window.formbricks.render();
|
||||
window.formbricks.resetForm();
|
||||
}
|
||||
}
|
||||
setIsOpen(!isOpen);
|
||||
}}>
|
||||
{isOpen ? "Close" : "Feedback"}
|
||||
</button>
|
||||
<div
|
||||
className="xs:rounded-bl-lg xs:rounded-tr-none h-full w-full overflow-hidden rounded-bl-none rounded-tr-lg rounded-tl-lg bg-slate-50 shadow-lg"
|
||||
id="formbricks-feedback-wrapper"></div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
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 { FooterLogo, Logomark } from "@/components/shared/Logo";
|
||||
import { FooterLogo } from "@/components/shared/Logo";
|
||||
import { MobileNavigation } from "@/components/shared/MobileNavigation";
|
||||
import { Navigation } from "@/components/shared/Navigation";
|
||||
import { Prose } from "@/components/shared/Prose";
|
||||
@@ -11,7 +6,12 @@ import { Search } from "@/components/shared/Search";
|
||||
import { ThemeSelector } from "@/components/shared/ThemeSelector";
|
||||
import navigation from "@/lib/docsNavigation";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import MetaInformation from "../shared/MetaInformation";
|
||||
import DocsFeedback from "./DocsFeedback";
|
||||
|
||||
function GitHubIcon(props: any) {
|
||||
return (
|
||||
@@ -57,6 +57,7 @@ function Header({ navigation }: any) {
|
||||
</div>
|
||||
<div className="hidden items-center justify-end md:flex md:flex-1 lg:w-0">
|
||||
<ThemeSelector className="relative z-10 mr-5" />
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
EndIcon={GitHubIcon}
|
||||
@@ -64,9 +65,12 @@ function Header({ navigation }: any) {
|
||||
onClick={() => router.push("https://github.com/formbricks/formbricks")}>
|
||||
View on Github
|
||||
</Button>
|
||||
{/* <Button variant="highlight" className="ml-2" onClick={() => router.push("/waitlist")}>
|
||||
<Button
|
||||
variant="highlight"
|
||||
className="ml-2"
|
||||
onClick={() => router.push("https://app.formbricks.com/auth/signup")}>
|
||||
Get started
|
||||
</Button> */}
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
@@ -76,12 +80,12 @@ interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
meta: {
|
||||
title: string;
|
||||
description?: 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];
|
||||
@@ -91,23 +95,23 @@ export function Layout({ children, meta }: LayoutProps) {
|
||||
return (
|
||||
<>
|
||||
<MetaInformation
|
||||
title="Formbricks Documentation"
|
||||
description="Modular, customizable, extendable. Take what you like, build on top what you need. Build your next big thing faster."
|
||||
title={`Formbricks Docs | ${meta.title}`}
|
||||
description={
|
||||
meta.description ? meta.description : "Open-source Experience Management for Digital Products."
|
||||
}
|
||||
/>
|
||||
<Header navigation={navigation} />
|
||||
|
||||
{isHomePage && <Hero />}
|
||||
|
||||
<div className="max-w-8xl relative mx-auto flex justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
<div className="hidden lg:relative lg:block lg:flex-none">
|
||||
<div className="absolute inset-y-0 right-0 w-[50vw] bg-slate-50 dark:hidden" />
|
||||
<div className="absolute top-16 bottom-0 right-0 hidden h-12 w-px bg-gradient-to-t from-slate-800 dark:block" />
|
||||
<div className="absolute top-28 bottom-0 right-0 hidden w-px bg-slate-800 dark:block" />
|
||||
<div className="absolute bottom-0 right-0 top-16 hidden h-12 w-px bg-gradient-to-t from-slate-800 dark:block" />
|
||||
<div className="absolute bottom-0 right-0 top-28 hidden w-px bg-slate-800 dark:block" />
|
||||
<div className="sticky top-[4.5rem] -ml-0.5 h-[calc(100vh-4.5rem)] overflow-y-auto overflow-x-hidden py-16 pl-0.5">
|
||||
<Navigation navigation={navigation} className="w-64 pr-8 xl:w-72 xl:pr-16" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 max-w-2xl flex-auto px-4 py-16 lg:max-w-none lg:pr-0 lg:pl-8 xl:px-16">
|
||||
<div className="min-w-0 max-w-2xl flex-auto px-4 py-16 lg:max-w-none lg:pl-8 lg:pr-0 xl:px-16">
|
||||
<article>
|
||||
{(meta.title || section) && (
|
||||
<header className="mb-9 space-y-1">
|
||||
@@ -125,6 +129,7 @@ export function Layout({ children, meta }: LayoutProps) {
|
||||
)}
|
||||
<Prose className="">{children}</Prose>
|
||||
</article>
|
||||
<DocsFeedback />
|
||||
<dl className="mt-12 flex border-t border-slate-200 pt-6 dark:border-slate-800">
|
||||
{previousPage && (
|
||||
<div>
|
||||
|
||||
@@ -44,7 +44,7 @@ const DummyUI: React.FC = () => {
|
||||
{eventClasses.map((eventClass) => (
|
||||
<SelectItem
|
||||
key={eventClass.id}
|
||||
className="py-1 px-0.5 text-slate-800 dark:bg-slate-700 dark:text-slate-300 dark:ring-slate-700"
|
||||
className="px-0.5 py-1 text-slate-800 dark:bg-slate-700 dark:text-slate-300 dark:ring-slate-700"
|
||||
value={eventClass.id}>
|
||||
{eventClass.name}
|
||||
</SelectItem>
|
||||
|
||||
@@ -13,18 +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 Action</div>
|
||||
<div className="text-sm text-slate-500">
|
||||
Create a new no-code event to filter your user base with.
|
||||
Create a new user action to display surveys when it's triggered.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,21 +36,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 +73,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 +97,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"
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function MultipleChoiceSingleQuestion({
|
||||
const data = {
|
||||
[question.id]: e.currentTarget[question.id].value,
|
||||
};
|
||||
console.log(data);
|
||||
|
||||
e.currentTarget[question.id].value = "";
|
||||
onSubmit(data);
|
||||
// reset form
|
||||
|
||||
@@ -108,12 +108,14 @@ export default function TemplateList() {
|
||||
};
|
||||
|
||||
const [activeTemplate, setActiveTemplate] = useState<Template | null>(onboardingSegmentation);
|
||||
const [selectedFilter, setSelectedFilter] = useState("All");
|
||||
|
||||
const categories = [
|
||||
"All",
|
||||
...(Array.from(new Set(templates.map((template) => template.category))) as string[]),
|
||||
];
|
||||
|
||||
const [selectedFilter, setSelectedFilter] = useState(categories[0]);
|
||||
|
||||
const customSurvey: Template = {
|
||||
name: "Custom Survey",
|
||||
description: "Create your survey from scratch.",
|
||||
@@ -134,75 +136,80 @@ export default function TemplateList() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="hidden h-full flex-col lg:flex">
|
||||
<div className="relative z-0 flex flex-1 overflow-hidden">
|
||||
<main className="relative z-0 max-h-[90vh] flex-1 overflow-y-auto rounded-l-lg bg-slate-100 py-6 px-6 focus:outline-none dark:bg-slate-700">
|
||||
<div className="mb-6 flex space-x-2">
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
type="button"
|
||||
onClick={() => setSelectedFilter(category)}
|
||||
className={clsx(
|
||||
selectedFilter === category
|
||||
? "text-brand-dark border-brand-dark font-semibold"
|
||||
: "border-slate-300 text-slate-700 hover:bg-slate-100 dark:border-slate-600 dark:text-slate-400",
|
||||
"rounded border bg-slate-50 px-3 py-1 text-xs transition-all duration-150 dark:bg-slate-800 "
|
||||
)}>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
{templates
|
||||
.filter((template) => selectedFilter === "All" || template.category === selectedFilter)
|
||||
.map((template: Template) => (
|
||||
<div>
|
||||
<div className="mt-6 hidden flex-col md:flex">
|
||||
<div className="z-0 flex min-h-[90vh] overflow-hidden">
|
||||
<main className="relative z-0 max-h-[90vh] flex-1 overflow-y-auto px-6 pb-6 focus:outline-none dark:bg-slate-700">
|
||||
<div className="mb-6 flex space-x-2">
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
type="button"
|
||||
onClick={() => setActiveTemplate(template)}
|
||||
key={template.name}
|
||||
onClick={() => setSelectedFilter(category)}
|
||||
className={clsx(
|
||||
activeTemplate?.name === template.name && "ring-brand ring-2",
|
||||
"duration-120 group relative rounded-lg bg-white p-6 shadow transition-all duration-150 hover:scale-105 dark:bg-slate-600"
|
||||
selectedFilter === category
|
||||
? "text-brand-dark border-brand-dark font-semibold"
|
||||
: "border-slate-300 text-slate-700 hover:bg-slate-100 dark:border-slate-600 dark:text-slate-400",
|
||||
"rounded border bg-slate-50 px-3 py-1 text-xs transition-all duration-150 dark:bg-slate-800 "
|
||||
)}>
|
||||
<div className="absolute top-6 right-6 rounded border border-slate-300 bg-slate-50 px-1.5 py-0.5 text-xs text-slate-500 dark:border-slate-500 dark:bg-slate-700 dark:text-slate-300">
|
||||
{template.category}
|
||||
</div>
|
||||
<template.icon className="h-8 w-8" />
|
||||
<h3 className="text-md mt-3 mb-1 text-left font-bold text-slate-700 dark:text-slate-200">
|
||||
{template.name}
|
||||
</h3>
|
||||
<p className="text-left text-xs text-slate-600 dark:text-slate-400">
|
||||
{template.description}
|
||||
</p>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTemplate(customSurvey)}
|
||||
className={clsx(
|
||||
activeTemplate?.name === customSurvey.name && "ring-brand ring-2",
|
||||
"duration-120 hover:border-brand-dark group relative rounded-lg border-2 border-dashed border-slate-300 bg-transparent p-8 transition-colors duration-150"
|
||||
)}>
|
||||
<PlusCircleIcon className="text-brand-dark h-8 w-8 transition-all duration-150 group-hover:scale-110" />
|
||||
<h3 className="text-md mt-3 mb-1 text-left font-bold text-slate-700 dark:text-slate-200">
|
||||
{customSurvey.name}
|
||||
</h3>
|
||||
<p className="text-left text-xs text-slate-600 dark:text-slate-400">
|
||||
{customSurvey.description}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
<aside className="group relative hidden max-h-[90vh] flex-1 flex-shrink-0 overflow-hidden rounded-r-lg border-l border-slate-200 bg-slate-200 shadow-inner dark:border-slate-700 dark:bg-slate-800 md:flex md:flex-col">
|
||||
{activeTemplate && (
|
||||
<PreviewSurvey
|
||||
activeQuestionId={null}
|
||||
questions={activeTemplate.preset.questions}
|
||||
brandColor="#00C4B8"
|
||||
/>
|
||||
)}
|
||||
</aside>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
{templates
|
||||
.filter((template) => selectedFilter === "All" || template.category === selectedFilter)
|
||||
.map((template: Template) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTemplate(template)}
|
||||
key={template.name}
|
||||
className={clsx(
|
||||
activeTemplate?.name === template.name && "ring-brand ring-2",
|
||||
"duration-120 group relative rounded-lg bg-white p-6 shadow transition-all duration-150 hover:scale-105 dark:bg-slate-600"
|
||||
)}>
|
||||
<div className="absolute right-6 top-6 rounded border border-slate-300 bg-slate-50 px-1.5 py-0.5 text-xs text-slate-500 dark:border-slate-500 dark:bg-slate-700 dark:text-slate-300">
|
||||
{template.category}
|
||||
</div>
|
||||
<template.icon className="h-8 w-8" />
|
||||
<h3 className="text-md mb-1 mt-3 text-left font-bold text-slate-700 dark:text-slate-200">
|
||||
{template.name}
|
||||
</h3>
|
||||
<p className="text-left text-xs text-slate-600 dark:text-slate-400">
|
||||
{template.description}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTemplate(customSurvey)}
|
||||
className={clsx(
|
||||
activeTemplate?.name === customSurvey.name && "ring-brand ring-2",
|
||||
"duration-120 hover:border-brand-dark group relative rounded-lg border-2 border-dashed border-slate-300 bg-transparent p-8 transition-colors duration-150"
|
||||
)}>
|
||||
<PlusCircleIcon className="text-brand-dark h-8 w-8 transition-all duration-150 group-hover:scale-110" />
|
||||
<h3 className="text-md mb-1 mt-3 text-left font-bold text-slate-700 dark:text-slate-200">
|
||||
{customSurvey.name}
|
||||
</h3>
|
||||
<p className="text-left text-xs text-slate-600 dark:text-slate-400">
|
||||
{customSurvey.description}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
<aside className="group relative hidden max-h-[90vh] flex-1 flex-shrink-0 overflow-hidden rounded-r-lg border-l border-slate-200 bg-slate-200 shadow-inner dark:border-slate-700 dark:bg-slate-800 md:flex md:flex-col">
|
||||
{activeTemplate && (
|
||||
<PreviewSurvey
|
||||
activeQuestionId={null}
|
||||
questions={activeTemplate.preset.questions}
|
||||
brandColor="#00C4B8"
|
||||
/>
|
||||
)}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center pt-36 text-slate-600 md:hidden">
|
||||
This demo is not yet optimized for smartphones.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ export interface Template {
|
||||
name: string;
|
||||
icon: any;
|
||||
description: string;
|
||||
category?: "Product Management" | "Growth Marketing" | "Increase Revenue";
|
||||
category?: "All" | "Product Management" | "Growth Marketing" | "Increase Revenue";
|
||||
preset: {
|
||||
name: string;
|
||||
questions: Question[];
|
||||
|
||||
@@ -187,7 +187,7 @@ export const templates: Template[] = [
|
||||
{
|
||||
name: "Pre-Churn Survey",
|
||||
icon: CancelSubscriptionIcon,
|
||||
category: "Increase Revenue",
|
||||
category: "Product Management",
|
||||
description: "Find out why people cancel you. These insights are pure gold!",
|
||||
preset: {
|
||||
name: "Churn Survey",
|
||||
@@ -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 🤍",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -542,7 +538,7 @@ export const templates: Template[] = [
|
||||
{
|
||||
id: createId(),
|
||||
type: "multipleChoiceSingle",
|
||||
headline: "Were you able to 'accomplish what you came here to do today'?",
|
||||
headline: "Were you able to accomplish what you came here to do today?",
|
||||
required: true,
|
||||
choices: [
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ const features = [
|
||||
];
|
||||
export default function Features() {
|
||||
return (
|
||||
<div className="relative px-4 pt-16 pb-20 sm:px-6 lg:px-8 lg:pt-24 lg:pb-28">
|
||||
<div className="relative px-4 pb-10 sm:px-6 lg:px-8 lg:pb-14 lg:pt-24">
|
||||
<div className="relative mx-auto max-w-7xl">
|
||||
<HeadingCentered
|
||||
closer
|
||||
|
||||
40
apps/formbricks-com/components/home/GitHubSponsorship.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import GitHubMarkWhite from "@/images/github-mark-white.svg";
|
||||
import GitHubMarkDark from "@/images/github-mark.svg";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function GitHubSponsorship() {
|
||||
return (
|
||||
<div className="xs:mx-auto xs:w-full relative mx-auto my-4 mb-12 mt-12 rounded-xl bg-gradient-to-br from-slate-100 to-slate-200 px-4 py-8 dark:from-slate-800 dark:via-slate-800 dark:to-slate-700 sm:px-6 sm:pb-12 sm:pt-8 md:max-w-none lg:mt-6 lg:px-8 lg:pt-8 ">
|
||||
<div className="right-10 lg:absolute">
|
||||
<Image
|
||||
src={GitHubMarkDark}
|
||||
alt="GitHub Sponsors Formbricks badge"
|
||||
width={100}
|
||||
height={100}
|
||||
className="mr-12 block dark:hidden md:mr-4 "
|
||||
/>
|
||||
<Image
|
||||
src={GitHubMarkWhite}
|
||||
alt="GitHub Sponsors Formbricks badge"
|
||||
width={100}
|
||||
height={100}
|
||||
className="mr-12 hidden dark:block md:mr-4 "
|
||||
/>
|
||||
</div>
|
||||
<h2 className="mt-4 text-2xl font-bold tracking-tight text-slate-800 dark:text-slate-200 lg:text-2xl">
|
||||
Sponsored by GitHub
|
||||
</h2>
|
||||
<p className="lg:text-md mt-4 max-w-3xl text-slate-500 dark:text-slate-400">
|
||||
We're proud to join the first accelerator program by GitHub!{" "}
|
||||
<span>
|
||||
<Link
|
||||
href="/blog/inaugural-batch-github-accelerator"
|
||||
className="decoration-brand-dark underline underline-offset-4">
|
||||
Read more.
|
||||
</Link>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,56 +1,116 @@
|
||||
import TemplateList from "../dummyUI/TemplateList";
|
||||
import CalLogoDark from "@/images/clients/cal-logo-dark.svg";
|
||||
import CalLogoLight from "@/images/clients/cal-logo-light.svg";
|
||||
import CrowdLogoDark from "@/images/clients/crowd-logo-dark.svg";
|
||||
import CrowdLogoLight from "@/images/clients/crowd-logo-light.svg";
|
||||
import StackOceanLogoDark from "@/images/clients/stack-ocean-dark.png";
|
||||
import StackOceanLogoLight from "@/images/clients/stack-ocean-light.png";
|
||||
import AnimationFallback from "@/public/animations/fallback-image-open-source-feedback-software.jpg";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { useState } from "react";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import ClovyrLogo from "@/images/clients/clovyr-logo.svg";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import VideoWalkThrough from "./VideoWalkThrough";
|
||||
import { PlayCircleIcon } from "@heroicons/react/24/solid";
|
||||
import HeroAnimation from "./HeroAnimation";
|
||||
|
||||
interface Props {}
|
||||
|
||||
export default function Hero({}: Props) {
|
||||
const plausible = usePlausible();
|
||||
const router = useRouter();
|
||||
const [videoModal, setVideoModal] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="px-4 py-20 text-center sm:px-6 lg:px-8 lg:py-28">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-slate-800 dark:text-slate-200 sm:text-4xl md:text-5xl">
|
||||
<span className="xl:inline">Better experience data.</span>{" "}
|
||||
<span className="from-brand-light to-brand-dark bg-gradient-to-b bg-clip-text text-transparent xl:inline">
|
||||
Better business
|
||||
<span className="xl:inline">Survey any segment.</span>{" "}
|
||||
<span
|
||||
className="font-extralight" /* className="from-brand-light to-brand-dark bg-gradient-to-b bg-clip-text text-transparent xl:inline" */
|
||||
>
|
||||
No coding required.
|
||||
</span>
|
||||
<span className="inline ">.</span>
|
||||
</h1>
|
||||
|
||||
<p className="xs:max-w-none mx-auto mt-3 max-w-xs text-base text-slate-500 dark:text-slate-300 sm:text-lg md:mt-5 md:text-xl">
|
||||
Survey specific customer segments at any point in the user journey.
|
||||
<p className="xs:max-w-none mx-auto mt-3 max-w-xs text-base text-slate-500 dark:text-slate-400 sm:text-lg md:mt-5 md:text-xl">
|
||||
Survey granular user segments at any point in the user journey.
|
||||
<br />
|
||||
<span className="hidden md:block">
|
||||
Continuously measure what your customers think and feel. All open-source.
|
||||
Gather up to 6x more insights with targeted micro-surveys.{" "}
|
||||
<span className="decoration-brand-dark underline underline-offset-4">All open-source.</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div className="mx-auto mt-5 max-w-md sm:flex sm:justify-center md:mt-8">
|
||||
<div className="mx-auto mt-5 max-w-2xl items-center space-x-8 sm:flex sm:justify-center md:mt-8">
|
||||
<p className="hidden whitespace-nowrap pt-1 text-xs text-slate-400 dark:text-slate-500 md:block">
|
||||
Trusted by
|
||||
</p>
|
||||
<div className="grid grid-cols-4 gap-8 pt-2">
|
||||
<Image
|
||||
src={CalLogoLight}
|
||||
alt="Cal Logo"
|
||||
className="block rounded-lg opacity-50 hover:opacity-100 dark:hidden"
|
||||
width={170}
|
||||
/>
|
||||
<Image
|
||||
src={CalLogoDark}
|
||||
alt="Cal Logo"
|
||||
className="hidden rounded-lg opacity-50 hover:opacity-100 dark:block"
|
||||
width={170}
|
||||
/>
|
||||
<Image
|
||||
src={CrowdLogoLight}
|
||||
alt="Crowd.dev Logo"
|
||||
className="block rounded-lg pb-1 opacity-50 hover:opacity-100 dark:hidden"
|
||||
width={200}
|
||||
/>
|
||||
<Image
|
||||
src={CrowdLogoDark}
|
||||
alt="Crowd.dev Logo"
|
||||
className="hidden rounded-lg pb-1 opacity-50 hover:opacity-100 dark:block"
|
||||
width={200}
|
||||
/>
|
||||
<Image
|
||||
src={ClovyrLogo}
|
||||
alt="Clovyr Logo"
|
||||
className="rounded-lg pb-1 opacity-50 hover:opacity-100"
|
||||
width={200}
|
||||
/>
|
||||
<Image
|
||||
src={StackOceanLogoLight}
|
||||
alt="StackOcean Logo"
|
||||
className="block rounded-lg pb-1 opacity-50 hover:opacity-100 dark:hidden"
|
||||
width={200}
|
||||
/>
|
||||
<Image
|
||||
src={StackOceanLogoDark}
|
||||
alt="StakcOcean Logo"
|
||||
className="hidden rounded-lg pb-1 opacity-50 hover:opacity-100 dark:block"
|
||||
width={200}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden pt-10 md:block">
|
||||
<Button
|
||||
variant="highlight"
|
||||
className="mr-3 px-6"
|
||||
onClick={() => setVideoModal(true)}
|
||||
EndIcon={PlayCircleIcon}
|
||||
endIconClassName=" ml-2">
|
||||
Watch video
|
||||
onClick={() => {
|
||||
router.push("https://app.formbricks.com/auth/signup");
|
||||
plausible("Hero_CTA_CreateSurvey");
|
||||
}}>
|
||||
Create survey
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="px-6"
|
||||
onClick={() => {
|
||||
router.push("/demo");
|
||||
plausible("Hero_CTA_LaunchDemo");
|
||||
}}>
|
||||
Live demo
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TemplateList />
|
||||
<VideoWalkThrough open={videoModal} setOpen={() => setVideoModal(false)} />
|
||||
<div className="relative">
|
||||
<HeroAnimation fallbackImage={AnimationFallback} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function GitHubIcon(props: any) {
|
||||
return (
|
||||
<svg aria-hidden="true" viewBox="0 0 16 16" {...props}>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
51
apps/formbricks-com/components/home/HeroAnimation.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { LottiePlayer } from "lottie-web";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function HeroAnimation({ fallbackImage, ...props }: any) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [lottie, setLottie] = useState<LottiePlayer | null>(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/formbricks-open-source-survey-software-hero-animation-v1.json",
|
||||
});
|
||||
|
||||
animation.addEventListener("DOMLoaded", () => {
|
||||
setLoaded(true);
|
||||
});
|
||||
|
||||
return () => animation.destroy();
|
||||
}
|
||||
}, [lottie]);
|
||||
|
||||
return (
|
||||
<div className="relative" {...props}>
|
||||
<div ref={ref} />
|
||||
{!loaded && (
|
||||
<div className="absolute inset-0">
|
||||
<Image
|
||||
src={fallbackImage}
|
||||
alt="Fallback Image"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
objectPosition="center"
|
||||
className="transition-opacity duration-300"
|
||||
style={{ opacity: loaded ? 0 : 1 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,18 +7,18 @@ import Image from "next/image";
|
||||
export default function Highlights({}) {
|
||||
return (
|
||||
<>
|
||||
<div className="mx-auto mt-8 mb-12 max-w-lg md:mt-32 md:mb-0 md:max-w-none">
|
||||
<div className="mx-auto mb-12 mt-8 max-w-lg md:mb-0 md:mt-32 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="pb-8 md:pb-0">
|
||||
<h2 className="xs:text-3xl text-2xl font-bold tracking-tight text-slate-800 dark:text-slate-200">
|
||||
<h2 className="xs:text-3xl text-2xl font-bold leading-7 tracking-tight text-slate-800 dark:text-slate-200">
|
||||
Ask at the right moment,
|
||||
<br />
|
||||
<span className="font-light">get the data you need.</span>
|
||||
</h2>
|
||||
<p className="text-md mt-6 max-w-lg leading-7 text-slate-500 dark:text-slate-400">
|
||||
Follow up emails are so 2010. Ask users as they experience your product - and leverage a 3x
|
||||
higher conversion rate.
|
||||
Follow up emails are so 2010. Ask users as they experience your product - and leverage a
|
||||
significantly higher conversion rate.
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg bg-slate-100 py-6 pr-4 dark:bg-slate-800 sm:py-16 sm:pr-8">
|
||||
@@ -36,7 +36,7 @@ export default function Highlights({}) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 mb-12 max-w-lg md:mt-32 md:mb-0 md:max-w-none">
|
||||
<div className="mx-auto mb-12 mt-8 max-w-lg md:mb-0 md:mt-32 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="order-last rounded-lg bg-slate-100 p-4 dark:bg-slate-800 sm:p-8 md:order-first">
|
||||
@@ -48,10 +48,10 @@ export default function Highlights({}) {
|
||||
<Image src={ImageAttributesDark} alt="react library" className="hidden rounded-lg dark:block" />
|
||||
</div>
|
||||
<div className="pb-8 md:pb-0">
|
||||
<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.
|
||||
<h2 className="xs:text-3xl text-2xl font-bold leading-7 tracking-tight text-slate-800 dark:text-slate-100 sm:text-3xl">
|
||||
Dont' ‘Spray and pray’.
|
||||
<br />
|
||||
<span className="font-light">Segment users, granularly.</span>
|
||||
<span className="font-light">Pre-segment 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
|
||||
|
||||
@@ -82,7 +82,7 @@ export default function Steps() {
|
||||
heading="Set Formbricks up in minutes"
|
||||
subheading="Formbricks is designed for as little dev attention as possible. Here’s how:"
|
||||
/>
|
||||
<div id="howitworks" className="mx-auto mb-12 mt-16 max-w-lg md:mt-8 md:mb-0 md:max-w-none">
|
||||
<div id="howitworks" className="mx-auto mb-12 mt-16 max-w-lg md:mb-0 md:mt-8 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="pb-8 sm:pl-10 md:pb-0">
|
||||
@@ -101,7 +101,7 @@ export default function Steps() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 mb-12 max-w-lg md:mt-32 md:mb-0 md:max-w-none">
|
||||
<div className="mx-auto mb-12 mt-8 max-w-lg md:mb-0 md:mt-32 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="order-last w-full rounded-lg bg-slate-100 p-4 dark:bg-slate-800 sm:py-8 md:order-first">
|
||||
@@ -113,24 +113,24 @@ export default function Steps() {
|
||||
setAddEventModalOpen(true);
|
||||
}}>
|
||||
<CursorArrowRaysIcon className="mr-2 h-5 w-5 text-white" />
|
||||
Add No-Code Event
|
||||
Add Action
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pb-8 md:pb-0">
|
||||
<h4 className="text-brand-dark font-bold">Step 2</h4>
|
||||
<h2 className="xs:text-3xl text-2xl font-bold tracking-tight text-slate-800 dark:text-slate-100 sm:text-3xl">
|
||||
Setup No-Code events
|
||||
No-Code: Track User Actions
|
||||
</h2>
|
||||
<p className="text-md mt-6 max-w-lg leading-7 text-slate-500 dark:text-slate-400">
|
||||
Set up an event which can trigger your survey - without writing a single line of code. Surveys
|
||||
can be triggered on specific pages or after an element is clicked.
|
||||
Set up user actions which can trigger your survey without writing a single line of code.
|
||||
Surveys can be triggered on specific pages or after an element is clicked.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 mb-12 max-w-lg md:mt-32 md:mb-0 md:max-w-none">
|
||||
<div className="mx-auto mb-12 mt-8 max-w-lg md:mb-0 md:mt-32 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="pb-8 sm:pl-10 md:pb-0">
|
||||
@@ -149,7 +149,7 @@ export default function Steps() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 mb-12 max-w-lg md:mt-32 md:mb-0 md:max-w-none">
|
||||
<div className="mx-auto mb-12 mt-8 max-w-lg md:mb-0 md:mt-32 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="order-last w-full rounded-lg bg-slate-100 p-4 dark:bg-slate-800 sm:py-8 md:order-first">
|
||||
@@ -163,15 +163,14 @@ export default function Steps() {
|
||||
Set segment and trigger
|
||||
</h2>
|
||||
<p className="text-md mt-6 max-w-lg leading-7 text-slate-500 dark:text-slate-400">
|
||||
Create a custom segment for each survey. Use attributes and past events to only survey the
|
||||
people who have answers. Trigger your survey on any event in your application. Context
|
||||
matters.
|
||||
Create a custom segment for each survey. Use attributes and past user actions to only survey
|
||||
the people who have answers. Trigger your survey on any user action in your app.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 mb-12 max-w-lg md:mt-32 md:mb-0 md:max-w-none">
|
||||
<div className="mx-auto mb-12 mt-8 max-w-lg md:mb-0 md:mt-32 md:max-w-none">
|
||||
<div className="px-4 sm:max-w-4xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="grid md:grid-cols-2 md:items-center md:gap-16">
|
||||
<div className="pb-8 sm:pl-10 md:pb-0">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, ChangeEvent } from "react";
|
||||
import { ChevronDownIcon, ChevronRightIcon, ChevronLeftIcon } from "@heroicons/react/24/solid";
|
||||
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/solid";
|
||||
import clsx from "clsx";
|
||||
import { useState } from "react";
|
||||
|
||||
interface APICallProps {
|
||||
method: "GET" | "POST";
|
||||
@@ -59,10 +59,10 @@ export function APILayout({ method, url, description, headers, bodies, responses
|
||||
http://localhost:300
|
||||
<span className="font-bold text-black dark:text-slate-300">{url}</span>
|
||||
</div>
|
||||
<div className="mt-4 ml-8 font-bold dark:text-slate-400">{description}</div>
|
||||
<div className="ml-8 mt-4 font-bold dark:text-slate-400">{description}</div>
|
||||
<div>
|
||||
<div className={clsx(switchState ? "block" : "hidden", "ml-8")}>
|
||||
<p className="mt-6 mb-2 text-lg font-semibold">Parameters</p>
|
||||
<p className="mb-2 mt-6 text-lg font-semibold">Parameters</p>
|
||||
<div>
|
||||
{headers.length > 0 && (
|
||||
<div className="text-base">
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
OnboardingIcon,
|
||||
PMFIcon,
|
||||
DogChaserIcon,
|
||||
AngryBirdRageIcon,
|
||||
Button,
|
||||
CancelSubscriptionIcon,
|
||||
InterviewPromptIcon,
|
||||
DogChaserIcon,
|
||||
DoorIcon,
|
||||
FeedbackIcon,
|
||||
BugBlueIcon,
|
||||
AngryBirdRageIcon,
|
||||
FeatureRequestIcon,
|
||||
InterviewPromptIcon,
|
||||
OnboardingIcon,
|
||||
PMFIcon,
|
||||
} from "@formbricks/ui";
|
||||
import clsx from "clsx";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
const BestPractices = [
|
||||
{
|
||||
@@ -67,8 +68,10 @@ const BestPractices = [
|
||||
];
|
||||
|
||||
export default function InsightOppos() {
|
||||
const plausible = usePlausible();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="pt-12 pb-10 md:pt-20">
|
||||
<div className="pb-10 pt-12 md:pt-20">
|
||||
<div className="px-4 py-20 text-center sm:px-6 lg:px-8" id="best-practices">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-slate-800 dark:text-slate-200 sm:text-4xl md:text-5xl">
|
||||
Get started with{" "}
|
||||
@@ -85,11 +88,11 @@ export default function InsightOppos() {
|
||||
{BestPractices.map((bestPractice) => (
|
||||
<div
|
||||
key={bestPractice.title}
|
||||
className="drop-shadow-card duration-120 relative cursor-pointer rounded-lg bg-slate-100 p-8 transition-all ease-in-out hover:scale-105 dark:bg-slate-800">
|
||||
className="drop-shadow-card duration-120 relative rounded-lg bg-slate-100 p-8 transition-all ease-in-out hover:scale-105 dark:bg-slate-800">
|
||||
<div
|
||||
className={clsx(
|
||||
// base styles independent what type of button it is
|
||||
"absolute right-10 rounded-full py-1 px-3",
|
||||
"absolute right-10 rounded-full px-3 py-1",
|
||||
// different styles depending on type
|
||||
bestPractice.category === "Boost Retention" &&
|
||||
"bg-pink-100 text-pink-500 dark:bg-pink-800 dark:text-pink-200",
|
||||
@@ -103,7 +106,7 @@ export default function InsightOppos() {
|
||||
<div className="h-12 w-12">
|
||||
<bestPractice.icon className="h-12 w-12 " />
|
||||
</div>
|
||||
<h3 className="mt-3 mb-1 text-xl font-bold text-slate-700 dark:text-slate-200">
|
||||
<h3 className="mb-1 mt-3 text-xl font-bold text-slate-700 dark:text-slate-200">
|
||||
{bestPractice.title}
|
||||
</h3>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">{bestPractice.description}</p>
|
||||
@@ -111,6 +114,16 @@ export default function InsightOppos() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-4 w-fit px-4 py-2 text-center">
|
||||
<Button
|
||||
variant="highlight"
|
||||
onClick={() => {
|
||||
router.push("/demo");
|
||||
plausible("subPractices_CTA_LaunchDemo");
|
||||
}}>
|
||||
Launch Live Demo
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -21,14 +21,14 @@ 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="relative px-4 py-8 sm:px-6 sm:pb-12 sm:pt-8 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">
|
||||
<Button
|
||||
variant="highlight"
|
||||
onClick={() => {
|
||||
plausible("openDemo");
|
||||
plausible("Breaker_CTAs");
|
||||
router.push(`${href}`);
|
||||
}}>
|
||||
{cta}
|
||||
|
||||
@@ -6,20 +6,20 @@ export default function CTA() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<>
|
||||
<div className="mx-auto px-4 py-16 sm:px-6 lg:px-8 lg:pt-24 lg:pb-40">
|
||||
<div className="mx-auto px-4 py-16 sm:px-6 lg:px-8 lg:pb-40 lg:pt-24">
|
||||
<HeadingCentered closer teaser="Get started" heading="Ready for the last form tool you need?" />
|
||||
|
||||
<div className="mt-12 grid grid-cols-1 content-center md:grid-cols-2">
|
||||
<div className="-mb-4 rounded-t-xl bg-gradient-to-br from-slate-300 to-slate-200 px-8 py-24 text-center text-slate-900 dark:from-slate-800 dark:to-slate-900 dark:text-slate-100 md:mb-0 md:ml-2.5 md:-mr-5 md:rounded-l-xl lg:p-24">
|
||||
<div className="-mb-4 rounded-t-xl bg-gradient-to-br from-slate-300 to-slate-200 px-8 py-24 text-center text-slate-900 dark:from-slate-800 dark:to-slate-900 dark:text-slate-100 md:-mr-5 md:mb-0 md:ml-2.5 md:rounded-l-xl lg:p-24">
|
||||
<h3 className="text-3xl font-bold">Self-hosted</h3>
|
||||
<p className="mt-2 mb-4">Run locally e.g. with docker-compose.</p>
|
||||
<p className="mb-4 mt-2">Run locally e.g. with docker-compose.</p>
|
||||
<Button variant="secondary" onClick={() => router.push("/docs")} className="mt-3">
|
||||
Read docs
|
||||
</Button>
|
||||
</div>
|
||||
<div className="rounded-xl bg-gradient-to-br from-slate-400 to-slate-300 py-24 text-center text-slate-800 dark:from-slate-800 dark:to-slate-700 dark:text-slate-100">
|
||||
<h3 className="text-3xl font-bold">Cloud</h3>
|
||||
<p className="mt-2 mb-4">Use our free managed service.</p>
|
||||
<p className="mb-4 mt-2">Use our free managed service.</p>
|
||||
<Button variant="secondary" onClick={() => router.push("/waitlist")} className="mt-3" disabled>
|
||||
Coming soon
|
||||
</Button>
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import Image from "next/image";
|
||||
import EarlyBird from "@/images/early bird deal for open source jotform alternative typeform and surveymonkey_v2.svg";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function EarlyBirdDeal() {
|
||||
const router = useRouter();
|
||||
const plausible = usePlausible();
|
||||
return (
|
||||
<div className="bg-brand-dark relative mx-4 max-w-7xl overflow-hidden rounded-xl p-6 pb-16 sm:p-8 sm:pb-16 md:py-8 md:px-12 lg:mx-0 lg:flex lg:items-center">
|
||||
<div className="bg-brand-dark relative mx-4 max-w-7xl overflow-hidden rounded-xl p-6 pb-16 sm:p-8 sm:pb-16 md:px-12 md:py-8 lg:mx-0 lg:flex lg:items-center">
|
||||
<div className="lg:w-0 lg:flex-1 ">
|
||||
<h2
|
||||
className="mb-1 text-2xl font-bold tracking-tight text-white sm:text-2xl"
|
||||
id="newsletter-headline">
|
||||
50% off for early birds.
|
||||
50% off for Early Birds.
|
||||
</h2>
|
||||
<h2 className="text-xl font-semibold tracking-tight text-slate-200 sm:text-lg">
|
||||
Limited Early Bird deal. Only{" "}
|
||||
Limited deal: Only{" "}
|
||||
<span className="bg- rounded-sm bg-slate-200/40 px-2 py-0.5 text-slate-100">17</span> left.
|
||||
</h2>
|
||||
|
||||
<div className="mt-6">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="dark:bg-slate-200 dark:text-slate-700 dark:hover:bg-slate-300"
|
||||
onClick={() => {
|
||||
plausible("openEarlyBird");
|
||||
plausible("Pricing_CTA_EarlyBird");
|
||||
window.open("https://app.formbricks.com/auth/signup", "_blank")?.focus();
|
||||
}}>
|
||||
Get Early Bird Deal
|
||||
Get Early Bird deal
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-2 mb-24 max-w-3xl text-xs tracking-tight text-slate-200 md:mb-0 md:max-w-sm lg:max-w-none">
|
||||
<p className="mb-24 mt-2 max-w-3xl text-xs tracking-tight text-slate-200 md:mb-0 md:max-w-sm lg:max-w-none">
|
||||
This saves you $588 every year.
|
||||
</p>
|
||||
<div className="absolute -right-20 -bottom-36 mx-auto h-96 w-96 scale-75 sm:-right-10">
|
||||
<div className="absolute -bottom-36 -right-20 mx-auto h-96 w-96 scale-75 sm:-right-10">
|
||||
<Image src={EarlyBird} fill alt="formbricks favicon open source forms typeform alternative" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import clsx from "clsx";
|
||||
import { FooterLogo } from "./Logo";
|
||||
|
||||
const navigation = {
|
||||
/* creation: [
|
||||
{ name: "React Form Builder", href: "/react-form-library", status: true },
|
||||
{ name: "No-Code Builder", href: "/visual-builder", 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: "Formbricks HQ", href: "/formbricks-hq", status: true },
|
||||
{ name: "Reports", href: "#", status: false },
|
||||
], */
|
||||
other: [
|
||||
{ name: "Community", href: "/community", status: true },
|
||||
{ name: "Blog", href: "/blog", status: true },
|
||||
@@ -58,7 +42,7 @@ export default function Footer() {
|
||||
<h2 id="footer-heading" className="sr-only">
|
||||
Footer
|
||||
</h2>
|
||||
<div className="mx-auto flex max-w-7xl flex-col space-y-6 px-4 py-12 text-center sm:px-6 lg:py-16 lg:px-8">
|
||||
<div className="mx-auto flex max-w-7xl flex-col space-y-6 px-4 py-12 text-center sm:px-6 lg:px-8 lg:py-16">
|
||||
<Link href="/">
|
||||
<span className="sr-only">Formbricks</span>
|
||||
<FooterLogo className="mx-auto h-8 w-auto sm:h-10" />
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { StarIcon } from "@heroicons/react/24/solid";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { Fragment } from "react";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { FooterLogo } from "./Logo";
|
||||
import { ThemeSelector } from "./ThemeSelector";
|
||||
|
||||
export default function Header() {
|
||||
/*
|
||||
const [videoModal, setVideoModal] = useState(false); */
|
||||
const plausible = usePlausible();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Popover className="relative" as="header">
|
||||
@@ -30,30 +35,54 @@ export default function Header() {
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Community
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/blog"
|
||||
href="https://formbricks.com/#pricing"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Blog <p className="bg-brand inline rounded-full px-2 text-xs text-white">1</p>
|
||||
Pricing
|
||||
</Link>
|
||||
<Link
|
||||
href="/docs"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Docs
|
||||
</Link>
|
||||
<Link
|
||||
href="/blog"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Blog{/* <p className="bg-brand inline rounded-full px-2 text-xs text-white">1</p> */}
|
||||
</Link>
|
||||
</Popover.Group>
|
||||
<div className="hidden flex-1 items-center justify-end md:flex">
|
||||
<ThemeSelector className="relative z-10 mr-5" />
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="group px-2"
|
||||
href="https://formbricks.com/github"
|
||||
target="_blank">
|
||||
<StarIcon className="h-6 w-6 text-amber-500 group-hover:text-amber-400" />
|
||||
</Button>
|
||||
{/* <Button variant="secondary" className="ml-2 px-2" onClick={() => setVideoModal(true)}>
|
||||
<VideoWalkThrough open={videoModal} setOpen={() => setVideoModal(false)} />
|
||||
<PlayCircleIcon className="h-6 w-6" />
|
||||
</Button> */}
|
||||
|
||||
{/* <Button
|
||||
variant="secondary"
|
||||
EndIcon={GitHubIcon}
|
||||
endIconClassName="fill-slate-800 ml-2 dark:fill-slate-200"
|
||||
href="https://github.com/formbricks/formbricks"
|
||||
target="_blank">
|
||||
View on Github
|
||||
</Button>
|
||||
{/* <Button variant="highlight" className="ml-2" onClick={() => router.push("/waitlist")}>
|
||||
Get Access
|
||||
</Button> */}
|
||||
<Button
|
||||
variant="highlight"
|
||||
className="ml-2"
|
||||
onClick={() => {
|
||||
router.push("https://app.formbricks.com");
|
||||
plausible("NavBar_CTA_Login");
|
||||
}}>
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +98,7 @@ export default function Header() {
|
||||
focus
|
||||
className="absolute inset-x-0 top-0 z-20 origin-top-right transform p-2 transition md:hidden">
|
||||
<div className="dark:divide-slate divide-y-2 divide-slate-100 rounded-lg bg-slate-200 shadow-lg ring-1 ring-black ring-opacity-5 dark:divide-slate-700 dark:bg-slate-800">
|
||||
<div className="px-5 pt-5 pb-6">
|
||||
<div className="px-5 pb-6 pt-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<FooterLogo className="h-8 w-auto" />
|
||||
@@ -85,20 +114,22 @@ export default function Header() {
|
||||
<div className="px-5 py-6">
|
||||
<div className="flex flex-col space-y-5 text-center text-sm dark:text-slate-300">
|
||||
<Link href="/community">Community</Link>
|
||||
<Link href="#pricing">Pricing</Link>
|
||||
<Link href="/docs">Docs</Link>
|
||||
<Link href="/blog">Blog</Link>
|
||||
<Button
|
||||
{/* <Button
|
||||
variant="secondary"
|
||||
EndIcon={GitHubIcon}
|
||||
onClick={() => router.push("https://github.com/formbricks/formbricks")}
|
||||
className="flex w-full justify-center fill-slate-800 dark:fill-slate-200">
|
||||
View on Github
|
||||
</Button>
|
||||
{/* <Button
|
||||
variant="primary"
|
||||
onClick={() => router.push("/waitlist")}
|
||||
className="flex w-full justify-center">
|
||||
Get access
|
||||
</Button> */}
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => router.push("https://app.formbricks.com/auth/signup")}
|
||||
className="flex w-full justify-center">
|
||||
Get started
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,11 +138,3 @@ export default function Header() {
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function GitHubIcon(props: any) {
|
||||
return (
|
||||
<svg aria-hidden="true" viewBox="0 0 16 16" {...props}>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
43
apps/formbricks-com/components/shared/HeaderLight.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { Popover } from "@headlessui/react";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { FooterLogo } from "./Logo";
|
||||
|
||||
export default function HeaderLight() {
|
||||
const plausible = usePlausible();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Popover className="relative" as="header">
|
||||
<div className=" max-w-8xl mx-auto flex items-center justify-between py-6 sm:px-2 md:justify-start lg:px-8 xl:px-12 ">
|
||||
<div className="flex w-0 flex-1 justify-start">
|
||||
<Link href="/">
|
||||
<span className="sr-only">Formbricks</span>
|
||||
<FooterLogo className="ml-7 h-8 w-auto sm:h-10" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="hidden flex-1 items-center justify-end md:flex">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
router.push("https://cal.com/johannes/onboarding");
|
||||
plausible("Demo_CTA_TalkToUs");
|
||||
}}>
|
||||
Talk to us
|
||||
</Button>
|
||||
<Button
|
||||
variant="highlight"
|
||||
className="ml-2"
|
||||
onClick={() => {
|
||||
router.push("https://app.formbricks.com/auth/signup");
|
||||
plausible("Demo_CTA_TryForFree");
|
||||
}}>
|
||||
Create surveys for free
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { Fragment } from "react";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { FooterLogo } from "./Logo";
|
||||
import { ThemeSelector } from "./ThemeSelector";
|
||||
import { usePlausible } from "next-plausible";
|
||||
|
||||
export default function Header() {
|
||||
const router = useRouter();
|
||||
const plausible = usePlausible();
|
||||
return (
|
||||
<Popover className="relative" as="header">
|
||||
<div className="flex items-center justify-between px-4 py-6 sm:px-6 md:justify-start ">
|
||||
<div className="flex w-0 flex-1 justify-start">
|
||||
<Link href="/">
|
||||
<span className="sr-only">Formbricks</span>
|
||||
<FooterLogo className="h-8 w-auto sm:h-10" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="-my-2 -mr-2 md:hidden">
|
||||
<Popover.Button className="inline-flex items-center justify-center rounded-md bg-slate-100 p-2 text-slate-400 hover:bg-slate-100 hover:text-slate-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-teal-500 dark:bg-slate-700 dark:text-slate-200">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
|
||||
</Popover.Button>
|
||||
</div>
|
||||
<Popover.Group as="nav" className="hidden space-x-10 md:flex">
|
||||
<Link
|
||||
href="#howitworks"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
How it works
|
||||
</Link>
|
||||
<Link
|
||||
href="#pricing"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Pricing <p className="bg-brand inline rounded-full px-2 text-xs text-white">50%</p>
|
||||
</Link>
|
||||
<Link
|
||||
href="/docs"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Docs
|
||||
</Link>
|
||||
</Popover.Group>
|
||||
<div className="hidden flex-1 items-center justify-end md:flex">
|
||||
<ThemeSelector className="relative z-10 mr-5" />
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="ml-2"
|
||||
onClick={() => {
|
||||
plausible("openDemo");
|
||||
window.open("https://app.formbricks.com/demo", "_blank")?.focus();
|
||||
}}>
|
||||
Try Demo
|
||||
</Button>
|
||||
<Button
|
||||
variant="highlight"
|
||||
className="ml-2"
|
||||
onClick={() => {
|
||||
plausible("openSignUp");
|
||||
window.open("https://app.formbricks.com/auth/signup", "_blank")?.focus();
|
||||
}}>
|
||||
Sign Up
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="duration-200 ease-out"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-100 ease-in"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95">
|
||||
<Popover.Panel
|
||||
focus
|
||||
className="absolute inset-x-0 top-0 z-20 origin-top-right transform p-2 transition md:hidden">
|
||||
<div className="dark:divide-slate divide-y-2 divide-slate-100 rounded-lg bg-slate-200 shadow-lg ring-1 ring-black ring-opacity-5 dark:divide-slate-700 dark:bg-slate-800">
|
||||
<div className="px-5 pt-5 pb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<FooterLogo className="h-8 w-auto" />
|
||||
</div>
|
||||
<div className="-mr-2">
|
||||
<Popover.Button className="inline-flex items-center justify-center rounded-md bg-white p-2 text-slate-400 hover:bg-slate-100 hover:text-slate-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-teal-500 dark:bg-slate-700 dark:text-slate-200">
|
||||
<span className="sr-only">Close menu</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</Popover.Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-6">
|
||||
<div className="flex flex-col space-y-5 text-center text-sm dark:text-slate-300">
|
||||
<Link href="#howitworks">How it works</Link>
|
||||
<Link href="#pricing">Pricing</Link>
|
||||
<Link href="/docs">Docs</Link>
|
||||
<Button
|
||||
variant="secondary"
|
||||
target="_blank"
|
||||
onClick={() => router.push("https://app.formbricks.com/demo")}
|
||||
className="flex w-full justify-center fill-slate-800 dark:fill-slate-200">
|
||||
Try Demo
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
target="_blank"
|
||||
onClick={() => router.push("https://app.formbricks.com/auth/signup")}
|
||||
className="flex w-full justify-center">
|
||||
Sign Up
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function GitHubIcon(props: any) {
|
||||
return (
|
||||
<svg aria-hidden="true" viewBox="0 0 16 16" {...props}>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,471 +0,0 @@
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/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 "@formbricks/ui";
|
||||
import { FooterLogo } from "./Logo";
|
||||
import { ThemeSelector } from "./ThemeSelector";
|
||||
|
||||
const creation = [
|
||||
{
|
||||
name: "React Library",
|
||||
description: "Build surveys with React.js",
|
||||
href: "/react-form-library",
|
||||
icon: CodeBracketSquareIcon,
|
||||
status: true,
|
||||
},
|
||||
{
|
||||
name: "No-Code Builder",
|
||||
description: "Notion-like visual builder",
|
||||
href: "/visual-builder",
|
||||
icon: CursorArrowRaysIcon,
|
||||
status: false,
|
||||
},
|
||||
{
|
||||
name: "Templates",
|
||||
description: "CSAT, PMF survey, etc.",
|
||||
href: "#",
|
||||
icon: ClipboardDocumentListIcon,
|
||||
status: false,
|
||||
},
|
||||
];
|
||||
|
||||
const pipes = [
|
||||
{
|
||||
name: "Core API",
|
||||
description: "The OS survey 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: "Formbricks HQ",
|
||||
description: "Manage submissions easily",
|
||||
href: "/formbricks-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 (
|
||||
<Popover className="relative" as="header">
|
||||
<div className="flex items-center justify-between px-4 py-6 sm:px-6 md:justify-start md:space-x-10">
|
||||
<div className="flex justify-start lg:w-0 lg:flex-1">
|
||||
<Link href="/">
|
||||
<span className="sr-only">Formbricks</span>
|
||||
<FooterLogo className="h-8 w-auto sm:h-10" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="-my-2 -mr-2 md:hidden">
|
||||
<Popover.Button className="inline-flex items-center justify-center rounded-md bg-slate-100 p-2 text-slate-400 hover:bg-slate-100 hover:text-slate-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-teal-500 dark:bg-slate-700 dark:text-slate-200">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
|
||||
</Popover.Button>
|
||||
</div>
|
||||
<Popover.Group as="nav" className="hidden space-x-10 md:flex">
|
||||
<Popover className="relative">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={clsx(
|
||||
open ? "text-slate-700" : "text-slate-400",
|
||||
"group inline-flex items-center rounded-md text-base font-medium hover:text-slate-700 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 dark:hover:text-slate-300"
|
||||
)}>
|
||||
<span>Bricks</span>
|
||||
<ChevronDownIcon className="ml-2 h-5 w-5" aria-hidden="true" />
|
||||
</Popover.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1">
|
||||
<Popover.Panel className="absolute z-10 mt-3 -ml-4 w-screen max-w-lg transform lg:left-1/2 lg:ml-0 lg:max-w-4xl lg:-translate-x-1/2">
|
||||
<div className="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5">
|
||||
<div className="relative grid gap-6 bg-slate-50 px-5 py-6 dark:bg-slate-700 sm:gap-6 sm:p-8 lg:grid-cols-3">
|
||||
<div>
|
||||
<h4 className="mb-6 ml-16 text-sm text-slate-400">Survey Creation</h4>
|
||||
{creation.map((brick) => (
|
||||
<Link
|
||||
key={brick.name}
|
||||
href={brick.href}
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-600 dark:hover:bg-opacity-50"
|
||||
: "cursor-default",
|
||||
"-m-3 flex items-start rounded-lg p-3 py-4"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
brick.status ? "text-brand-dark dark:text-brand-light" : "text-slate-500",
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md sm:h-12 sm:w-12"
|
||||
)}>
|
||||
<brick.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-800 dark:text-slate-50"
|
||||
: "text-slate-500 dark:text-slate-400",
|
||||
"text-lg font-semibold"
|
||||
)}>
|
||||
{brick.name}
|
||||
</p>
|
||||
<p className="text-sm text-slate-400 dark:text-slate-500">
|
||||
{brick.description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mb-6 ml-16 text-sm text-slate-400">Data Pipelines</h4>
|
||||
{pipes.map((brick) => (
|
||||
<Link
|
||||
key={brick.name}
|
||||
href={brick.href}
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-600 dark:hover:bg-opacity-50"
|
||||
: "cursor-default",
|
||||
"-m-3 flex items-start rounded-lg p-3 py-4"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
brick.status ? "text-brand-dark dark:text-brand-light" : "text-slate-500",
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md sm:h-12 sm:w-12"
|
||||
)}>
|
||||
<brick.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-800 dark:text-slate-50"
|
||||
: "text-slate-500 dark:text-slate-400",
|
||||
"text-lg font-semibold"
|
||||
)}>
|
||||
{brick.name}
|
||||
</p>
|
||||
<p className="text-sm text-slate-400 dark:text-slate-500">
|
||||
{brick.description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mb-6 ml-16 text-sm text-slate-400">Data Insights</h4>
|
||||
{insights.map((brick) => (
|
||||
<Link
|
||||
key={brick.name}
|
||||
href={brick.href}
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-600 dark:hover:bg-opacity-50"
|
||||
: "cursor-default",
|
||||
"-m-3 flex items-start rounded-lg p-3 py-4"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
brick.status ? "text-brand-dark dark:text-brand-light" : "text-slate-500",
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md sm:h-12 sm:w-12"
|
||||
)}>
|
||||
<brick.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-800 dark:text-slate-50"
|
||||
: "text-slate-500 dark:text-slate-400",
|
||||
"text-lg font-semibold"
|
||||
)}>
|
||||
{brick.name}
|
||||
</p>
|
||||
<p className="text-sm text-slate-400 dark:text-slate-500">
|
||||
{brick.description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
<Link
|
||||
href="/community"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Community
|
||||
</Link>
|
||||
<Link
|
||||
href="/blog"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Blog <p className="bg-brand inline rounded-full px-2 text-xs text-white">1</p>
|
||||
</Link>
|
||||
<Link
|
||||
href="/docs"
|
||||
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
|
||||
Docs
|
||||
</Link>
|
||||
</Popover.Group>
|
||||
<div className="hidden items-center justify-end md:flex md:flex-1 lg:w-0">
|
||||
<ThemeSelector className="relative z-10 mr-5" />
|
||||
<Button
|
||||
variant="secondary"
|
||||
EndIcon={GitHubIcon}
|
||||
endIconClassName="fill-slate-800 dark:fill-slate-200"
|
||||
onClick={() => router.push("https://github.com/formbricks/formbricks")}>
|
||||
View on Github
|
||||
</Button>
|
||||
<Button variant="highlight" className="ml-2" onClick={() => router.push("/get-started")}>
|
||||
Get started
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="duration-200 ease-out"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-100 ease-in"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95">
|
||||
<Popover.Panel
|
||||
focus
|
||||
className="absolute inset-x-0 top-0 z-20 origin-top-right transform p-2 transition md:hidden">
|
||||
<div className="dark:divide-slate divide-y-2 divide-slate-100 rounded-lg bg-slate-200 shadow-lg ring-1 ring-black ring-opacity-5 dark:divide-slate-700 dark:bg-slate-800">
|
||||
<div className="px-5 pt-5 pb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<FooterLogo className="h-8 w-auto" />
|
||||
</div>
|
||||
<div className="-mr-2">
|
||||
<Popover.Button className="inline-flex items-center justify-center rounded-md bg-white p-2 text-slate-400 hover:bg-slate-100 hover:text-slate-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-teal-500 dark:bg-slate-700 dark:text-slate-200">
|
||||
<span className="sr-only">Close menu</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</Popover.Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="relative bg-slate-200 px-5 py-6 dark:bg-slate-800">
|
||||
<div>
|
||||
<h4 className="mb-3 text-sm text-slate-900 dark:text-slate-300">Survey Creation</h4>
|
||||
{creation.map((brick) => (
|
||||
<Link
|
||||
key={brick.name}
|
||||
href={brick.href}
|
||||
className={clsx(
|
||||
brick.status ? "cursor-pointer" : "cursor-default",
|
||||
"-m-3 flex items-start rounded-lg p-3 py-3"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
brick.status ? "text-brand-dark dark:text-brand-light" : "text-slate-500",
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md sm:h-12 sm:w-12"
|
||||
)}>
|
||||
<brick.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-900 dark:text-slate-200"
|
||||
: "text-slate-400 dark:text-slate-500",
|
||||
"text-lg font-semibold"
|
||||
)}>
|
||||
{brick.name}
|
||||
</p>
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-900 dark:text-slate-400"
|
||||
: "text-slate-400 dark:text-slate-600",
|
||||
"text-sm"
|
||||
)}>
|
||||
{brick.description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mt-8 mb-3 text-sm text-slate-900 dark:text-slate-300">Data Pipelines</h4>
|
||||
{pipes.map((brick) => (
|
||||
<Link
|
||||
key={brick.name}
|
||||
href={brick.href}
|
||||
className={clsx(
|
||||
brick.status ? "cursor-pointer" : "cursor-default",
|
||||
"-m-3 flex items-start rounded-lg p-3 py-3"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
brick.status ? "text-brand-dark dark:text-brand-light" : "text-slate-500",
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md sm:h-12 sm:w-12"
|
||||
)}>
|
||||
<brick.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-900 dark:text-slate-200"
|
||||
: "text-slate-400 dark:text-slate-500",
|
||||
"text-lg font-semibold"
|
||||
)}>
|
||||
{brick.name}
|
||||
</p>
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-900 dark:text-slate-400"
|
||||
: "text-slate-400 dark:text-slate-600",
|
||||
"text-sm"
|
||||
)}>
|
||||
{brick.description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mt-8 mb-3 text-sm text-slate-900 dark:text-slate-300">Data Insights</h4>
|
||||
{insights.map((brick) => (
|
||||
<Link
|
||||
key={brick.name}
|
||||
href={brick.href}
|
||||
className={clsx(
|
||||
brick.status ? "cursor-pointer" : "cursor-default",
|
||||
"-m-3 flex items-start rounded-lg p-3 py-3"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
brick.status ? "text-brand-dark dark:text-brand-light" : "text-slate-500",
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md sm:h-12 sm:w-12"
|
||||
)}>
|
||||
<brick.icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-900 dark:text-slate-200"
|
||||
: "text-slate-400 dark:text-slate-500",
|
||||
"text-lg font-semibold"
|
||||
)}>
|
||||
{brick.name}
|
||||
</p>
|
||||
<p
|
||||
className={clsx(
|
||||
brick.status
|
||||
? "text-slate-900 dark:text-slate-400"
|
||||
: "text-slate-400 dark:text-slate-600",
|
||||
"text-sm"
|
||||
)}>
|
||||
{brick.description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="px-5 py-6">
|
||||
<div className="grid grid-cols-3 text-center text-sm font-medium text-slate-900 hover:text-slate-700 dark:text-slate-200 sm:text-base">
|
||||
<Link href="/community">Community</Link>
|
||||
|
||||
<Link href="/blog">Blog</Link>
|
||||
|
||||
<Link href="/docs">Documentation</Link>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<Button
|
||||
variant="secondary"
|
||||
EndIcon={GitHubIcon}
|
||||
onClick={() => router.push("https://github.com/formbricks/formbricks")}
|
||||
className="flex w-full justify-center fill-slate-800 dark:fill-slate-200">
|
||||
View on Github
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => router.push("/get-started")}
|
||||
className="mt-3 flex w-full justify-center">
|
||||
Get started
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function GitHubIcon(props: any) {
|
||||
return (
|
||||
<svg aria-hidden="true" viewBox="0 0 16 16" {...props}>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import Image from "next/image";
|
||||
import clsx from "clsx";
|
||||
import Highlight, { defaultProps } from "prism-react-renderer";
|
||||
|
||||
import { Button } from "@formbricks/ui";
|
||||
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 (
|
||||
<svg aria-hidden="true" viewBox="0 0 42 10" fill="none" {...props}>
|
||||
<circle cx="5" cy="5" r="4.5" />
|
||||
<circle cx="21" cy="5" r="4.5" />
|
||||
<circle cx="37" cy="5" r="4.5" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<div className="overflow-hidden bg-slate-900 dark:-mb-32 dark:mt-[-4.5rem] dark:pb-32 dark:pt-[4.5rem] dark:lg:mt-[-4.75rem] dark:lg:pt-[4.75rem]">
|
||||
<div className="py-16 sm:px-2 lg:relative lg:py-20 lg:px-0">
|
||||
<div className="mx-auto grid max-w-2xl grid-cols-1 items-center gap-y-16 gap-x-8 px-4 lg:max-w-8xl lg:grid-cols-2 lg:px-8 xl:gap-x-16 xl:px-12">
|
||||
<div className="relative z-10 md:text-center lg:text-left">
|
||||
<Image
|
||||
className="absolute bottom-full right-full -mr-72 -mb-56 opacity-50"
|
||||
src={blurCyanImage}
|
||||
alt=""
|
||||
width={530}
|
||||
height={530}
|
||||
unoptimized
|
||||
priority
|
||||
/>
|
||||
<div className="relative">
|
||||
<p className="inline bg-gradient-to-r from-indigo-200 via-slate-400 to-indigo-200 bg-clip-text font-display text-5xl tracking-tight text-transparent">
|
||||
Never miss the cache again.
|
||||
</p>
|
||||
<p className="mt-3 text-2xl tracking-tight text-slate-400">
|
||||
Cache every single thing your app could ever do ahead of time, so your code never even has to
|
||||
run at all.
|
||||
</p>
|
||||
<div className="mt-8 flex gap-4 md:justify-center lg:justify-start">
|
||||
<Button href="/">Get started</Button>
|
||||
<Button href="/" variant="secondary">
|
||||
View on GitHub
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative lg:static xl:pl-10">
|
||||
<div className="absolute inset-x-[-50vw] -top-32 -bottom-48 [mask-image:linear-gradient(transparent,white,white)] dark:[mask-image:linear-gradient(transparent,white,transparent)] lg:left-[calc(50%+14rem)] lg:right-0 lg:-top-32 lg:-bottom-32 lg:[mask-image:none] lg:dark:[mask-image:linear-gradient(white,white,transparent)]">
|
||||
<HeroBackground className="absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2 lg:left-0 lg:translate-x-0 lg:translate-y-[-60%]" />
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Image
|
||||
className="absolute -top-64 -right-64"
|
||||
src={blurCyanImage}
|
||||
alt=""
|
||||
width={530}
|
||||
height={530}
|
||||
unoptimized
|
||||
priority
|
||||
/>
|
||||
<Image
|
||||
className="absolute -bottom-40 -right-44"
|
||||
src={blurIndigoImage}
|
||||
alt=""
|
||||
width={567}
|
||||
height={567}
|
||||
unoptimized
|
||||
priority
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-tr from-slate-300 via-slate-300/70 to-slate-300 opacity-10 blur-lg" />
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-tr from-slate-300 via-slate-300/70 to-slate-300 opacity-10" />
|
||||
<div className="relative rounded-2xl bg-[#0A101F]/80 ring-1 ring-white/10 backdrop-blur">
|
||||
<div className="absolute -top-px left-20 right-11 h-px bg-gradient-to-r from-slate-300/0 via-slate-300/70 to-slate-300/0" />
|
||||
<div className="absolute -bottom-px left-11 right-20 h-px bg-gradient-to-r from-slate-400/0 via-slate-400 to-slate-400/0" />
|
||||
<div className="pl-4 pt-4">
|
||||
<TrafficLightsIcon className="h-2.5 w-auto stroke-slate-500/30" />
|
||||
<div className="mt-4 flex space-x-2 text-xs">
|
||||
{tabs.map((tab) => (
|
||||
<div
|
||||
key={tab.name}
|
||||
className={clsx(
|
||||
"flex h-6 rounded-full",
|
||||
tab.isActive
|
||||
? "bg-gradient-to-r from-slate-400/30 via-slate-400 to-slate-400/30 p-px font-medium text-slate-300"
|
||||
: "text-slate-500"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center rounded-full px-2.5",
|
||||
tab.isActive && "bg-slate-800"
|
||||
)}>
|
||||
{tab.name}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 flex items-start px-1 text-sm">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="select-none border-r border-slate-300/5 pr-4 font-mono text-slate-600">
|
||||
{Array.from({
|
||||
length: code.split("\n").length,
|
||||
}).map((_, index) => (
|
||||
<Fragment key={index}>
|
||||
{(index + 1).toString().padStart(2, "0")}
|
||||
<br />
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
<Highlight {...defaultProps} code={code} language={codeLanguage} theme={undefined}>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre className={clsx(className, "flex overflow-x-auto pb-6")} style={style}>
|
||||
<code className="px-4">
|
||||
{tokens.map((line, lineIndex) => (
|
||||
<div key={lineIndex} {...getLineProps({ line })}>
|
||||
{line.map((token, tokenIndex) => (
|
||||
<span key={tokenIndex} {...getTokenProps({ token })} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
import { useId } from 'react'
|
||||
|
||||
export function HeroBackground(props) {
|
||||
let id = useId()
|
||||
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 668 1069"
|
||||
width={668}
|
||||
height={1069}
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id={`${id}-clip-path`}>
|
||||
<path
|
||||
fill="#fff"
|
||||
transform="rotate(-180 334 534.4)"
|
||||
d="M0 0h668v1068.8H0z"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g opacity=".4" clipPath={`url(#${id}-clip-path)`} strokeWidth={4}>
|
||||
<path
|
||||
opacity=".3"
|
||||
d="M584.5 770.4v-474M484.5 770.4v-474M384.5 770.4v-474M283.5 769.4v-474M183.5 768.4v-474M83.5 767.4v-474"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<path
|
||||
d="M83.5 221.275v6.587a50.1 50.1 0 0 0 22.309 41.686l55.581 37.054a50.102 50.102 0 0 1 22.309 41.686v6.587M83.5 716.012v6.588a50.099 50.099 0 0 0 22.309 41.685l55.581 37.054a50.102 50.102 0 0 1 22.309 41.686v6.587M183.7 584.5v6.587a50.1 50.1 0 0 0 22.31 41.686l55.581 37.054a50.097 50.097 0 0 1 22.309 41.685v6.588M384.101 277.637v6.588a50.1 50.1 0 0 0 22.309 41.685l55.581 37.054a50.1 50.1 0 0 1 22.31 41.686v6.587M384.1 770.288v6.587a50.1 50.1 0 0 1-22.309 41.686l-55.581 37.054A50.099 50.099 0 0 0 283.9 897.3v6.588"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<path
|
||||
d="M384.1 770.288v6.587a50.1 50.1 0 0 1-22.309 41.686l-55.581 37.054A50.099 50.099 0 0 0 283.9 897.3v6.588M484.3 594.937v6.587a50.1 50.1 0 0 1-22.31 41.686l-55.581 37.054A50.1 50.1 0 0 0 384.1 721.95v6.587M484.3 872.575v6.587a50.1 50.1 0 0 1-22.31 41.686l-55.581 37.054a50.098 50.098 0 0 0-22.309 41.686v6.582M584.501 663.824v39.988a50.099 50.099 0 0 1-22.31 41.685l-55.581 37.054a50.102 50.102 0 0 0-22.309 41.686v6.587M283.899 945.637v6.588a50.1 50.1 0 0 1-22.309 41.685l-55.581 37.05a50.12 50.12 0 0 0-22.31 41.69v6.59M384.1 277.637c0 19.946 12.763 37.655 31.686 43.962l137.028 45.676c18.923 6.308 31.686 24.016 31.686 43.962M183.7 463.425v30.69c0 21.564 13.799 40.709 34.257 47.529l134.457 44.819c18.922 6.307 31.686 24.016 31.686 43.962M83.5 102.288c0 19.515 13.554 36.412 32.604 40.645l235.391 52.309c19.05 4.234 32.605 21.13 32.605 40.646M83.5 463.425v-58.45M183.699 542.75V396.625M283.9 1068.8V945.637M83.5 363.225v-141.95M83.5 179.524v-77.237M83.5 60.537V0M384.1 630.425V277.637M484.301 830.824V594.937M584.5 1068.8V663.825M484.301 555.275V452.988M584.5 622.075V452.988M384.1 728.537v-56.362M384.1 1068.8v-20.88M384.1 1006.17V770.287M283.9 903.888V759.85M183.699 1066.71V891.362M83.5 1068.8V716.012M83.5 674.263V505.175"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="83.5"
|
||||
cy="384.1"
|
||||
r="10.438"
|
||||
transform="rotate(-180 83.5 384.1)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="83.5"
|
||||
cy="200.399"
|
||||
r="10.438"
|
||||
transform="rotate(-180 83.5 200.399)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="83.5"
|
||||
cy="81.412"
|
||||
r="10.438"
|
||||
transform="rotate(-180 83.5 81.412)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="183.699"
|
||||
cy="375.75"
|
||||
r="10.438"
|
||||
transform="rotate(-180 183.699 375.75)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="183.699"
|
||||
cy="563.625"
|
||||
r="10.438"
|
||||
transform="rotate(-180 183.699 563.625)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="384.1"
|
||||
cy="651.3"
|
||||
r="10.438"
|
||||
transform="rotate(-180 384.1 651.3)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="484.301"
|
||||
cy="574.062"
|
||||
r="10.438"
|
||||
transform="rotate(-180 484.301 574.062)"
|
||||
fill="#0EA5E9"
|
||||
fillOpacity=".42"
|
||||
stroke="#0EA5E9"
|
||||
/>
|
||||
<circle
|
||||
cx="384.1"
|
||||
cy="749.412"
|
||||
r="10.438"
|
||||
transform="rotate(-180 384.1 749.412)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="384.1"
|
||||
cy="1027.05"
|
||||
r="10.438"
|
||||
transform="rotate(-180 384.1 1027.05)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="283.9"
|
||||
cy="924.763"
|
||||
r="10.438"
|
||||
transform="rotate(-180 283.9 924.763)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="183.699"
|
||||
cy="870.487"
|
||||
r="10.438"
|
||||
transform="rotate(-180 183.699 870.487)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="283.9"
|
||||
cy="738.975"
|
||||
r="10.438"
|
||||
transform="rotate(-180 283.9 738.975)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="83.5"
|
||||
cy="695.138"
|
||||
r="10.438"
|
||||
transform="rotate(-180 83.5 695.138)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="83.5"
|
||||
cy="484.3"
|
||||
r="10.438"
|
||||
transform="rotate(-180 83.5 484.3)"
|
||||
fill="#0EA5E9"
|
||||
fillOpacity=".42"
|
||||
stroke="#0EA5E9"
|
||||
/>
|
||||
<circle
|
||||
cx="484.301"
|
||||
cy="432.112"
|
||||
r="10.438"
|
||||
transform="rotate(-180 484.301 432.112)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="584.5"
|
||||
cy="432.112"
|
||||
r="10.438"
|
||||
transform="rotate(-180 584.5 432.112)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="584.5"
|
||||
cy="642.95"
|
||||
r="10.438"
|
||||
transform="rotate(-180 584.5 642.95)"
|
||||
fill="#1E293B"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="484.301"
|
||||
cy="851.699"
|
||||
r="10.438"
|
||||
transform="rotate(-180 484.301 851.699)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
<circle
|
||||
cx="384.1"
|
||||
cy="256.763"
|
||||
r="10.438"
|
||||
transform="rotate(-180 384.1 256.763)"
|
||||
stroke="#334155"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import Footer from "./Footer";
|
||||
import HeaderPMF from "./HeaderPMF";
|
||||
import MetaInformation from "./MetaInformation";
|
||||
import HeaderLight from "./HeaderLight";
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
@@ -12,7 +12,7 @@ export default function Layout({ title, description, children }: LayoutProps) {
|
||||
return (
|
||||
<div className="flex h-screen flex-col justify-between">
|
||||
<MetaInformation title={title} description={description} />
|
||||
<HeaderPMF />
|
||||
<HeaderLight />
|
||||
{
|
||||
<main className="max-w-8xl relative mx-auto mb-auto flex w-full flex-col justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
{children}
|
||||
@@ -2,6 +2,25 @@ import Footer from "./Footer";
|
||||
import Header from "./Header";
|
||||
import MetaInformation from "./MetaInformation";
|
||||
import { Prose } from "./Prose";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const useExternalLinks = (selector: string) => {
|
||||
useEffect(() => {
|
||||
const links = document.querySelectorAll(selector);
|
||||
|
||||
links.forEach((link) => {
|
||||
link.setAttribute("target", "_blank");
|
||||
link.setAttribute("rel", "noopener noreferrer");
|
||||
});
|
||||
|
||||
return () => {
|
||||
links.forEach((link) => {
|
||||
link.removeAttribute("target");
|
||||
link.removeAttribute("rel");
|
||||
});
|
||||
};
|
||||
}, [selector]);
|
||||
};
|
||||
|
||||
interface Props {
|
||||
meta: {
|
||||
@@ -12,11 +31,12 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function LayoutMdx({ meta, children }: Props) {
|
||||
useExternalLinks(".prose a");
|
||||
return (
|
||||
<div className="flex h-screen flex-col justify-between">
|
||||
<MetaInformation title={meta.title} description={meta.description} />
|
||||
<Header />
|
||||
<main className="min-w-0 max-w-2xl flex-auto px-4 lg:max-w-none lg:pr-0 lg:pl-8 xl:px-16">
|
||||
<main className="min-w-0 max-w-2xl flex-auto px-4 lg:max-w-none lg:pl-8 lg:pr-0 xl:px-16">
|
||||
<article className="mx-auto my-16 max-w-3xl px-2">
|
||||
{meta.title && (
|
||||
<header className="mb-9 space-y-1">
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import Footer from "./Footer";
|
||||
import MetaInformation from "./MetaInformation";
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default function Layout({ title, description, children }: LayoutProps) {
|
||||
return (
|
||||
<div className="flex h-screen flex-col justify-between">
|
||||
<MetaInformation title={title} description={description} />
|
||||
{
|
||||
<main className="max-w-8xl relative mx-auto mb-auto flex w-full flex-col justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
{children}
|
||||
</main>
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { useRouter } from "next/router";
|
||||
import HeadingCentered from "./HeadingCentered";
|
||||
|
||||
export default function CTA() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<>
|
||||
<div className="mx-auto py-16 lg:pt-24 lg:pb-40">
|
||||
<div className="mx-auto py-16 lg:pb-40 lg:pt-24">
|
||||
<p className="text-md text-brand-dark dark:text-brand-light font-semibold uppercase">
|
||||
It's free & open-source
|
||||
</p>
|
||||
@@ -14,16 +13,16 @@ export default function CTA() {
|
||||
Try Formbricks right now!
|
||||
</p>
|
||||
<div className="mt-12 grid grid-cols-1 content-center md:grid-cols-2">
|
||||
<div className="-mb-2 rounded-t-xl bg-gradient-to-br from-slate-300 to-slate-200 text-center text-slate-900 dark:from-slate-800 dark:to-slate-900 dark:text-slate-200 md:mb-0 md:ml-2.5 md:-mr-5 md:rounded-l-xl">
|
||||
<div className="-mb-2 rounded-t-xl bg-gradient-to-br from-slate-300 to-slate-200 text-center text-slate-900 dark:from-slate-800 dark:to-slate-900 dark:text-slate-200 md:-mr-5 md:mb-0 md:ml-2.5 md:rounded-l-xl">
|
||||
<h3 className="text-3xl font-bold">Self-hosted</h3>
|
||||
<p className="mt-2 mb-4 dark:text-slate-400">Run locally with docker-compose.</p>
|
||||
<Button variant="secondary" onClick={() => router.push("/docs")} className="mt-3 mb-8 md:mb-0">
|
||||
<p className="mb-4 mt-2 dark:text-slate-400">Run locally with docker-compose.</p>
|
||||
<Button variant="secondary" onClick={() => router.push("/docs")} className="mb-8 mt-3 md:mb-0">
|
||||
Read docs
|
||||
</Button>
|
||||
</div>
|
||||
<div className="rounded-xl bg-gradient-to-br from-slate-400 to-slate-300 pb-10 text-center text-slate-800 dark:from-slate-800 dark:to-slate-700 dark:text-slate-200">
|
||||
<h3 className="text-3xl font-bold">Cloud</h3>
|
||||
<p className="mt-2 mb-4 dark:text-slate-400">Use our free managed service.</p>
|
||||
<p className="mb-4 mt-2 dark:text-slate-400">Use our free managed service.</p>
|
||||
<Button variant="secondary" onClick={() => router.push("/waitlist")} className="mt-3">
|
||||
Get started
|
||||
</Button>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
export default function HeadingCentered() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="mx-auto grid grid-cols-1 content-center gap-10 pt-24 pb-12 md:grid-cols-2">
|
||||
<div className="mx-auto grid grid-cols-1 content-center gap-10 pb-12 pt-24 md:grid-cols-2">
|
||||
<div className="">
|
||||
<p className="text-md text-brand-dark dark:text-brand-light font-semibold uppercase">
|
||||
What are you waiting for?
|
||||
|
||||
@@ -6,7 +6,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function MetaInformation({ title, description }: Props) {
|
||||
const pageTitle = `${title} | Open Source Forms & Surveys by Formbricks`;
|
||||
const pageTitle = `${title} | Open-Source Survey Software`;
|
||||
return (
|
||||
<Head>
|
||||
<title>{pageTitle}</title>
|
||||
|
||||
@@ -47,13 +47,13 @@ const Modal: React.FC<Modal> = ({
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
|
||||
<Dialog.Panel
|
||||
className={clsx(
|
||||
"relative transform rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-xl ",
|
||||
`${noPadding ? "" : "px-4 pt-5 pb-4 sm:p-6"}`
|
||||
"relative transform rounded-lg bg-slate-100 text-left shadow-xl transition-all dark:bg-slate-800 sm:my-8 sm:w-full sm:max-w-xl ",
|
||||
`${noPadding ? "" : "px-4 pb-4 pt-5 sm:p-6"}`
|
||||
)}>
|
||||
<div className="absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<div className="absolute right-0 top-0 hidden pr-4 pt-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" />
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function WaitlistForm() {
|
||||
value="e0084486-8751-43e4-8cfb-58b7c3f5f318"
|
||||
readOnly
|
||||
/>
|
||||
<label htmlFor="e0084">Build in public</label>
|
||||
<label htmlFor="e0084">Stay in the loop</label>
|
||||
</div>
|
||||
<Button type="submit" className="mt-5 w-full justify-center">
|
||||
Subscribe
|
||||
|
||||
162
apps/formbricks-com/components/shared/Pricing.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import { Button } from "@formbricks/ui";
|
||||
import clsx from "clsx";
|
||||
import EarlyBirdDeal from "./EarlyBirdDeal";
|
||||
import HeadingCentered from "./HeadingCentered";
|
||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||
import { usePlausible } from "next-plausible";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
const tiers = [
|
||||
{
|
||||
name: "Self-hosting",
|
||||
priceMonthly: "free",
|
||||
paymentRythm: "/always",
|
||||
button: "secondary",
|
||||
discounted: false,
|
||||
highlight: false,
|
||||
description: "Host Formbricks on your own server.",
|
||||
features: [
|
||||
"All Free feautres",
|
||||
"Easy self-hosting (Docker)",
|
||||
"Unlimited surveys",
|
||||
"Unlimited responses",
|
||||
"Unlimited team members",
|
||||
],
|
||||
ctaName: "Read docs",
|
||||
plausibleGoal: "Pricing_CTA_SelfHosting",
|
||||
href: "/docs/self-hosting/deployment",
|
||||
},
|
||||
{
|
||||
name: "Free",
|
||||
href: "https://app.formbricks.com/auth/signup",
|
||||
priceMonthly: "$0",
|
||||
paymentRythm: "/month",
|
||||
button: "highlight",
|
||||
discounted: false,
|
||||
highlight: true,
|
||||
description: "All Pro features included.",
|
||||
features: [
|
||||
"Unlimited surveys",
|
||||
"Unlimited team members",
|
||||
"Granular targeting",
|
||||
"In-product surveys",
|
||||
"Link surveys",
|
||||
"30+ templates",
|
||||
"API access",
|
||||
"Integrations (Slack, PostHog, Zapier)",
|
||||
"100 responses per survey",
|
||||
],
|
||||
ctaName: "Start for free",
|
||||
plausibleGoal: "Pricing_CTA_FreePlan",
|
||||
},
|
||||
{
|
||||
name: "Pro",
|
||||
href: "https://app.formbricks.com/auth/signup",
|
||||
priceMonthly: "$99",
|
||||
paymentRythm: "/month",
|
||||
button: "secondary",
|
||||
discounted: true,
|
||||
highlight: false,
|
||||
description: "All features included. Unlimited usage.",
|
||||
features: ["All features of Free plan", "Unlimited responses", "Remove branding"],
|
||||
ctaName: "Sign up now",
|
||||
plausibleGoal: "Pricing_CTA_ProPlan",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Pricing() {
|
||||
const plausible = usePlausible();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="-mt-10 pb-20">
|
||||
<div className="mx-auto max-w-7xl py-4 sm:px-6 sm:pb-6 lg:px-8" id="pricing">
|
||||
<HeadingCentered heading="One price, unlimited usage." teaser="Pricing" />
|
||||
|
||||
<div className="mx-auto space-y-4 px-4 lg:grid lg:grid-cols-3 lg:gap-6 lg:space-y-0 lg:px-0">
|
||||
{tiers.map((tier) => (
|
||||
<div
|
||||
key={tier.name}
|
||||
className={clsx(
|
||||
`h-fit rounded-lg shadow-sm`,
|
||||
tier.highlight
|
||||
? "border border-slate-300 bg-slate-200 dark:border-slate-500 dark:bg-slate-800"
|
||||
: "bg-slate-100 dark:bg-slate-700"
|
||||
)}>
|
||||
<div className="p-8">
|
||||
<h2
|
||||
className={clsx(
|
||||
"inline-flex text-3xl font-bold",
|
||||
tier.highlight
|
||||
? "text-slate-700 dark:text-slate-200"
|
||||
: "text-slate-500 dark:text-slate-300"
|
||||
)}>
|
||||
{tier.name}
|
||||
</h2>
|
||||
<p className="mt-4 whitespace-pre-wrap text-sm text-slate-600 dark:text-slate-300">
|
||||
{tier.description}
|
||||
</p>
|
||||
<ul className="mt-4 space-y-4">
|
||||
{tier.features.map((feature, index) => (
|
||||
<li key={index} className="flex items-start">
|
||||
<div className="rounded-full border border-green-300 bg-green-100 p-0.5 dark:bg-green-800">
|
||||
<CheckIcon className="h-5 w-5 p-0.5 text-green-500 dark:text-green-400" />
|
||||
</div>
|
||||
<span className="ml-2 text-sm text-slate-500 dark:text-slate-400">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="mt-8">
|
||||
<span
|
||||
className={clsx(
|
||||
`text-4xl font-light`,
|
||||
tier.highlight
|
||||
? "text-slate-800 dark:text-slate-100"
|
||||
: "text-slate-500 dark:text-slate-200",
|
||||
tier.discounted ? "decoration-brand line-through" : ""
|
||||
)}>
|
||||
{tier.priceMonthly}
|
||||
</span>{" "}
|
||||
<span className="text-4xl font-bold text-slate-900 dark:text-slate-50">
|
||||
{tier.discounted && "$49"}
|
||||
</span>
|
||||
<span
|
||||
className={clsx(
|
||||
"text-base font-medium",
|
||||
tier.highlight
|
||||
? "text-slate-500 dark:text-slate-400"
|
||||
: "text-slate-400 dark:text-slate-500"
|
||||
)}>
|
||||
{tier.paymentRythm}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
plausible(`${tier.plausibleGoal}`);
|
||||
router.push(`${tier.href}`);
|
||||
}}
|
||||
className={clsx(
|
||||
"mt-6 w-full justify-center py-4 text-lg shadow-sm",
|
||||
tier.highlight
|
||||
? ""
|
||||
: "bg-slate-300 hover:bg-slate-200 dark:bg-slate-600 dark:hover:bg-slate-500"
|
||||
)}
|
||||
variant={tier.highlight ? "highlight" : "secondary"}>
|
||||
{tier.ctaName}
|
||||
</Button>
|
||||
|
||||
{tier.name === "Free" && (
|
||||
<p className="mt-1.5 text-center text-xs text-slate-500">No Creditcard required.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<EarlyBirdDeal />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import HeadingCentered from "./HeadingCentered";
|
||||
import clsx from "clsx";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import EarlyBirdDeal from "./EarlyBirdDeal";
|
||||
|
||||
const tiers = [
|
||||
{
|
||||
name: "Self-hosting",
|
||||
href: "#",
|
||||
priceMonthly: "tba",
|
||||
button: "secondary",
|
||||
discounted: false,
|
||||
highlight: false,
|
||||
paymentRythm: "/month",
|
||||
description: "Host Formbricks on your own server.",
|
||||
ctaName: "Contact us",
|
||||
ctaAction: () => window.open("mailto:hola@formbricks.com"),
|
||||
},
|
||||
{
|
||||
name: "Cloud",
|
||||
href: "#",
|
||||
priceMonthly: "$99",
|
||||
button: "highlight",
|
||||
discounted: true,
|
||||
highlight: true,
|
||||
paymentRythm: "/month",
|
||||
description: "Use the managed cloud, gather insights immediately.",
|
||||
ctaName: "Sign up now",
|
||||
ctaAction: () => window.open("https://app.formbricks.com/auth/signup"),
|
||||
},
|
||||
];
|
||||
|
||||
export default function PricingPmf() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="-mt-10 pb-20">
|
||||
<div className="mx-auto max-w-7xl py-4 sm:px-6 sm:pb-6 lg:px-8 ">
|
||||
<HeadingCentered heading="One price, unlimited usage." teaser="Pricing" />
|
||||
|
||||
<div className="mx-auto space-y-4 px-4 sm:grid sm:grid-cols-2 sm:gap-6 sm:space-y-0 md:px-0 lg:max-w-5xl">
|
||||
{tiers.map((tier) => (
|
||||
<div
|
||||
key={tier.name}
|
||||
className={clsx(
|
||||
`rounded-lg shadow-sm`,
|
||||
tier.highlight
|
||||
? "border border-slate-300 bg-slate-200 dark:border-slate-500 dark:bg-slate-600"
|
||||
: "bg-slate-100 dark:bg-slate-700"
|
||||
)}>
|
||||
<div className="p-8">
|
||||
<h2
|
||||
className={clsx(
|
||||
"inline-flex text-3xl font-bold",
|
||||
tier.highlight
|
||||
? "text-slate-700 dark:text-slate-200"
|
||||
: "text-slate-500 dark:text-slate-300"
|
||||
)}>
|
||||
{tier.name}
|
||||
</h2>
|
||||
<p
|
||||
className={clsx(
|
||||
"mt-4 whitespace-pre-wrap text-sm",
|
||||
tier.highlight
|
||||
? "text-slate-600 dark:text-slate-300"
|
||||
: "text-slate-500 dark:text-slate-300"
|
||||
)}>
|
||||
{tier.description}
|
||||
</p>
|
||||
<p className="mt-8">
|
||||
<span
|
||||
className={clsx(
|
||||
`text-4xl font-light`,
|
||||
tier.highlight
|
||||
? "text-slate-800 dark:text-slate-100"
|
||||
: "text-slate-500 dark:text-slate-200",
|
||||
tier.discounted ? "decoration-brand line-through" : ""
|
||||
)}>
|
||||
{tier.priceMonthly}
|
||||
</span>{" "}
|
||||
<span className="text-4xl font-bold text-slate-900 dark:text-slate-50">
|
||||
{tier.discounted && "$49"}
|
||||
</span>
|
||||
<span
|
||||
className={clsx(
|
||||
"text-base font-medium",
|
||||
tier.highlight
|
||||
? "text-slate-500 dark:text-slate-400"
|
||||
: "text-slate-400 dark:text-slate-500"
|
||||
)}>
|
||||
{tier.paymentRythm}
|
||||
</span>
|
||||
</p>
|
||||
{tier.ctaName && tier.ctaAction && (
|
||||
<Button
|
||||
onClick={tier.ctaAction}
|
||||
className="mt-6 w-full justify-center py-4 text-lg shadow-sm"
|
||||
variant={tier.highlight ? "highlight" : "secondary"}>
|
||||
{tier.ctaName}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<EarlyBirdDeal />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
export default function HeadingCentered() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="mx-auto grid max-w-md grid-cols-1 content-center gap-10 px-4 py-12 sm:max-w-3xl sm:px-6 md:grid-cols-2 md:pt-24 md:pb-36 lg:max-w-6xl lg:px-8">
|
||||
<div className="mx-auto grid max-w-md grid-cols-1 content-center gap-10 px-4 py-12 sm:max-w-3xl sm:px-6 md:grid-cols-2 md:pb-36 md:pt-24 lg:max-w-6xl lg:px-8">
|
||||
<div className="">
|
||||
<p className="text-md text-brand-dark dark:text-brand-light mb-3 font-semibold uppercase">
|
||||
What are you waiting for?
|
||||
|
||||
@@ -43,7 +43,7 @@ const features = [
|
||||
export default function FeatureTable({}) {
|
||||
return (
|
||||
<div className="mt-32 rounded-xl bg-gradient-to-br from-slate-900 via-slate-900 to-slate-800 dark:from-slate-200 dark:to-slate-300 lg:mt-56">
|
||||
<div className="mx-auto max-w-4xl px-4 py-8 sm:px-6 sm:pt-8 sm:pb-12 lg:max-w-7xl lg:px-8 lg:pt-12">
|
||||
<div className="mx-auto max-w-4xl px-4 py-8 sm:px-6 sm:pb-12 sm:pt-8 lg:max-w-7xl lg:px-8 lg:pt-12">
|
||||
<p className="text-md dark:text-brand-dark text-brand-light mb-3 max-w-2xl font-semibold uppercase sm:mt-4">
|
||||
Why Formbricks?
|
||||
</p>
|
||||
|
||||
9
apps/formbricks-com/images/clients/cal-logo-dark.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="101" height="22" viewBox="0 0 101 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.0582 20.817C4.32115 20.817 0 16.2763 0 10.6704C0 5.04589 4.1005 0.467773 10.0582 0.467773C13.2209 0.467773 15.409 1.43945 17.1191 3.66311L14.3609 5.96151C13.2025 4.72822 11.805 4.11158 10.0582 4.11158C6.17833 4.11158 4.04533 7.08268 4.04533 10.6704C4.04533 14.2582 6.38059 17.1732 10.0582 17.1732C11.7866 17.1732 13.2577 16.5566 14.4161 15.3233L17.1375 17.7151C15.501 19.8453 13.2577 20.817 10.0582 20.817Z" fill="#fafafa"/>
|
||||
<path d="M29.0161 5.88601H32.7304V20.4612H29.0161V18.331C28.2438 19.8446 26.9566 20.8536 24.4927 20.8536C20.5577 20.8536 17.4133 17.4341 17.4133 13.2297C17.4133 9.02528 20.5577 5.60571 24.4927 5.60571C26.9383 5.60571 28.2438 6.61477 29.0161 8.12835V5.88601ZM29.1264 13.2297C29.1264 10.95 27.5634 9.06266 25.0995 9.06266C22.7274 9.06266 21.1828 10.9686 21.1828 13.2297C21.1828 15.4346 22.7274 17.3967 25.0995 17.3967C27.5451 17.3967 29.1264 15.4907 29.1264 13.2297Z" fill="#fafafa"/>
|
||||
<path d="M35.3599 0H39.0742V20.4427H35.3599V0Z" fill="#fafafa"/>
|
||||
<path d="M40.7291 18.5182C40.7291 17.3223 41.6853 16.3132 42.9908 16.3132C44.2964 16.3132 45.2158 17.3223 45.2158 18.5182C45.2158 19.7515 44.278 20.7605 42.9908 20.7605C41.7037 20.7605 40.7291 19.7515 40.7291 18.5182Z" fill="#fafafa"/>
|
||||
<path d="M59.4296 18.1068C58.0505 19.7885 55.9543 20.8536 53.4719 20.8536C49.0404 20.8536 45.7858 17.4341 45.7858 13.2297C45.7858 9.02528 49.0404 5.60571 53.4719 5.60571C55.8623 5.60571 57.9402 6.61477 59.3193 8.20309L56.4508 10.6136C55.7336 9.71667 54.7958 9.04397 53.4719 9.04397C51.0999 9.04397 49.5553 10.95 49.5553 13.211C49.5553 15.472 51.0999 17.378 53.4719 17.378C54.9062 17.378 55.8991 16.6306 56.6346 15.6215L59.4296 18.1068Z" fill="#fafafa"/>
|
||||
<path d="M59.7422 13.2297C59.7422 9.02528 62.9968 5.60571 67.4283 5.60571C71.8598 5.60571 75.1144 9.02528 75.1144 13.2297C75.1144 17.4341 71.8598 20.8536 67.4283 20.8536C62.9968 20.8349 59.7422 17.4341 59.7422 13.2297ZM71.3449 13.2297C71.3449 10.95 69.8003 9.06266 67.4283 9.06266C65.0563 9.04397 63.5117 10.95 63.5117 13.2297C63.5117 15.4907 65.0563 17.3967 67.4283 17.3967C69.8003 17.3967 71.3449 15.4907 71.3449 13.2297Z" fill="#fafafa"/>
|
||||
<path d="M100.232 11.5482V20.4428H96.518V12.4638C96.518 9.94119 95.3412 8.85739 93.576 8.85739C91.921 8.85739 90.7442 9.67958 90.7442 12.4638V20.4428H87.0299V12.4638C87.0299 9.94119 85.8346 8.85739 84.0878 8.85739C82.4329 8.85739 80.9802 9.67958 80.9802 12.4638V20.4428H77.2659V5.8676H80.9802V7.88571C81.7525 6.31607 83.15 5.53125 85.3014 5.53125C87.3425 5.53125 89.0525 6.5403 89.9903 8.24074C90.9281 6.50293 92.3072 5.53125 94.8079 5.53125C97.8603 5.54994 100.232 7.86702 100.232 11.5482Z" fill="#fafafa"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
9
apps/formbricks-com/images/clients/cal-logo-light.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="101" height="22" viewBox="0 0 101 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.0582 20.817C4.32115 20.817 0 16.2763 0 10.6704C0 5.04589 4.1005 0.467773 10.0582 0.467773C13.2209 0.467773 15.409 1.43945 17.1191 3.66311L14.3609 5.96151C13.2025 4.72822 11.805 4.11158 10.0582 4.11158C6.17833 4.11158 4.04533 7.08268 4.04533 10.6704C4.04533 14.2582 6.38059 17.1732 10.0582 17.1732C11.7866 17.1732 13.2577 16.5566 14.4161 15.3233L17.1375 17.7151C15.501 19.8453 13.2577 20.817 10.0582 20.817Z" fill="#292929"/>
|
||||
<path d="M29.0161 5.88601H32.7304V20.4612H29.0161V18.331C28.2438 19.8446 26.9566 20.8536 24.4927 20.8536C20.5577 20.8536 17.4133 17.4341 17.4133 13.2297C17.4133 9.02528 20.5577 5.60571 24.4927 5.60571C26.9383 5.60571 28.2438 6.61477 29.0161 8.12835V5.88601ZM29.1264 13.2297C29.1264 10.95 27.5634 9.06266 25.0995 9.06266C22.7274 9.06266 21.1828 10.9686 21.1828 13.2297C21.1828 15.4346 22.7274 17.3967 25.0995 17.3967C27.5451 17.3967 29.1264 15.4907 29.1264 13.2297Z" fill="#292929"/>
|
||||
<path d="M35.3599 0H39.0742V20.4427H35.3599V0Z" fill="#292929"/>
|
||||
<path d="M40.7291 18.5182C40.7291 17.3223 41.6853 16.3132 42.9908 16.3132C44.2964 16.3132 45.2158 17.3223 45.2158 18.5182C45.2158 19.7515 44.278 20.7605 42.9908 20.7605C41.7037 20.7605 40.7291 19.7515 40.7291 18.5182Z" fill="#292929"/>
|
||||
<path d="M59.4296 18.1068C58.0505 19.7885 55.9543 20.8536 53.4719 20.8536C49.0404 20.8536 45.7858 17.4341 45.7858 13.2297C45.7858 9.02528 49.0404 5.60571 53.4719 5.60571C55.8623 5.60571 57.9402 6.61477 59.3193 8.20309L56.4508 10.6136C55.7336 9.71667 54.7958 9.04397 53.4719 9.04397C51.0999 9.04397 49.5553 10.95 49.5553 13.211C49.5553 15.472 51.0999 17.378 53.4719 17.378C54.9062 17.378 55.8991 16.6306 56.6346 15.6215L59.4296 18.1068Z" fill="#292929"/>
|
||||
<path d="M59.7422 13.2297C59.7422 9.02528 62.9968 5.60571 67.4283 5.60571C71.8598 5.60571 75.1144 9.02528 75.1144 13.2297C75.1144 17.4341 71.8598 20.8536 67.4283 20.8536C62.9968 20.8349 59.7422 17.4341 59.7422 13.2297ZM71.3449 13.2297C71.3449 10.95 69.8003 9.06266 67.4283 9.06266C65.0563 9.04397 63.5117 10.95 63.5117 13.2297C63.5117 15.4907 65.0563 17.3967 67.4283 17.3967C69.8003 17.3967 71.3449 15.4907 71.3449 13.2297Z" fill="#292929"/>
|
||||
<path d="M100.232 11.5482V20.4428H96.518V12.4638C96.518 9.94119 95.3412 8.85739 93.576 8.85739C91.921 8.85739 90.7442 9.67958 90.7442 12.4638V20.4428H87.0299V12.4638C87.0299 9.94119 85.8346 8.85739 84.0878 8.85739C82.4329 8.85739 80.9802 9.67958 80.9802 12.4638V20.4428H77.2659V5.8676H80.9802V7.88571C81.7525 6.31607 83.15 5.53125 85.3014 5.53125C87.3425 5.53125 89.0525 6.5403 89.9903 8.24074C90.9281 6.50293 92.3072 5.53125 94.8079 5.53125C97.8603 5.54994 100.232 7.86702 100.232 11.5482Z" fill="#292929"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
1
apps/formbricks-com/images/clients/clovyr-logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 416 98"><defs><style>.cls-1{fill:#00bf74;}.cls-2{fill:#00e874;}.cls-3{fill:#00a774;}</style></defs><title>clovyr-logo</title><path class="cls-1" d="M47,42.88a9.85,9.85,0,0,1-5.44-1.64L12.05,22.56s-3-1.64-3-4.24S12.46,14,12.46,14L32.33,2.35c.23-.14.47-.27.71-.4a18.72,18.72,0,0,1,2-.85,18.15,18.15,0,0,1,3-.88A9.89,9.89,0,0,1,40.82,0a8.73,8.73,0,0,1,2.74.74,11.82,11.82,0,0,0,2.87.77s.28,0,.58.06Z"/><path class="cls-1" d="M42.16,50.83A9.9,9.9,0,0,0,38,46.91L7.23,30.47S4.3,28.66,2.07,30s-2,5.16-2,5.16L0,58.28a7.48,7.48,0,0,0,0,.82,19.19,19.19,0,0,0,.24,2.14,19.51,19.51,0,0,0,.72,3,10.42,10.42,0,0,0,1.28,2.59,8.57,8.57,0,0,0,2,2A12,12,0,0,1,6.36,71s.18.22.35.47Z"/><path class="cls-2" d="M47,42.88a9.85,9.85,0,0,0,5.44-1.64L82,22.56s3-1.64,3-4.24S81.56,14,81.56,14L61.69,2.35c-.23-.14-.47-.27-.71-.4a17.84,17.84,0,0,0-2-.85,18.15,18.15,0,0,0-3-.88A9.89,9.89,0,0,0,53.2,0a8.73,8.73,0,0,0-2.74.74,11.7,11.7,0,0,1-2.87.77s-.28,0-.58.06Z"/><path class="cls-2" d="M51.86,50.83A9.9,9.9,0,0,1,56,46.91L86.8,30.47S89.72,28.66,92,30s2,5.16,2,5.16L94,58.28a7.48,7.48,0,0,1,0,.82,17.09,17.09,0,0,1-.24,2.14,19.51,19.51,0,0,1-.72,3,10.42,10.42,0,0,1-1.28,2.59,8.57,8.57,0,0,1-2,2A12,12,0,0,0,87.66,71s-.18.22-.35.47Z"/><path class="cls-3" d="M42.16,50.83a10.05,10.05,0,0,1,1.31,5.57L42.18,91.51s.1,3.46-2.13,4.76-5.43-.84-5.43-.84L14.69,83.92c-.24-.13-.48-.28-.71-.43a19,19,0,0,1-1.71-1.28A18.8,18.8,0,0,1,10,80.07a10.17,10.17,0,0,1-1.59-2.41,8.77,8.77,0,0,1-.73-2.76A11.55,11.55,0,0,0,6.94,72s-.1-.27-.23-.54Z"/><path class="cls-3" d="M51.86,50.83a10.05,10.05,0,0,0-1.31,5.57l1.29,35.11S51.74,95,54,96.27s5.43-.84,5.43-.84L79.33,83.92c.25-.13.48-.28.71-.43a17.7,17.7,0,0,0,1.71-1.28A18.8,18.8,0,0,0,84,80.07a10.17,10.17,0,0,0,1.59-2.41,8.51,8.51,0,0,0,.73-2.76A12,12,0,0,1,87.08,72s.1-.27.23-.54Z"/><path class="cls-2" d="M412.05,33.37l3.65-7c.73-1.31.07-2.37-1.42-2.37H400.94c-.25,0-.49,0-.73,0A11.47,11.47,0,0,0,392,28.87a39.46,39.46,0,0,0-4,6c-.89-7.84-4.14-10-7.95-10.1a6.09,6.09,0,0,0-.74,0c-2.62,0-2.89.27-2.89,2.89V59c0,6.48,0,13,0,19.44,0,2,.39,2.38,2.37,2.39h7.44c2.25,0,2.65-.4,2.65-2.65,0-9.78,0-19.57,0-29.35a21.91,21.91,0,0,1,.38-4,11.12,11.12,0,0,1,4.94-7.6,14.88,14.88,0,0,1,6.85-1.83,43,43,0,0,1,7.35,0A3.7,3.7,0,0,0,412.05,33.37Z"/><path class="cls-2" d="M226.83,81.76c-15.66.25-29.41-13.1-29.23-29.5a29.3,29.3,0,0,1,29.66-28.83c16.06.21,29.26,13.12,29.22,29.2A29.37,29.37,0,0,1,226.83,81.76ZM243.7,53.07a18,18,0,0,0-1.55-8,16.59,16.59,0,0,0-26.27-5.55,18.32,18.32,0,0,0-3.79,21,16.35,16.35,0,0,0,22.47,7.66C240.56,65,243.37,59.75,243.7,53.07Z"/><path class="cls-2" d="M327.6,95.23c-.7,1.88-.09,2.71,1.86,2.74,2.7,0,5.41,0,8.11,0a2.65,2.65,0,0,0,2.71-1.91Q346.34,81,352.42,65.92q5.15-12.83,10.28-25.66c1.78-4.43,3.57-8.86,5.33-13.31.75-1.9.16-2.82-1.85-2.85-3,0,.23,0-2.73,0-8.08,0-10,5.2-10.66,6.84q-1.92,5.08-3.85,10.18-3.48,9.29-7,18.57c-.21.58-.51,1.13-.76,1.69l-.36,0q-2.79-7.48-5.57-15-3.81-10.27-7.6-20.54a2.29,2.29,0,0,0-2.18-1.7c-2.83,0-5.67-.08-8.49,0-1.82.07-2.4,1.09-1.74,2.8,1,2.55,2,5.08,3.05,7.61,4.89,12,9.69,24.08,14.75,36,1.51,3.54,2.67,4.81.9,8.46Z"/><path class="cls-2" d="M146.23,82c-12.76-.36-23.44-7.75-27.49-19.77a28.13,28.13,0,0,1,3.87-26.48c4.46-6.26,10.5-10.48,18.08-11.7,8.76-1.41,16.14.8,23.29,6.5,1.09.87,1,2.09.17,3.28-4.5,7.09-12.92,1.71-16.62,1.32-7.8-.81-13.26,3.4-16.23,9.52-4,8.15-1.59,19.21,7.59,23.84A16.05,16.05,0,0,0,150,69.88c2.17-.49,9.7-3.66,14.63,2.69a1.84,1.84,0,0,1-.5,2.61A27.82,27.82,0,0,1,146.23,82Z"/><path class="cls-2" d="M285.07,80.8a2.77,2.77,0,0,0,3-2.08c1.19-2.88,2.4-5.75,3.6-8.62Q296.83,57.76,302,45.41q4-9.51,7.88-19c.67-1.63.15-2.37-1.59-2.39-3,0-.09,0-3.14,0a11,11,0,0,0-11.08,8c-.24.69-.49,1.38-.74,2.06q-4,11.14-8.05,22.28c-.63,1.74-1.27,3.48-2,5.5-.27.1-.47-.56-.52-.71q-3-8.25-6-16.5c-2.23-6.17-4.49-12.35-6.71-18.53a2.85,2.85,0,0,0-3-2.07c-2.58,0-5.15,0-7.73,0s-3.19,1.07-2.26,3.33Q261.51,38.16,266,49q3.88,9.41,7.74,18.82c1.49,3.63,3,7.25,4.48,10.89a3.09,3.09,0,0,0,3.25,2.13Z"/><path class="cls-2" d="M175.71,44.2v-31c0-2.58,5.88-4,10.47-3.85,1.78,0,2.29.53,2.37,2.31,0,.38,0,.77,0,1.15V77.36c0,.51,0,1,0,1.54a1.79,1.79,0,0,1-2,1.89c-3,0-5.92,0-8.88,0-1.32,0-1.93-.71-1.94-2.17,0-4,0-8,0-12.07Z"/></svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
15
apps/formbricks-com/images/clients/crowd-logo-dark.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="131" height="25" viewBox="0 0 131 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M57.3019 1.04274C57.4086 1.06739 57.4915 1.15158 57.5145 1.25871L58.0944 3.96418C58.1278 4.12015 58.0256 4.27283 57.8686 4.30127L52.2078 5.32694C52.0202 5.36093 51.8536 5.2031 51.8773 5.01393L52.4746 0.247383C52.4954 0.0813042 52.6553 -0.030334 52.8183 0.00732826L57.3019 1.04274Z" fill="#E94F2E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M88.9753 24.6259C90.9399 24.6259 92.6615 23.897 93.7118 22.6069C93.8882 22.3902 94.2929 22.4798 94.3217 22.7577L94.4476 23.9688C94.4625 24.1127 94.5838 24.222 94.7285 24.222H97.6708C97.8268 24.222 97.9532 24.0956 97.9532 23.9396V1.39179C97.9532 1.23582 97.8268 1.10938 97.6708 1.10938H94.4767C94.3207 1.10938 94.1943 1.23581 94.1943 1.39179V9.85822C94.1943 10.1411 93.7865 10.2737 93.5874 10.0726C92.5408 9.01541 90.9503 8.40974 89.1928 8.40974C88.3496 8.40974 87.5673 8.52313 86.8516 8.73751V12.95C87.577 12.2203 88.5844 11.7959 89.8141 11.7959C92.4546 11.7959 94.1632 13.7219 94.1632 16.4867C94.1632 19.2516 92.4546 21.1466 89.8141 21.1466C88.5844 21.1466 87.577 20.7288 86.8516 20.0064V24.3493C87.5069 24.5308 88.2167 24.6259 88.9753 24.6259ZM107.836 24.6259C111.709 24.6259 114.45 22.7405 115.219 19.5864C115.261 19.4139 115.127 19.2516 114.949 19.2516H111.992C111.867 19.2516 111.758 19.3344 111.715 19.4514C111.218 20.7852 109.883 21.5193 107.898 21.5193C105.48 21.5193 104.066 20.2572 103.712 17.7604C103.688 17.5949 103.819 17.4494 103.986 17.4489L114.948 17.4195C115.104 17.419 115.23 17.2927 115.23 17.1371V16.2693C115.23 11.4852 112.31 8.40974 107.712 8.40974C103.208 8.40974 100.07 11.7337 100.07 16.5489C100.07 21.3019 103.27 24.6259 107.836 24.6259ZM107.743 11.5163C109.908 11.5163 111.337 12.7904 111.462 14.744C111.472 14.8996 111.345 15.0267 111.189 15.0267H104.099C103.925 15.0267 103.792 14.87 103.828 14.6999C104.274 12.6132 105.615 11.5163 107.743 11.5163ZM124.813 24.0483C124.769 24.1535 124.667 24.222 124.553 24.222H121.205C121.089 24.222 120.985 24.1514 120.942 24.0438L115.078 9.26229C115.004 9.07689 115.141 8.87572 115.34 8.87572H118.768C118.885 8.87572 118.99 8.94832 119.032 9.05808L121.634 15.9276C122.045 17.0812 122.406 18.1365 122.664 18.9692C122.753 19.2573 123.25 19.2574 123.341 18.9699C123.618 18.0918 124.004 17.0229 124.43 15.9276L127.155 9.05405C127.198 8.94641 127.302 8.87572 127.418 8.87572H130.717C130.918 8.87572 131.055 9.0808 130.977 9.26683L124.813 24.0483Z" fill="url(#paint0_linear_470_15640)"/>
|
||||
<path d="M0 16.5178C0 21.3951 3.04441 24.6259 7.70422 24.6259C11.5916 24.6259 14.5143 22.3166 15.1113 18.9172C15.1408 18.7493 15.0087 18.5992 14.8383 18.5992H11.5614C11.4309 18.5992 11.3183 18.6893 11.2808 18.8143C10.8175 20.3562 9.51661 21.2087 7.70422 21.2087C5.28112 21.2087 3.78998 19.4069 3.78998 16.5178C3.78998 13.6287 5.40538 11.7959 7.82848 11.7959C9.55525 11.7959 10.797 12.6233 11.2802 14.2235C11.3177 14.3476 11.4298 14.4364 11.5594 14.4364H14.8139C14.982 14.4364 15.1133 14.2901 15.0884 14.1238C14.5611 10.6088 11.7475 8.40974 7.67315 8.40974C3.1376 8.40974 0 11.7648 0 16.5178Z" fill="white"/>
|
||||
<path d="M26.6791 9.03409C26.6791 8.9045 26.591 8.791 26.4645 8.7628C25.9341 8.64456 25.4678 8.59613 25.0016 8.59613C23.4122 8.59613 22.1775 9.21215 21.3988 10.1982C21.2177 10.4275 20.7605 10.3376 20.7322 10.0468L20.6462 9.16187C20.6321 9.01717 20.5105 8.90679 20.3651 8.90679H17.3312C17.1753 8.90679 17.0488 9.03323 17.0488 9.1892V23.9396C17.0488 24.0956 17.1753 24.222 17.3312 24.222H20.5564C20.7124 24.222 20.8388 24.0956 20.8388 23.9396V16.7663C20.8388 13.7841 22.5474 12.324 25.2812 12.324H26.3967C26.5527 12.324 26.6791 12.1975 26.6791 12.0416V9.03409Z" fill="white"/>
|
||||
<path d="M26.8958 16.5178C26.8958 21.3019 30.3441 24.5948 35.0971 24.5948C39.8501 24.5948 43.2984 21.3019 43.2984 16.5178C43.2984 11.7337 39.8501 8.44081 35.0971 8.44081C30.3441 8.44081 26.8958 11.7337 26.8958 16.5178ZM30.6858 16.5178C30.6858 13.7219 32.4876 11.8269 35.0971 11.8269C37.7066 11.8269 39.5084 13.7219 39.5084 16.5178C39.5084 19.3137 37.7066 21.2087 35.0971 21.2087C32.4876 21.2087 30.6858 19.3137 30.6858 16.5178Z" fill="white"/>
|
||||
<path d="M73.9832 24.6259C75.9478 24.6259 77.6694 23.897 78.7197 22.6069C78.8961 22.3902 79.3008 22.4798 79.3296 22.7577L79.4555 23.9688C79.4704 24.1127 79.5917 24.222 79.7364 24.222H82.6787C82.8347 24.222 82.9611 24.0956 82.9611 23.9396V1.39179C82.9611 1.23582 82.8347 1.10938 82.6787 1.10938H79.4846C79.3286 1.10938 79.2022 1.23582 79.2022 1.39179V9.85822C79.2022 10.1412 78.7944 10.2737 78.5953 10.0726C77.5487 9.01541 75.9582 8.40974 74.2007 8.40974C69.5719 8.40974 66.776 11.8269 66.776 16.611C66.776 21.364 69.5408 24.6259 73.9832 24.6259ZM74.822 21.1466C72.1814 21.1466 70.566 19.2205 70.566 16.4867C70.566 13.753 72.1814 11.7959 74.822 11.7959C77.4625 11.7959 79.1711 13.7219 79.1711 16.4867C79.1711 19.2516 77.4625 21.1466 74.822 21.1466Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M44.4678 12.2829C44.2996 12.3134 44.1968 12.4852 44.2495 12.6478L47.9349 24.0266C47.9727 24.1431 48.0812 24.222 48.2036 24.222H51.489C51.6117 24.222 51.7204 24.1427 51.7579 24.0259L53.8074 17.6362C54.2018 16.3586 54.4679 15.4493 54.6548 14.7703C54.7376 14.4693 55.2536 14.4796 55.3276 14.7829C55.5103 15.5325 55.7665 16.4696 56.1063 17.574L58.1562 24.0251C58.1934 24.1424 58.3023 24.222 58.4253 24.222H61.5591C61.6794 24.222 61.7866 24.1457 61.826 24.032L66.9426 9.25051C67.0061 9.06706 66.8699 8.87572 66.6758 8.87572H63.2728C63.1492 8.87572 63.0399 8.95614 63.0031 9.07419L61.0146 15.4616C60.8322 16.09 60.5571 17.0888 60.319 18.0264C60.2429 18.3259 59.7533 18.322 59.6794 18.022C59.4281 17.0024 59.1178 15.8698 58.9954 15.4616L57.0068 9.07419C56.9701 8.95614 56.8608 8.87572 56.7372 8.87572H53.2989C53.1762 8.87572 53.0675 8.955 53.03 9.07188L50.9805 15.4616C50.6605 16.4416 50.4306 17.1899 50.2328 17.9965C50.1587 18.2989 49.6913 18.2986 49.6241 17.9947C49.4268 17.1028 49.2162 16.2555 48.9923 15.4616L47.9044 11.9134C47.8625 11.7766 47.7248 11.6928 47.5841 11.7183L44.4678 12.2829Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_470_15640" x1="93.4995" y1="15.9976" x2="84.4995" y2="16.9976" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E94F2E"/>
|
||||
<stop offset="1" stop-color="#E94F2E" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
15
apps/formbricks-com/images/clients/crowd-logo-light.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="131" height="24" viewBox="0 0 131 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M57.3097 1.04274C57.4165 1.06739 57.4993 1.15158 57.5223 1.25872L58.1022 3.9642C58.1357 4.12016 58.0334 4.27285 57.8764 4.30128L52.2156 5.32696C52.028 5.36095 51.8614 5.20312 51.8851 5.01395L52.4824 0.247384C52.5032 0.0813045 52.6631 -0.0303341 52.8262 0.00732829L57.3097 1.04274Z" fill="#E94F2E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M88.9753 23.6416C90.9399 23.6416 92.6615 22.9127 93.7118 21.6226C93.8883 21.4059 94.2929 21.4955 94.3218 21.7735L94.4476 22.9845C94.4626 23.1284 94.5838 23.2378 94.7285 23.2378H97.6708C97.8268 23.2378 97.9532 23.1113 97.9532 22.9553V0.407414C97.9532 0.251441 97.8268 0.125 97.6708 0.125H94.4767C94.3208 0.125 94.1943 0.25144 94.1943 0.407413V8.87388C94.1943 9.15681 93.7865 9.28936 93.5874 9.0883C92.5408 8.03107 90.9503 7.4254 89.1928 7.4254C88.3496 7.4254 87.5673 7.53878 86.8516 7.75317V11.9657C87.577 11.236 88.5844 10.8115 89.8141 10.8115C92.4547 10.8115 94.1633 12.7376 94.1633 15.5024C94.1633 18.2673 92.4547 20.1623 89.8141 20.1623C88.5844 20.1623 87.577 19.7445 86.8516 19.0221V23.365C87.5069 23.5465 88.2167 23.6416 88.9753 23.6416ZM107.836 23.6416C111.709 23.6416 114.45 21.7562 115.219 18.6021C115.261 18.4296 115.127 18.2673 114.95 18.2673H111.992C111.867 18.2673 111.759 18.3501 111.715 18.4671C111.218 19.8009 109.883 20.5351 107.899 20.5351C105.481 20.5351 104.067 19.2729 103.712 16.7761C103.688 16.6106 103.819 16.4651 103.986 16.4646L114.948 16.4352C115.104 16.4347 115.23 16.3084 115.23 16.1527V15.285C115.23 10.5009 112.31 7.4254 107.712 7.4254C103.208 7.4254 100.07 10.7494 100.07 15.5646C100.07 20.3176 103.27 23.6416 107.836 23.6416ZM107.743 10.532C109.908 10.532 111.337 11.8061 111.462 13.7596C111.472 13.9153 111.345 14.0424 111.189 14.0424H104.099C103.925 14.0424 103.792 13.8857 103.828 13.7155C104.274 11.6288 105.615 10.532 107.743 10.532ZM124.813 23.064C124.77 23.1692 124.667 23.2378 124.553 23.2378H121.205C121.089 23.2378 120.985 23.1671 120.942 23.0595L115.078 8.27795C115.004 8.09255 115.141 7.89138 115.34 7.89138H118.768C118.885 7.89138 118.99 7.96398 119.032 8.07374L121.634 14.9433C122.045 16.0969 122.406 17.1521 122.664 17.9849C122.754 18.273 123.25 18.2731 123.341 17.9856C123.618 17.1075 124.004 16.0386 124.43 14.9433L127.155 8.06971C127.198 7.96207 127.302 7.89138 127.418 7.89138H130.717C130.918 7.89138 131.055 8.09646 130.977 8.28249L124.813 23.064Z" fill="url(#paint0_linear_470_15680)"/>
|
||||
<path d="M0 15.5335C0 20.4108 3.04442 23.6416 7.70425 23.6416C11.5916 23.6416 14.5144 21.3323 15.1114 17.9329C15.1408 17.765 15.0088 17.6149 14.8383 17.6149H11.5614C11.4309 17.6149 11.3184 17.705 11.2808 17.83C10.8176 19.3719 9.51665 20.2244 7.70425 20.2244C5.28114 20.2244 3.78999 18.4226 3.78999 15.5335C3.78999 12.6444 5.4054 10.8115 7.82851 10.8115C9.55529 10.8115 10.797 11.639 11.2803 13.2392C11.3177 13.3632 11.4298 13.4521 11.5594 13.4521H14.814C14.9821 13.4521 15.1134 13.3058 15.0884 13.1395C14.5611 9.62446 11.7476 7.4254 7.67318 7.4254C3.13762 7.4254 0 10.7805 0 15.5335Z" fill="#140505"/>
|
||||
<path d="M26.6792 8.04975C26.6792 7.92016 26.5911 7.80665 26.4646 7.77846C25.9342 7.66022 25.4679 7.61179 25.0017 7.61179C23.4123 7.61179 22.1776 8.22781 21.3989 9.21386C21.2178 9.44318 20.7606 9.35331 20.7323 9.06247L20.6462 8.17753C20.6322 8.03283 20.5105 7.92245 20.3652 7.92245H17.3313C17.1753 7.92245 17.0489 8.04889 17.0489 8.20486V22.9553C17.0489 23.1113 17.1753 23.2378 17.3313 23.2378H20.5565C20.7125 23.2378 20.8389 23.1113 20.8389 22.9553V15.782C20.8389 12.7997 22.5475 11.3397 25.2813 11.3397H26.3968C26.5528 11.3397 26.6792 11.2132 26.6792 11.0572V8.04975Z" fill="#140505"/>
|
||||
<path d="M26.8959 15.5335C26.8959 20.3176 30.3442 23.6105 35.0972 23.6105C39.8503 23.6105 43.2985 20.3176 43.2985 15.5335C43.2985 10.7494 39.8503 7.45646 35.0972 7.45646C30.3442 7.45646 26.8959 10.7494 26.8959 15.5335ZM30.6859 15.5335C30.6859 12.7376 32.4877 10.8426 35.0972 10.8426C37.7068 10.8426 39.5085 12.7376 39.5085 15.5335C39.5085 18.3294 37.7068 20.2244 35.0972 20.2244C32.4877 20.2244 30.6859 18.3294 30.6859 15.5335Z" fill="#140505"/>
|
||||
<path d="M73.9835 23.6416C75.9481 23.6416 77.6697 22.9127 78.72 21.6226C78.8964 21.4059 79.3011 21.4955 79.33 21.7735L79.4558 22.9845C79.4707 23.1284 79.592 23.2378 79.7367 23.2378H82.679C82.835 23.2378 82.9614 23.1113 82.9614 22.9553V0.407414C82.9614 0.251441 82.835 0.125 82.679 0.125H79.4849C79.329 0.125 79.2025 0.251441 79.2025 0.407414V8.87388C79.2025 9.15681 78.7947 9.28936 78.5957 9.0883C77.549 8.03107 75.9585 7.4254 74.201 7.4254C69.5722 7.4254 66.7763 10.8426 66.7763 15.6267C66.7763 20.3797 69.5411 23.6416 73.9835 23.6416ZM74.8223 20.1623C72.1817 20.1623 70.5663 18.2362 70.5663 15.5024C70.5663 12.7687 72.1817 10.8115 74.8223 10.8115C77.4629 10.8115 79.1714 12.7376 79.1714 15.5024C79.1714 18.2673 77.4629 20.1623 74.8223 20.1623Z" fill="#140505"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M44.468 11.2986C44.2998 11.3291 44.197 11.5008 44.2497 11.6635L47.9351 23.0424C47.9729 23.1588 48.0814 23.2378 48.2038 23.2378H51.4892C51.612 23.2378 51.7206 23.1585 51.7581 23.0416L53.8077 16.6519C54.202 15.3742 54.4682 14.465 54.655 13.7859C54.7379 13.4849 55.2539 13.4953 55.3278 13.7986C55.5105 14.5481 55.7667 15.4853 56.1065 16.5897L58.1564 23.0409C58.1937 23.1581 58.3025 23.2378 58.4256 23.2378H61.5593C61.6797 23.2378 61.7868 23.1615 61.8262 23.0477L66.9429 8.26618C67.0064 8.08272 66.8702 7.89138 66.676 7.89138H63.2731C63.1494 7.89138 63.0402 7.9718 63.0034 8.08985L61.0149 14.4773C60.8324 15.1057 60.5574 16.1045 60.3192 17.042C60.2432 17.3415 59.7535 17.3377 59.6796 17.0377C59.4283 16.0181 59.1181 14.8855 58.9956 14.4773L57.0071 8.08985C56.9703 7.9718 56.8611 7.89138 56.7374 7.89138H53.2992C53.1764 7.89138 53.0677 7.97066 53.0302 8.08754L50.9807 14.4773C50.6607 15.4573 50.4308 16.2056 50.233 17.0122C50.1589 17.3145 49.6915 17.3143 49.6243 17.0104C49.427 16.1185 49.2164 15.2712 48.9925 14.4773L47.9046 10.929C47.8627 10.7922 47.725 10.7084 47.5843 10.7339L44.468 11.2986Z" fill="#140505"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_470_15680" x1="93.4995" y1="15.0132" x2="84.4995" y2="16.0133" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E94F2E"/>
|
||||
<stop offset="1" stop-color="#CA2400"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
BIN
apps/formbricks-com/images/clients/stack-ocean-dark.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
apps/formbricks-com/images/clients/stack-ocean-light.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
1
apps/formbricks-com/images/github-mark-white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 960 B |
1
apps/formbricks-com/images/github-mark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>
|
||||
|
After Width: | Height: | Size: 963 B |
@@ -49,6 +49,13 @@ const navigation = [
|
||||
title: "Self-hosting",
|
||||
links: [{ title: "Deployment", href: "/docs/self-hosting/deployment" }],
|
||||
},
|
||||
{
|
||||
title: "Contributing",
|
||||
links: [
|
||||
{ title: "Introduction", href: "/docs/contributing/introduction" },
|
||||
{ title: "Setup Dev Environment", href: "/docs/contributing/setup" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default navigation;
|
||||
|
||||
70
apps/formbricks-com/lib/handleFeedbackSubmit.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
export const handleFeedbackSubmit = async (YesNo, pageUrl) => {
|
||||
const response_data = {
|
||||
data: {
|
||||
isHelpful: YesNo,
|
||||
pageUrl: pageUrl,
|
||||
},
|
||||
};
|
||||
|
||||
const payload = {
|
||||
response: response_data,
|
||||
surveyId: process.env.NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_FORMBRICKS_COM_API_HOST}/api/v1/client/environments/${process.env.NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID}/responses`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
}
|
||||
);
|
||||
|
||||
if (res.ok) {
|
||||
const responseJson = await res.json();
|
||||
return responseJson.id; // Return the response ID
|
||||
} else {
|
||||
console.error("Error submitting form");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error submitting form:", error);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateFeedback = async (freeText, responseId) => {
|
||||
if (!responseId) {
|
||||
console.error("No response ID available");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
response: {
|
||||
data: {
|
||||
additionalInfo: freeText,
|
||||
},
|
||||
finished: true,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_FORMBRICKS_COM_API_HOST}/api/v1/client/environments/${process.env.NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID}/responses/${responseId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
}
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
console.error("Error updating response");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating response:", error);
|
||||
}
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
|
||||
import nextMDX from "@next/mdx";
|
||||
import { withPlausibleProxy } from "next-plausible";
|
||||
import remarkGfm from "remark-gfm";
|
||||
@@ -5,75 +7,69 @@ import rehypePrism from "@mapbox/rehype-prism";
|
||||
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
transpilePackages: ["@formbricks/ui"],
|
||||
pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"],
|
||||
transpilePackages: ["@formbricks/ui"],
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: "/discord",
|
||||
destination: "https://discord.gg/3YFcABF2Ts",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/roadmap",
|
||||
destination: "https://github.com/orgs/formbricks/projects/1",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/github",
|
||||
destination: "https://github.com/formbricks/formbricks",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/privacy",
|
||||
destination: "/privacy-policy",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/form-hq",
|
||||
destination: "/formbricks-hq",
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs",
|
||||
destination: "/docs/introduction/what-is-formbricks",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/formbricks-hq/self-hosting",
|
||||
destination: "/docs",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/react-form-library/getting-started",
|
||||
destination: "/docs",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/react-form-library/work-with-components",
|
||||
destination: "/docs",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/react-form-library/introduction",
|
||||
destination: "/docs",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/formbricks-hq/schema",
|
||||
destination: "/docs",
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: "/demo",
|
||||
destination: "https://app.formbricks.com/demo",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/pmf",
|
||||
destination: "/",
|
||||
permanent: false,
|
||||
permanent: true,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -12,40 +12,39 @@
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.3.3",
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@heroicons/react": "^2.0.16",
|
||||
"@headlessui/react": "^1.7.14",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/mdx": "^13.2.4",
|
||||
"@next/mdx": "^13.3.0",
|
||||
"add": "^2.0.6",
|
||||
"clsx": "^1.2.1",
|
||||
"lottie-web": "^5.10.2",
|
||||
"next": "13.2.4",
|
||||
"lottie-web": "^5.11.0",
|
||||
"next": "13.3.0",
|
||||
"next-plausible": "^3.7.2",
|
||||
"next-sitemap": "^4.0.5",
|
||||
"next-sitemap": "^4.0.7",
|
||||
"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": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@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-dom": "18.0.11",
|
||||
"@types/react-responsive-embed": "^2.1.0",
|
||||
"@types/react": "^18.0.35",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "8.36.0",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"typescript": "4.9.5"
|
||||
"postcss": "^8.4.22",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Image from "next/image";
|
||||
import { Callout } from "@/components/shared/Callout";
|
||||
import HeroAnimation from "@/components/shared/HeroAnimation.tsx";
|
||||
import MdxTryItCTA from "@/components/shared/MdxTryItCTA.tsx";
|
||||
import HeaderImage from "/images/SEO/Best React Form Library and Builder 2023 to create and build forms surveys easy and fast.png";
|
||||
import GithubStars from "/images/SEO/Stars - best open source react survey builder 2023 comparison of github repository stars for form builder library.png";
|
||||
import Winner from "/images/SEO/Winner comparison best React survey Library and Builder 2023 2022.png";
|
||||
|
||||
export const meta = {
|
||||
title: "Best React Form Library 2023 (easy form creation)",
|
||||
description:
|
||||
"What is the best form & survey library for React? React forms and surveys can be pretty annoying. We compared three form builder libraries in 2023.",
|
||||
};
|
||||
|
||||
_Building forms in React natively is pretty annoying. Different input mechanisms, validations, data transformations, initial default values... it never ends. Today we will look at tools which makes all of this a whole lot easier. Let’s have a look!_
|
||||
|
||||
<Image
|
||||
src={HeaderImage}
|
||||
alt="Best React form library builder comparison between Formik Formbricks and React Form Builder 2"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
|
||||
We’re bringing you three different solutions with different pros and cons. But first...
|
||||
|
||||
<Callout title="What's your objective?" type="note">
|
||||
Do you want to build a highly customized form optimized to the last millisecond? Do you want a slick,
|
||||
multi-page survey? Or do you need a visual editor to have your users create forms? Anyways, we've got you
|
||||
covered!
|
||||
</Callout>
|
||||
|
||||
## What we compare: Ease of form creation, custom styling and additional work required
|
||||
|
||||
We have tested 12 different libraries to share the 3 most compelling ones with you. In order to build exactly the form you envision in the shortest amount of time possible, we shine light one these three aspects of the contending React form libraries:
|
||||
|
||||
### 1. Ease of building forms
|
||||
|
||||
Here we looked at the overall developer experience and the “completeness” of the solution. In other words: Will you be able to build the form you want just with this library? And how fast?
|
||||
|
||||
### 2. Ease of custom styling
|
||||
|
||||
In many cases forms have to have certain look & feel to match the application you are building. Here we look at how easy it is to get this look & feel done.
|
||||
|
||||
### 3. Additional work required to access data
|
||||
|
||||
When the form is done the next question comes up: Where do I pipe the submissions to? And how can I or someone else analyze them? Here we look at how quickly you can achieve your ultimate goal which is not creating forms but gathering and accessing qualitative data.
|
||||
|
||||
Now that we have this cleared out of the way, let’s look at how popular the solutions are:
|
||||
|
||||
<Image
|
||||
src={GithubStars}
|
||||
alt="Comparison of GitHub Stars of the best react form builder libraries 2023"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
|
||||
As you can pretty well see, **Formik** plays in a different league. Launched 4 years ago, it has accumulated over 31k stars - kudos!
|
||||
|
||||
**React Form Builder 2** is almost as old as Formik but never took off. However, very recently it is more actively developed and starts picking up traction (6k weekly NPM downloads).
|
||||
|
||||
**Formbricks** is a pretty new repository which collected 1.1k stars over the past couple of weeks.
|
||||
|
||||
Let’s have a closer look!
|
||||
|
||||
## Closer look: Who is competing?
|
||||
|
||||
### 1. Formbricks React Form Library
|
||||
|
||||
The Formbricks [React Library](/react-form-library) is the newests kid on the block. While still early, it looks very promising. It uses the impressive performance of React Hook Forms with the objective to make it faster and easier to use. With a modern developer experience and a growing number of HTML and non-HTML question-types, it’s a sweet option for most use cases.
|
||||
|
||||
<Callout title="No backend needed" type="note">
|
||||
Even though Formbricks React works as a standalone solution, the full power is unleashed when using it with the [self-hostable dashboard](/formbricks-hq). Setup webhooks, integrations & email notifications or directly view at your data.
|
||||
|
||||
</Callout>
|
||||
|
||||
### 2. Formik
|
||||
|
||||
No React form generator comparison post can do without Formik. Formik is alongside React Hook Forms by far the most popular library to build forms in React (5M weekly downloads combined). It’s fast, it’s comprehensive and it’s battletested. It comes with input validation, formatting, masking, arrays, and error handling.
|
||||
|
||||
### 3. React Form Builder 2
|
||||
|
||||
The React Form Builder 2 adds a visual builder to the mix. It comes with React Drag and Drop out of the box that interfaces with a JSON endpoint to load and save generated forms via a schema. It contains 16 question types ranging from star rating question to a signature field. The updated version 2 can be extended with custom components easily. It is a hobby project which picked up quite a bit of traction recently.
|
||||
|
||||
<Callout title="In development" type="warning">
|
||||
The Formbricks React Library is currently in alpha testing. Most features are still in development. Follow
|
||||
us on [Twitter](https://twitter.com/formbricks) to stay uptodate on the release!
|
||||
</Callout>
|
||||
|
||||
##
|
||||
|
||||
# Overview
|
||||
|
||||
Before we get into the details, here is the overview for you:
|
||||
|
||||
| React Library | Ease of building forms | Ease of custom styling | Additional work required |
|
||||
| -------------------- | ---------------------- | ---------------------- | ------------------------ |
|
||||
| Formbricks React | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| Formik | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| React Form Builder 2 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
|
||||
|
||||
## Ease of building forms
|
||||
|
||||
When you made a decision on which React Library to use, the last thing you want is to go out and look for another package just because your initial choice lacks e.g. a slider component.
|
||||
|
||||
**Formbricks React** currently offers 8 question types and keeps adding more in a fast pace. Check out [the repository](https://formbricks.com/github) for more details.
|
||||
|
||||
**React Form Builder** comes with 16 questions types out of the box. It includes star-rating questions, NPS questions, etc. Additionally, the second version makes it really easy to add your custom components to it, if you want.
|
||||
|
||||
**Formik** only supports standard HTML question types out of the box. Anything non-HTML (like a date picker, sliders, rating questions) you have to build yourself or find compatible packages for.
|
||||
|
||||
## Ease of custom styling
|
||||
|
||||
When it comes to custom styling, the options differ quiet a bit. Let’s start with React Form Builder 2 as it is the most limited:
|
||||
|
||||
**React Form Builder 2** uses Bootstrap so within the realms of Bootstrap you are free to make changes. If you don’t want to use Bootstrap, you’ll have a hard time styling your form and form builder.
|
||||
|
||||
**Formik** works smoothly with Tailwind, if you know how to set it up. In a nutshell, you can pass predefined tailwind styles from the parent component to avoid repetition and keep our form file tidy. [Here is a tutorial which touches upon it](https://dev.to/przpiw/build-elegant-forms-reactformik-tailwind-54d8). Here is a sneak peak:
|
||||
|
||||
```jsx
|
||||
export const LoginForm = ({styles}) => (
|
||||
<Formik>
|
||||
<Form>
|
||||
<label className={styles.label} htmlFor='Email'>
|
||||
Email
|
||||
</label>
|
||||
<Field className={styles.field} id='email' name='email' />
|
||||
<ErrorMessage component='a' className={styles.errorMsg} name='email' />
|
||||
<label className={styles.label} htmlFor='Email'>
|
||||
Password
|
||||
</label>
|
||||
<Field className={styles.field} id='password' name='password' />
|
||||
<ErrorMessage
|
||||
component='a'
|
||||
className={styles.errorMsg}
|
||||
name='password'
|
||||
/>
|
||||
<div className='mt-8'>
|
||||
<button type='submit' className={styles.button}>
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
```
|
||||
|
||||
**Formbricks React** was built with a great developer experience in mind. There is a native Tailwind support so you can style your components with “className” like you’re used to in React:
|
||||
|
||||
```jsx
|
||||
<FormElement type="text" name="name" label="Your name" className="rounded-sm" />
|
||||
```
|
||||
|
||||
Not using Tailwind? Maybe you should 😛 Jokes aside, you can also pass a custom style sheet and Formbricks React will apply it to your form. As soon as the React Lib is in public beta, we’ll add a step-by-step tutorial [in our docs](/docs).
|
||||
|
||||
## Additional work required
|
||||
|
||||
Building your form or survey is only the first step. What happens with the qualitative data after your respondents hit “Submit”?
|
||||
|
||||
### React Form Builder
|
||||
|
||||
Unfortunately, the data handling is not part of the scope of the React Form Builder 2 package.
|
||||
|
||||
### Formik (Formium) & Formbricks
|
||||
|
||||
Not many know that Formik has a commercial counterpart called Formium. Formium offers a versatile submission API where you can pipe your submissions to and take it from there.
|
||||
|
||||
Formbricks has a very similar offer called Formbricks HQ which lets you forward form responses via email or integrate with third-party tools. Here is a comparison of their free plans:
|
||||
|
||||
| Feature | Formium (free) | Formbricks HQ (free) |
|
||||
| ------------------------- | -------------- | -------------------- |
|
||||
| Free submissions / month | 100 | 500 |
|
||||
| File Uploads | 100MB | 200MB |
|
||||
| Auto Responder | ✅ | ⚙️ |
|
||||
| Email forwarding | ✅ | ✅ |
|
||||
| Custom Email Templates | ✅ | ⚙️ |
|
||||
| Multiple Email Recipients | ✅ | ✅ |
|
||||
| Custom Mail Server | ✅ | ⚙️ |
|
||||
| Webhooks | ✅ | ✅ |
|
||||
| Airtable Integration | ✅ | ⚙️ |
|
||||
| Google Sheets Integration | ✅ | ⚙️ |
|
||||
| Zapier Integration | ✅ | ⚙️ |
|
||||
| Automated Workflows | ❌ | ⚙️ |
|
||||
| Easy self-hosting | ❌ | ✅ |
|
||||
| Can be used in EU? | ❌ | ✅ |
|
||||
| Last feature shipped | 2020 | Nov 2022 |
|
||||
|
||||
<Callout title="In development" type="note">
|
||||
The Formbricks HQ is currently in alpha testing. Most features are still in development. Follow us on
|
||||
[Twitter](https://twitter.com/formbricks) to stay uptodate on the release!
|
||||
</Callout>
|
||||
|
||||
Overall, both tools have a very similar set of features. However, Formium has not been further developed over the past two years whereas Formbricks shows quite some ambition to offer a full suite of form and survey solutions on the one open-source platform.
|
||||
|
||||
<Callout title="Formium no option for EU-based companies" type="warning">
|
||||
|
||||
Since Formium is hosted in the US and operated by a US entity, European companies and institutions cannot use Formium without running the legal risk of violating the data privacy of the respondents.
|
||||
|
||||
</Callout>
|
||||
##
|
||||
# Results
|
||||
|
||||
### 3rd place
|
||||
|
||||
[React Form Builder 2](https://github.com/kiho/react-form-builder) has a unique differentiator: A visual form builder. If this is essential for what you're building, this is your choice.7
|
||||
|
||||
### 2nd place
|
||||
|
||||
[Formik](https://formik.org/) has a lot of strengths - and a few weaknesses. It is very customizable and comes with a powerful validation engine. Many developers are very happy with the reduced complexity compared to building forms natively in React. However, it isn't very easy to get started with for junior developers. Lastly, it causes a lot of rerenders, something that React Hook Form solved better.
|
||||
|
||||
### 1st place
|
||||
|
||||
It's still verrry early for [Formbricks](/react-form-library). However, the approach looks promising as it makes working with forms and surveys in React a lot easier. The React Library packs a lot of powerful features which makes it by far the easiest option for coding forms in React. The upcoming Formbricks HQ takes setting up a backend off developers plate completely. The modularity and extendability makes it a future-proof choice.
|
||||
|
||||
<Image
|
||||
src={Winner}
|
||||
alt="Best React form library builder comparison between Formik Formbricks and React Form Builder 2"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
|
||||
Follow us on [Twitter](https://twitter.com/formbricks) or [join our Discord community](https://formbricks.com/discord) to stay uptodate on the progress.
|
||||
|
||||
<MdxTryItCTA />
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,118 @@
|
||||
import Image from "next/image";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Formbricks from "./open-source-survey-software-free-2023-formbricks-typeform-alternative.png";
|
||||
import Typebot from "./typebot-open-source-free-conversational-form-builder-survey-software-opensource.jpg";
|
||||
import LimeSurvey from "./free-survey-tool-limesurvey-open-source-software-opensource.png";
|
||||
import OpnForm from "./opnform-free-open-source-form-survey-tools-builder-2023-self-hostign.jpg";
|
||||
import HeaderImage from "./2023-title-best-open-source-survey-software-tools-and-alternatives.png";
|
||||
import SurveyJS from "./surveyjs-free-opensource-form-survey-tool-software-to-make-surveys-2023.png";
|
||||
|
||||
export const meta = {
|
||||
title: "Best Open-source Form & Survey Tools (still maintained in 2023)",
|
||||
description:
|
||||
"Most open-source projects get abandoned after a while. But these 5 open-source form and survey tools are still alive and kicking in 2023.",
|
||||
date: "2023-04-12",
|
||||
};
|
||||
|
||||
_Most open-source projects get abandoned after a while. But these 5 open-source form and survey tools are still alive and kicking in 2023._
|
||||
|
||||
<Image
|
||||
src={HeaderImage}
|
||||
alt="Free and self-hostable: Find the 5 best (and maintained) open-source survey tools 2023."
|
||||
className="rounded-lg"
|
||||
/>
|
||||
|
||||
Looking for the perfect open-source form and survey tool to help you gather valuable insights and improve your business? Look no further!
|
||||
|
||||
We've compiled a list of the top 5 open-source form and survey tools that are still maintained in 2023. In-product surveys, conversational bots, AI-generated surveys: These tools offer various features that cater to different needs.
|
||||
|
||||
## 1. Formbricks - In-product micro-surveys
|
||||
|
||||
<Image
|
||||
src={Formbricks}
|
||||
alt="Formbricks is a free and open-source survey software for in-product micro-surveys. Ask any segment at any point in time."
|
||||
className="rounded-lg"
|
||||
/>
|
||||
|
||||
Formbricks is a powerful open-source form and survey solution designed to help you get better experience data for your business. This tool allows you to survey specific customer segments at any point in the user journey, providing you with invaluable insights into what your customers think and feel.
|
||||
|
||||
- 👍 **Pre-segment users:** Don't ask everyone, all the time. Granularly segment your user base to get deep insights
|
||||
- 👍 **Event-based surveys:** Trigger surveys based on user behavior, such as page views, clicks, and more
|
||||
- 👍 **Extensive template library:** Choose from a wide range of templates to create surveys that answer your question
|
||||
- 👍 **Easy self-hosting:** Docker makes it possible to self-host Formbricks in minutes.
|
||||
- ⚠️ **It's early for Formbricks.** Product developes rapidly but might encounter a bug here and there.
|
||||
|
||||
[Try it out](https://app.formbricks.com), [read more](https://formbricks.com) or dive into the [code base.](https://formbricks.com/github)
|
||||
|
||||
## 2. SurveyJS - Build-it-yourself libraries
|
||||
|
||||
<Image src={SurveyJS} alt="SurveyJS is a comprehensive" className="rounded-lg" />
|
||||
|
||||
SurveyJS is a collection of JavaScript Librarys to build forms. Building your own form management system has never been easier than with SurveyJS. It packs:
|
||||
|
||||
- 👍 **Library:** A JavaScript library to render surveys and forms
|
||||
- 👍 **Analytics:** A dashboard to analyze survey and form results
|
||||
- 👍 **Editor:** A visual editor to create surveys and forms
|
||||
- 👍 **PDF:** A library to render survey and form rersults as PDF
|
||||
- ⚠️ **Pricing:** Starts at $499 / year
|
||||
|
||||
[Dive into the code on GitHub](https://github.com/surveyjs)
|
||||
|
||||
## 3. Typebot - Truly conversational Forms
|
||||
|
||||
<Image src={Typebot} alt="SurveyJS is a comprehensive" className="rounded-lg" />
|
||||
|
||||
Coming in at number three on our list is Typebot, that makes it really easy to create conversational forms and surveys. Typebot helps you engage with your audience in a more interactive way, leading to higher response rates and better data. It comes with:
|
||||
|
||||
- 👍 **A slick visual** editor to create conversational forms and surveys
|
||||
- 👍 A library to render conversational forms and surveys **on your website**
|
||||
- 👍 A dashboard to **analyze survey and form results**
|
||||
- 👍 **Lots of integrations** to pipe your data to
|
||||
- ⚠️ **Limited to conversational forms.** If you need more classical forms, this might not be the right tool for you.
|
||||
|
||||
[Dive into the code on GitHub](https://github.com/baptisteArno/typebot.io)
|
||||
|
||||
## 4. OpnForm - Straight-forward survey builder
|
||||
|
||||
<Image src={OpnForm} alt="SurveyJS is a comprehensive" className="rounded-lg" />
|
||||
|
||||
OpnForms is a flexible and powerful open-source form and survey tool designed to make data collection easy and efficient. OpnForm packs lots of features, especially for a Beta:
|
||||
|
||||
- 👍 **Multiple Question Types:** Choose from a wide variety of question types to create highly customizable forms and surveys.
|
||||
- 👍 **Conditional Logic:** Show or hide questions based on previous responses to create personalized surveys.
|
||||
- 👍 **Export Data:** Easily export collected data in various formats for further analysis.
|
||||
- ⚠️ The UI is a bit **stuffed and not very intuitive.**
|
||||
|
||||
[Dive into the code on GitHub](https://github.com/JhumanJ/OpnForm)
|
||||
|
||||
## 5. LimeSurvey - Old (but gold?)
|
||||
|
||||
<Image src={LimeSurvey} alt="SurveyJS is a comprehensive" className="rounded-lg" />
|
||||
|
||||
LimeSurvey has been around for at least a decade. It's a powerful survey tool made for more classical, scientific surveying. It packs:
|
||||
|
||||
- 👍 **Multilingual Surveys:** Create surveys in multiple languages to reach a global audience.
|
||||
- 👍 **Extensive Question Types:** Choose from a wide range of question types to create highly engaging surveys.
|
||||
- 👍 **Advanced Logic:** Utilize advanced survey logic features to personalize your surveys and capture more accurate data.
|
||||
- 👍 **Survey Templates:** Start with pre-built survey templates to save time and effort.
|
||||
- ⚠️ **Very old-fashioned UI:** The product is quite the opposite of slick and user-friendly.
|
||||
|
||||
[Dive into the code on GitHub](https://github.com/LimeSurvey/LimeSurvey)
|
||||
|
||||
## Summary ☟
|
||||
|
||||
In this article, we've rounded up the top 5 open-source form and survey tools that are still rocking it in 2023. Perfect for devs who are always on the lookout for the latest and greatest!
|
||||
|
||||
1. Formbricks: A game-changer for in-product micro-surveys, letting you target specific customer segments at any point in their journey. It's still early days, but this bad boy is worth keeping an eye on.
|
||||
|
||||
2. SurveyJS: A must-have for DIY enthusiasts, this collection of JavaScript libraries makes building your own form management system a breeze. Just remember, the starting price is $499/year.
|
||||
|
||||
3. Typebot: Make your forms and surveys truly conversational with Typebot's slick visual editor and user-friendly interface. Just note that it's limited to conversational forms.
|
||||
|
||||
4. OpnForm: A flexible and powerful tool that's all about making data collection simple and efficient. It's still in Beta, so the UI might not be super polished, but it's packed with features.
|
||||
|
||||
5. LimeSurvey: An oldie but a goldie. This veteran survey tool is perfect for more traditional, scientific surveying. Just be prepared to deal with its old-fashioned UI.
|
||||
|
||||
Take a peek at these tools, and we're sure you'll find one that suits your needs. Happy surveying!
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,37 @@
|
||||
import Image from "next/image";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import TitleImage from "./formbricks-sponsored-by-github-accelerator-2023.webp";
|
||||
import NewsletterSignup from "@/components/shared/NewsletterSignup";
|
||||
|
||||
export const meta = {
|
||||
title: "Formbricks Joins GitHub Accelerator's Inaugural Cohort 💃",
|
||||
description:
|
||||
"We're getting ready to take our open-source experience management platform to new heights, thanks to being part of the first-ever GitHub Accelerator program!",
|
||||
date: "2023-04-13",
|
||||
};
|
||||
|
||||
_We're getting ready to take our open-source experience management platform to new heights, thanks to being part of the first-ever GitHub Accelerator program!_
|
||||
|
||||
<Image
|
||||
src={TitleImage}
|
||||
alt="GitHub sponsors Formbricks to join their open-source accelerator program"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
|
||||
## Hey there,
|
||||
|
||||
fellow open-source enthusiasts! We've got some fantastic news: Formbricks has been chosen for the inaugural GitHub Accelerator program!
|
||||
|
||||
Launched in November 2022, the GitHub Accelerator program is all about fostering a sustainable open-source ecosystem and helping projects like ours turn into full-time careers. We're stoked to be **one of the 20 projects in the 2023 cohort, chosen from a whopping 1,000+ applications.**
|
||||
|
||||
As part of the GitHub Accelerator, we'll be attending weekly sessions with open-source gurus, diving into topics like licensing, governance, funding goals, sponsorships, and a whole lot more. Once we've soaked up all that knowledge, we'll share our learnings and experiences with the ever-growing open-source community.
|
||||
|
||||
This opportunity is a game-changer for Formbricks, as the support from the GitHub Accelerator will help us level up our platform even more. It's a massive win for everyone who loves open-source experience management solutions, and we can't wait to see where this journey takes us!
|
||||
|
||||
[Read GitHub announcement](https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next/)
|
||||
|
||||
### From now on, we're official sponsored by GitHub! 🎉
|
||||
|
||||
<NewsletterSignup />
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
@@ -1,10 +1,9 @@
|
||||
import Head from "next/head";
|
||||
import { Card } from "@/components/shared/Card";
|
||||
import Link from "next/link";
|
||||
import HeroTitle from "@/components/shared/HeroTitle";
|
||||
import Layout from "@/components/shared/Layout";
|
||||
import { getAllArticles } from "@/lib/articles";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
import HeroTitle from "@/components/shared/HeroTitle";
|
||||
import Link from "next/link";
|
||||
|
||||
function Article({ article }: any) {
|
||||
return (
|
||||
|
||||
@@ -94,8 +94,8 @@ The first step is building the React Form Builder into a full-fledged product. W
|
||||
|
||||
Alongside the React Form Builder we'll redesign our Core API. You'll be able to:
|
||||
|
||||
- Send any form to our API endpoint to view and manage submissions on [formbricks.com](/formbricks-hq)
|
||||
- Forward your data however you like: [Email](/email), [webhooks](/webhooks), 3rd party integrations
|
||||
- Send any form to our API endpoint to view and manage submissions on [formbricks.com](https://app.formbricks.com)
|
||||
- Forward your data however you like: Email, Webhooks, 3rd party integrations
|
||||
|
||||
Please [join our Discord](https://discord.com/invite/3YFcABF2Ts) to provide feedback on what's important to you!
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ const CommunityPage = () => {
|
||||
<p className="mt-2 text-sm text-slate-500 dark:text-slate-400">
|
||||
The leader board of the Formbricks community contributors 🙌
|
||||
</p>
|
||||
<ol className="mt-10 ml-4 list-decimal">
|
||||
<ol className="ml-4 mt-10 list-decimal">
|
||||
{topContributors.map((MVP) => (
|
||||
<li
|
||||
key={MVP.name}
|
||||
@@ -58,7 +58,7 @@ const CommunityPage = () => {
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<div className="rounded-lg bg-gradient-to-b from-slate-200 to-slate-300 px-10 pt-6 pb-12 dark:from-slate-800 dark:to-slate-700">
|
||||
<div className="rounded-lg bg-gradient-to-b from-slate-200 to-slate-300 px-10 pb-12 pt-6 dark:from-slate-800 dark:to-slate-700">
|
||||
<h3 className="mt-7 text-3xl font-bold text-slate-800 dark:text-slate-200 xl:text-4xl">
|
||||
Community Discord
|
||||
</h3>
|
||||
@@ -66,7 +66,7 @@ const CommunityPage = () => {
|
||||
Get support for anything your building - or just say hi 👋
|
||||
</p>
|
||||
<Button
|
||||
className="mt-7 w-full justify-center font-bold"
|
||||
className="mt-7 w-full justify-center"
|
||||
variant="highlight"
|
||||
onClick={() => router.push("/discord")}>
|
||||
Join Discord <ChatBubbleOvalLeftEllipsisIcon className="ml-1 inline h-5 w-5" />
|
||||
|
||||
12
apps/formbricks-com/pages/demo/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import LayoutWaitlist from "@/components/shared/LayoutLight";
|
||||
import TemplateList from "@/components/dummyUI/TemplateList";
|
||||
|
||||
export default function DemoPage() {
|
||||
return (
|
||||
<LayoutWaitlist
|
||||
title="Formbricks Demo"
|
||||
description="Leverage 30+ templates to kick-start your experience management.">
|
||||
<TemplateList />
|
||||
</LayoutWaitlist>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import { Callout } from "@/components/shared/Callout";
|
||||
|
||||
export const meta = {
|
||||
title: "API Key Setup",
|
||||
description:
|
||||
"Generate, store, and delete personal API keys for secure Formbricks access. Ensure safekeeping to prevent unauthorized account control.",
|
||||
};
|
||||
|
||||
## Auth: Personal API key
|
||||
@@ -13,10 +15,10 @@ The API requests are authorized with a personal API key. This API key gives you
|
||||
|
||||
### How to generate an API key
|
||||
|
||||
1. Go to your profile at [app.formbricks.com](https://app.formbricks.com/app/me/settings).
|
||||
2. Select “Add key”.
|
||||
3. Label it (this is just for you).
|
||||
4. Copy the key. You won’t be able to see it again.
|
||||
1. Go to your settings on [app.formbricks.com](https://app.formbricks.com).
|
||||
2. Go to page “API keys”.
|
||||
3. Create a key for the development or production environment.
|
||||
4. Copy the key immediately. You won’t be able to see it again.
|
||||
|
||||
<Callout title="Store API key safely" type="warning">
|
||||
Anyone who has your API key has full control over your account. For security reasons, you cannot view the
|
||||
@@ -25,8 +27,9 @@ The API requests are authorized with a personal API key. This API key gives you
|
||||
|
||||
### Delete a personal API key
|
||||
|
||||
1. Go to your profile at [app.formbricks.com](https://app.formbricks.com/app/me/settings).
|
||||
2. Find the key you wish to revoke and select “Delete”.
|
||||
3. Your API key will stop working immediately.
|
||||
1. Go to settings on [app.formbricks.com](https://app.formbricks.com/app/me/settings).
|
||||
2. Go to page “API keys”.
|
||||
3. Find the key you wish to revoke and select “Delete”.
|
||||
4. Your API key will stop working immediately.
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { APILayout } from "@/components/shared/APILayout.tsx";
|
||||
|
||||
export const meta = {
|
||||
title: "API: Create response",
|
||||
description: "Learn how to create a new response to a survey via API.",
|
||||
};
|
||||
|
||||
<APILayout
|
||||
|
||||
@@ -4,6 +4,8 @@ import { APILayout } from "@/components/shared/APILayout.tsx";
|
||||
|
||||
export const meta = {
|
||||
title: "API Overview",
|
||||
description:
|
||||
"Explore Formbricks' APIs: Public Client API for client-side tasks, and User API for account management with secure API Key authentication.",
|
||||
};
|
||||
|
||||
Formbricks offers two types of APIs: the Public Client API and the User API. Each API serves a different purpose, has different authentication requirements, and provides access to different data and settings.
|
||||
|
||||
@@ -4,6 +4,7 @@ import { APILayout } from "@/components/shared/APILayout.tsx";
|
||||
|
||||
export const meta = {
|
||||
title: "API: Update submission",
|
||||
description: "Learn how to update a new response to a survey via API.",
|
||||
};
|
||||
|
||||
<APILayout
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Layout } from "@/components/docs/Layout";
|
||||
|
||||
export const meta = {
|
||||
title: "Setting attributes with code",
|
||||
description:
|
||||
"Set attributes in code using setAttribute function. Enhance user segmentation, target surveys effectively, and gather valuable insights for better decisions. All open-source.",
|
||||
};
|
||||
|
||||
One way to send attributes to Formbricks is in your code. In Formbricks, there are two special attributes for [user identification](/docs/attributes/identify-users)(user ID & email) and custom attributes. An example:
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Layout } from "@/components/docs/Layout";
|
||||
|
||||
export const meta = {
|
||||
title: "Identifying Users",
|
||||
description:
|
||||
"Identify users with Formbricks by setting User ID, email, and custom attributes. Enhance survey targeting and recontacting while maintaining user privacy.",
|
||||
};
|
||||
|
||||
At Formbricks, we value user privacy. By default, Formbricks doesn't collect or store any personal information from your users. However, we understand that it can be helpful for you to know which user submitted the feedback and also functionality like recontacting users and controlling the waiting period between surveys requires identifying the users. That's why we provide a way for you to share existing user data from your app, so you can view it in our dashboard.
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Layout } from "@/components/docs/Layout";
|
||||
|
||||
export const meta = {
|
||||
title: "What are attributes and why are they useful?",
|
||||
description:
|
||||
"How to use attributes for user segmentation, enhancing survey targeting & results. Improve feedback quality and make data-driven decisions.",
|
||||
};
|
||||
|
||||
Surveying your user base without segmentation leads to weak results and survey fatigue. Attributes help you segment your users into groups.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Layout } from "@/components/docs/Layout";
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import { Callout } from "@/components/shared/Callout";
|
||||
|
||||
export const meta = {
|
||||
title: "Contribution Guide",
|
||||
description: "How to contribute to Formbricks",
|
||||
};
|
||||
|
||||
We are so happy that you are interested in contributing to Formbricks 🤗
|
||||
|
||||
There are many ways to contribute to Formbricks with writing Issues, fixing bugs, building new features or updating the docs.
|
||||
|
||||
## Issues
|
||||
|
||||
Spotted a bug? Has deployment gone wrong? Do you have user feedback? [Raise an issue](https://github.com/formbricks/formbricks/issues/new/choose) for the fastest response.
|
||||
|
||||
... or pick up and fix an issue if you want to do a Pull Request.
|
||||
|
||||
## Feature requests
|
||||
|
||||
Raise an issue for these and tag it as an Enhancement. We love every idea. Please give us as much context on the why as possible.
|
||||
|
||||
## Creating a PR
|
||||
|
||||
Please fork the repository, make your changes and create a new pull request if you want to make an update.
|
||||
|
||||
If you want to speak to us before doing lots of work, please join our [Discord server](https://formbricks.com/discord) and tell us what you would like to work on - we're very responsive and friendly!
|
||||
|
||||
For QA of your Pull-Request, you can also get in touch with Matti on Discord. But we will also get to your PR without you taking additional action ;-)
|
||||
|
||||
## Features
|
||||
|
||||
We are currently working on having a clear [Roadmap](https://github.com/formbricks/formbricks) for the next steps ahead.
|
||||
|
||||
But you can also pick a feature that is not already on the roadmap if you think it creates a positive impact for Formbricks.
|
||||
|
||||
If you are at all unsure, just raise it as an enhancement issue first and tell us that you like to work on it, and we'll very quickly respond.
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;
|
||||
81
apps/formbricks-com/pages/docs/contributing/setup/index.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Layout } from "@/components/docs/Layout";
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import { Callout } from "@/components/shared/Callout";
|
||||
|
||||
export const meta = {
|
||||
title: "Setup Dev Environment",
|
||||
description: "Setup a development environment for Formbricks.",
|
||||
};
|
||||
|
||||
To get the project running locally on your machine you need to have the following development tools installed:
|
||||
|
||||
- Node.JS (we recommend v18)
|
||||
- [pnpm](https://pnpm.io/)
|
||||
- [Docker](https://www.docker.com/) (to run PostgreSQL / MailHog)
|
||||
|
||||
1. Clone the project:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/formbricks/formbricks
|
||||
```
|
||||
|
||||
and move into the directory
|
||||
|
||||
```bash
|
||||
cd formbricks
|
||||
```
|
||||
|
||||
1. Install Node.JS packages via pnpm. Don't have pnpm? Get it [here](https://pnpm.io/installation)
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
1. To make the process of installing a dev dependencies easier, we offer a [`docker-compose.yml`](https://docs.docker.com/compose/) with the following servers:
|
||||
|
||||
- a `postgres` container and environment variables preset to reach it,
|
||||
- a `mailhog` container that acts as a mock SMTP server and shows received mails in a web UI (forwarded to your host's `localhost:8025`)
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
1. Create a `.env` file based on `.env.example` and change it according to your setup. If you are using a cloud based database or another mail server, you will need to update the `DATABASE_URL` and SMTP settings in your `.env` accordingly.
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
1. Make sure your PostgreSQL Database Server is running. Then let prisma set up the database for you:
|
||||
|
||||
```bash
|
||||
pnpm dlx prisma migrate dev
|
||||
```
|
||||
|
||||
1. Start the development server of the app:
|
||||
|
||||
```bash
|
||||
pnpm dev --filter=web...
|
||||
```
|
||||
|
||||
**You can now access the app on [https://localhost:3000](https://localhost:3000)**. You will be automatically redirected to the login. To use your local installation of formbricks, create a new account.
|
||||
|
||||
For viewing the confirmation email and other emails the system sends you, you can access mailhog at [https://localhost:8025](https://localhost:8025)
|
||||
|
||||
### Build
|
||||
|
||||
To build all apps and packages, run the following command:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Develop
|
||||
|
||||
To develop all apps and packages, run the following command:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;
|
||||
@@ -2,6 +2,8 @@ import { Layout } from "@/components/docs/Layout";
|
||||
|
||||
export const meta = {
|
||||
title: "Code Events",
|
||||
description:
|
||||
"Integrate code events in Formbricks using formbricks.track() to trigger surveys based on user actions, like button clicks, for precise insights. All open-source.",
|
||||
};
|
||||
|
||||
Events can also be set in the code base. You can fire an event using `formbricks.track()`
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Layout } from "@/components/docs/Layout";
|
||||
|
||||
export const meta = {
|
||||
title: "No-Code Events",
|
||||
description:
|
||||
"Utilize Formbricks' No-Code Events like Page URL, innerText, and CSS Selector for easy survey triggers and enhanced user insights.",
|
||||
};
|
||||
|
||||
No-Code events can be set up within Formbricks with just a few clicks. There are three types of No-Code events:
|
||||
@@ -17,12 +19,12 @@ The page URL event is triggered, when a user visits a specific page in your appl
|
||||
- `notMatch`: The URL should not match the specified condition.
|
||||
- `notContains`: The URL should not contain the specified string as a substring.
|
||||
|
||||
## innerText Event (coming soon)
|
||||
## innerText Event
|
||||
|
||||
The innerText event checks if the `innerText` of a clicked HTML element matches a specific text, e.g. the label of a button.
|
||||
The innerText event checks if the `innerText` of a clicked HTML element matches a specific text, e.g. the label of a button. Display a survey on any button click!
|
||||
|
||||
## CSS Selector Event (coming soon)
|
||||
## CSS Selector Event
|
||||
|
||||
The CSS Selector event checks if the provided CSS selector matches the selector of a clicked HTML element. The CSS selector can be a class, id or any other CSS selector within your website.
|
||||
The CSS Selector event checks if the provided CSS selector matches the selector of a clicked HTML element. The CSS selector can be a class, id or any other CSS selector within your website. Display a survey on any element click!
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Layout } from "@/components/docs/Layout";
|
||||
|
||||
export const meta = {
|
||||
title: "What are events and why are they useful?",
|
||||
description:
|
||||
"Events in Formbricks enable targeted survey displays during specific user journey moments. Enhance user segmentation by tracking events for granular surveying.",
|
||||
};
|
||||
|
||||
You want to understand what your users think and feel during specific moments in the user journey. To be able to ask at exactly the right point in time, you need events.
|
||||
|
||||