Compare commits

..

80 Commits

Author SHA1 Message Date
pandeymangg
ed383e7862 test perf 2024-09-26 20:08:14 +05:30
pandeymangg
3db7c7946b fix: testing perf 2024-09-26 15:00:40 +05:30
pandeymangg
2f63659739 fix: fixes debouncing in e2e tests 2024-09-25 18:50:35 +05:30
Piyush Gupta
b3757ae7c1 fix: typo 2024-09-25 15:53:10 +05:30
Piyush Gupta
942bf818a5 chore: add docs 2024-09-25 15:50:45 +05:30
Piyush Gupta
74b03a54e1 fix: reviews 2024-09-25 12:22:34 +05:30
Piyush Gupta
5282637772 Merge branch 'feat-advanced-logic-editor' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-25 10:30:09 +05:30
pandeymangg
9165daffe9 fix: perf 2024-09-25 10:26:41 +05:30
Piyush Gupta
de05d2abdf Merge branch 'feat-advanced-logic-editor' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-24 22:59:25 +05:30
Piyush Gupta
a907179c7d chore: variable name 2024-09-24 20:10:27 +05:30
Piyush Gupta
bc7d3e5da4 chore: code rabbit review changes 2024-09-24 20:08:32 +05:30
Johannes
7042d73e99 UI tweaks 2024-09-24 15:11:01 +02:00
Piyush Gupta
fe7ca5a923 fix: functionality and code bugs 2024-09-24 17:01:48 +05:30
Piyush Gupta
960edfd3f0 removes error throwing from data-migration 2024-09-24 11:15:42 +05:30
Piyush Gupta
2b0df8280d fix: build error 2024-09-24 09:51:26 +05:30
Piyush Gupta
13ce552a39 Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-23 23:23:50 +05:30
Piyush Gupta
4d6665ab3e Merge branch 'feat-advanced-logic-editor' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-23 23:22:02 +05:30
Piyush Gupta
483bdc0eff fix: code review changes 2024-09-23 23:21:46 +05:30
pandeymangg
8238b502fe fix: error 2024-09-23 18:18:22 +05:30
pandeymangg
35ff935a27 fix: lag 2024-09-23 17:00:30 +05:30
pandeymangg
901cd42f56 Merge branch 'feat-advanced-logic-editor' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-23 16:33:12 +05:30
pandeymangg
63e1ac11cf fix: lag 2024-09-23 16:32:55 +05:30
Piyush Gupta
744f3410ae Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-23 15:45:23 +05:30
pandeymangg
0e73d81999 fix 2024-09-23 12:19:11 +05:30
pandeymangg
ba46782da4 remove comment 2024-09-23 11:42:22 +05:30
pandeymangg
350c895d8c fixes 2024-09-20 09:51:35 +05:30
Piyush Gupta
4ed1747ee2 fix: variable and hidden field comparison 2024-09-15 21:29:30 +05:30
Piyush Gupta
88c492afd8 adds complex logic evaluate check 2024-09-13 12:25:51 +05:30
Piyush Gupta
c7e0b02595 feat: adds unit tests 2024-09-13 11:52:40 +05:30
Piyush Gupta
15aa9b2731 Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-12 22:05:32 +05:30
Piyush Gupta
6f7359abf6 feat: adds e2e tests 2024-09-12 22:05:25 +05:30
Piyush Gupta
0bf5e0fd4c fix: comparison fixes 2024-09-12 15:57:11 +05:30
Piyush Gupta
887c5c0eef feat: adds image support in inputCombobox 2024-09-11 16:48:23 +05:30
Piyush Gupta
af4ae38564 fix: input clear on operand change 2024-09-11 14:10:26 +05:30
Piyush Gupta
6b829744a1 fix: dhru review changes 2024-09-10 19:47:12 +05:30
Piyush Gupta
7b0d4926e8 fix: updated storybook component 2024-09-10 12:28:55 +05:30
Piyush Gupta
334166b4b1 fix: refine logic completed 2024-09-10 12:17:04 +05:30
Piyush Gupta
1685e77a35 fix: evaluate logic 2024-09-10 10:14:11 +05:30
Piyush Gupta
927f97e9ad Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-10 09:28:33 +05:30
Piyush Gupta
76c437b16a Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-09 20:39:43 +05:30
Johannes
0c2d425a45 tweak 2024-09-09 12:27:20 +02:00
Piyush Gupta
7aa827d1e1 Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-09 14:21:56 +05:30
Piyush Gupta
d4961f1840 fix: dropoff and comparison funciton 2024-09-09 14:21:20 +05:30
Piyush Gupta
6772ea6be4 fix: build error 2024-09-06 19:28:57 +05:30
Piyush Gupta
9bacc88063 fix: advanced logic data migration 2024-09-06 19:23:59 +05:30
Piyush Gupta
005c777c9c Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-06 15:34:54 +05:30
Piyush Gupta
560dce3bbf feat: add includeVariables option to integration settings 2024-09-06 15:18:25 +05:30
Piyush Gupta
ffd45a6f20 feat: adds variables in response table, response finished email, response card, download responses sheet 2024-09-06 14:27:16 +05:30
Piyush Gupta
1ddd82c084 fix: data migration for surveys with end destination 2024-09-06 14:25:16 +05:30
Piyush Gupta
647539c617 fix: undefined conditions or actions 2024-09-06 11:52:29 +05:30
Piyush Gupta
a5a3161f7c Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-06 11:20:48 +05:30
Piyush Gupta
0535581f6d WIP: adds survey refine for advanced logic 2024-09-05 22:05:42 +05:30
Piyush Gupta
97b04f9e43 changed left operand id -> value 2024-09-05 17:00:39 +05:30
Piyush Gupta
6ec0861c49 fix: inputCombobox styling 2024-09-05 15:19:48 +05:30
Piyush Gupta
8542320a8e fix build error 2024-09-05 12:54:32 +05:30
Piyush Gupta
7d3c8d35e1 Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-05 11:31:27 +05:30
Piyush Gupta
6abcf91a07 Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-04 11:39:24 +05:30
Piyush Gupta
9a7887d9fd Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-09-03 16:01:37 +05:30
Piyush Gupta
7dd8bb95bd fix: delete logicItem in case of no conditions 2024-09-03 10:38:13 +05:30
Piyush Gupta
df40c0ef11 adds ranking question logic 2024-09-02 16:19:42 +05:30
Piyush Gupta
7e14b86a63 fix: UI suggestions 2024-09-02 15:45:08 +05:30
Piyush Gupta
a1ba3af439 fix: data migration for incomplete logic 2024-09-01 20:57:35 +05:30
Piyush Gupta
0a8c5e384d fix: adds data migration script 2024-08-30 16:41:15 +05:30
Piyush Gupta
b20ce46a7b Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-08-29 20:29:45 +05:30
Piyush Gupta
58636a9d51 Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-08-29 09:16:23 +05:30
Piyush Gupta
328e3d0b9a adds conditional options in matchValue 2024-08-28 22:34:12 +05:30
Piyush Gupta
f1a2ecaa3a feat: adds data migration script WIP 2024-08-28 17:55:34 +05:30
Piyush Gupta
d56f05fb19 fix: question, hiddenfield, variable delete error if used in logic 2024-08-28 16:35:05 +05:30
Piyush Gupta
c32ced20f1 fix: adds variable in response schema and updated config to store variables on each response 2024-08-27 19:57:40 +05:30
Piyush Gupta
387590986a fix: removes userAttributes based logic chaining 2024-08-27 16:41:27 +05:30
Piyush Gupta
4a5c0b1409 feat: updates survey template 2024-08-27 15:26:08 +05:30
Piyush Gupta
275731e381 replaces logic with advancedLogic 2024-08-27 06:30:09 +05:30
Piyush Gupta
71c3ac0e4e component render fix based on new schema 2024-08-25 23:16:19 +05:30
Piyush Gupta
d876c495be new schema 2024-08-24 22:04:04 +05:30
Piyush Gupta
1ba885e5dc adds local schema updation handlers 2024-08-23 15:43:34 +05:30
Piyush Gupta
245972234e Merge branch 'main' of https://github.com/formbricks/formbricks into feat-advanced-logic-editor 2024-08-22 15:37:07 +05:30
Piyush Gupta
f743709908 adds inputCombobox in actions 2024-08-22 15:14:14 +05:30
Piyush Gupta
79603293a0 adds inputCombobox storybook 2024-08-21 21:54:50 +05:30
Piyush Gupta
29e0cf96d4 adds condition handling, added logicInput component 2024-08-21 21:39:35 +05:30
Piyush Gupta
4eea6a11c8 feat: added types for advanced logic editor 2024-08-19 13:16:40 +05:30
626 changed files with 4355 additions and 5371 deletions

View File

@@ -1,7 +1,7 @@
name: oss.gg hack submission 🕹️
description: "Submit your contribution for the for the oss.gg hackathon"
title: "[🕹️]"
labels: 🕹️ oss.gg, player submission, hacktoberfest
title: "[oss.gg hackathon]"
labels: 🕹️ oss.gg, player submission
assignees: []
body:
- type: textarea

View File

@@ -51,6 +51,7 @@ jobs:
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
echo "E2E_TESTING=1" >> .env
echo "NEXT_PUBLIC_E2E_TESTING=1" >> .env
shell: bash
- name: Build App

View File

@@ -20,20 +20,6 @@ jobs:
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
fix
feat
chore
docs
style
refactor
perf
test
build
ci
revert
ossgg
- uses: marocchino/sticky-pull-request-comment@v2
# When the previous steps fails, the workflow would stop. By adding this

View File

@@ -13,8 +13,8 @@
"dependencies": {
"@formbricks/js": "workspace:*",
"@formbricks/ui": "workspace:*",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"lucide-react": "^0.418.0",
"next": "14.2.5",
"react": "18.3.1",
"react-dom": "18.3.1"
},

View File

@@ -1,6 +1,6 @@
import type { AppProps } from "next/app";
import Head from "next/head";
import "@formbricks/ui/globals.css";
import "../styles/globals.css";
const App = ({ Component, pageProps }: AppProps) => {
return (

View File

@@ -0,0 +1,26 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Example on overriding packages/js colors */
.dark {
--fb-brand-color: red;
--fb-brand-text-color: white;
--fb-border-color: green;
--fb-border-color-highlight: var(--slate-500);
--fb-focus-color: red;
--fb-heading-color: yellow;
--fb-subheading-color: green;
--fb-info-text-color: orange;
--fb-signature-text-color: blue;
--fb-survey-background-color: black;
--fb-accent-background-color: rgb(13, 13, 12);
--fb-accent-background-color-selected: red;
--fb-placeholder-color: white;
--fb-shadow-color: yellow;
--fb-rating-fill: var(--yellow-300);
--fb-rating-hover: var(--yellow-500);
--fb-back-btn-border: currentColor;
--fb-submit-btn-border: transparent;
--fb-rating-selected: black;
}

View File

@@ -129,8 +129,10 @@ Locate that file. We are using the [Tailwind Template “Syntax”](https://tail
```tsx
import { useRouter } from "next/router";
import { useState } from "react";
import { Button } from "@formbricks/ui/components/Button";
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/components/Popover";
import { Button } from "@formbricks/ui/Button";
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/Popover";
import { handleFeedbackSubmit, updateFeedback } from "../../lib/handleFeedbackSubmit";
export const DocsFeedback = () => {

View File

@@ -1,9 +1,4 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@formbricks/ui/components/Accordion";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@formbricks/ui/Accordion";
import { FaqJsonLdComponent } from "./FAQPageJsonLd";
const FAQ_DATA = [

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,75 +0,0 @@
import { MdxImage } from "@/components/MdxImage";
import StepOne from "./images/1-publish-to-web.webp";
import StepTwo from "./images/2-warning-publish.webp";
import StepThree from "./images/3-share-link.webp";
export const metadata = {
title: "Shareable Dashboards",
description:
"Create shareable links to dashboards of specific surveys.",
};
# Shareable Dashboards
Formbricks allows you to create public, shareable versions of your survey results dashboards. This feature enables you to easily share survey results with stakeholders, team members, or the public without granting access to your Formbricks account.
## How To Publish Survey Results
1. **Go to survey summary**: Choose the survey for which you want to create a shareable dashboard and go to its summary page.
2. **Share results**: Click the "Share results" and then "Publish to web".
<MdxImage
src={StepOne}
alt="Go to survey summary"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. **Confirm**: Click "Publish to public web" (it's public).
<MdxImage
src={StepTwo}
alt="Go to survey summary"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
4. **Share link**: Formbricks has generated a unique URL for your public dashboard. Share it around.
<MdxImage
src={StepThree}
alt="Go to survey summary"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<Note>Whoever has access to the link can access the survey results.</Note>
## How To Unpublish Survey Results
Unpublish is very simple: Go to "Share results" -> "Unpublish from web" -> "Unpublish".
<MdxImage
src={StepThree}
alt="Go to survey summary"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## Key Features
- **Read-only access**: Viewers can see survey results but cannot modify data or settings.
- **Real-time updates**: The shared dashboard reflects current survey data in real-time.
- **Filters included**: Visitors can access all filters to dissect the data.
- **Revocable access**: You can disable the shared link at any time to restrict access.
## Use Cases
- Share results with clients or stakeholders
- Publish survey findings to your website or blog
- Collaborate with team members without sharing account credentials
- Create transparency by making certain survey results public
Shareable dashboards provide a simple yet powerful way to disseminate survey insights while maintaining control over your Formbricks account and data.

View File

@@ -38,7 +38,7 @@ These variables are present inside your machines docker-compose file. Restart
| S3_SECRET_KEY | Secret key for S3. | optional | (resolved by the AWS SDK) |
| S3_REGION | Region for S3. | optional | (resolved by the AWS SDK) |
| S3_BUCKET_NAME | S3 bucket name for data storage. Formbricks enables S3 storage when this is set. | optional (required if S3 is enabled) | |
| S3_ENDPOINT_URL | Endpoint for S3. | optional | (resolved by the AWS SDK) |
| S3_ENDPOINT | Endpoint for S3. | optional | (resolved by the AWS SDK) |
| PRIVACY_URL | URL for privacy policy. | optional | |
| TERMS_URL | URL for terms of service. | optional | |
| IMPRINT_URL | URL for imprint. | optional | |

View File

@@ -1,17 +1,15 @@
// ResponsiveVideo.tsx
// ResponsiveVideo.js
export const ResponsiveVideo = ({ src, title }) => {
return (
<div className="max-w-[1280px]">
<div className="relative w-full overflow-hidden pt-[56.25%]">
<iframe
src={src}
title={title}
frameBorder="0"
className="absolute left-0 top-0 h-full w-full"
referrerPolicy="strict-origin-when-cross-origin"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen></iframe>
</div>
<div className="relative w-full overflow-hidden pt-[56.25%]">
<iframe
src={src}
title={title}
frameBorder="0"
className="absolute left-0 top-0 h-full w-full"
referrerPolicy="strict-origin-when-cross-origin"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen></iframe>
</div>
);
};

View File

@@ -44,7 +44,6 @@ export const navigation: Array<NavGroup> = [
{ title: "Limit submissions", href: "/global/limit-submissions" }, // global
{ title: "Recall Functionality", href: "/global/recall" }, // global
{ title: "Partial Submissions", href: "/global/partial-submissions" }, // global
{ title: "Shareable Dashboards", href: "/global/shareable-dashboards" },
],
},
],
@@ -69,7 +68,6 @@ export const navigation: Array<NavGroup> = [
{ title: "Limit submissions", href: "/global/limit-submissions" }, // global
{ title: "Recall Functionality", href: "/global/recall" }, // global
{ title: "Partial Submissions", href: "/global/partial-submissions" }, // global
{ title: "Shareable Dashboards", href: "/global/shareable-dashboards" },
],
},
],
@@ -93,7 +91,6 @@ export const navigation: Array<NavGroup> = [
{ title: "User Metadata", href: "/global/metadata" },
{ title: "Custom Styling", href: "/global/overwrite-styling" }, // global
{ title: "Conditional Logic", href: "/global/conditional-logic" },
{ title: "Shareable Dashboards", href: "/global/shareable-dashboards" },
{ title: "Start & End Dates", href: "/global/custom-start-end-conditions" },
{ title: "Limit submissions", href: "/global/limit-submissions" }, // global
{ title: "Recall Functionality", href: "/global/recall" },

View File

@@ -13,39 +13,39 @@
"browserslist": "defaults, not ie <= 11",
"dependencies": {
"@algolia/autocomplete-core": "^1.17.4",
"@calcom/embed-react": "^1.5.1",
"@calcom/embed-react": "^1.5.0",
"@docsearch/css": "3",
"@docsearch/react": "^3.6.1",
"@formbricks/lib": "workspace:*",
"@formbricks/types": "workspace:*",
"@formbricks/ui": "workspace:*",
"@headlessui/react": "^2.1.8",
"@headlessui/react": "^2.1.2",
"@headlessui/tailwindcss": "^0.2.1",
"@mapbox/rehype-prism": "^0.9.0",
"@mdx-js/loader": "^3.0.1",
"@mdx-js/react": "^3.0.1",
"@next/mdx": "14.2.13",
"@next/mdx": "14.2.5",
"@paralleldrive/cuid2": "^2.2.2",
"@sindresorhus/slugify": "^2.2.1",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/typography": "^0.5.13",
"acorn": "^8.12.1",
"autoprefixer": "^10.4.20",
"autoprefixer": "^10.4.19",
"clsx": "^2.1.1",
"fast-glob": "^3.3.2",
"flexsearch": "^0.7.43",
"framer-motion": "11.7.0",
"framer-motion": "11.3.20",
"lottie-web": "^5.12.2",
"lucide": "^0.446.0",
"lucide-react": "^0.446.0",
"lucide": "^0.418.0",
"lucide-react": "^0.418.0",
"mdast-util-to-string": "^4.0.0",
"mdx-annotations": "^0.1.4",
"next": "14.2.13",
"next-plausible": "^3.12.2",
"next-seo": "^6.6.0",
"next": "14.2.5",
"next-plausible": "^3.12.0",
"next-seo": "^6.5.0",
"next-sitemap": "^4.2.3",
"next-themes": "^0.3.0",
"node-fetch": "^3.3.2",
"prism-react-renderer": "^2.4.0",
"prism-react-renderer": "^2.3.1",
"prismjs": "^1.29.0",
"react": "18.3.1",
"react-dom": "18.3.1",
@@ -56,13 +56,13 @@
"remark-gfm": "^4.0.0",
"remark-mdx": "^3.0.1",
"schema-dts": "^1.1.2",
"sharp": "^0.33.5",
"sharp": "^0.33.4",
"shiki": "^0.14.7",
"simple-functional-loader": "^1.2.1",
"tailwindcss": "^3.4.13",
"tailwindcss": "^3.4.7",
"unist-util-filter": "^5.0.1",
"unist-util-visit": "^5.0.0",
"zustand": "^4.5.5"
"zustand": "^4.5.4"
},
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",

View File

@@ -12,30 +12,30 @@
},
"dependencies": {
"@formbricks/ui": "workspace:*",
"eslint-plugin-react-refresh": "^0.4.12",
"eslint-plugin-react-refresh": "^0.4.9",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@chromatic-com/storybook": "^2.0.2",
"@chromatic-com/storybook": "^1.6.1",
"@formbricks/config-typescript": "workspace:*",
"@storybook/addon-a11y": "^8.3.3",
"@storybook/addon-essentials": "^8.3.3",
"@storybook/addon-interactions": "^8.3.3",
"@storybook/addon-links": "^8.3.3",
"@storybook/addon-onboarding": "^8.3.3",
"@storybook/blocks": "^8.3.3",
"@storybook/react": "^8.3.3",
"@storybook/react-vite": "^8.3.3",
"@storybook/test": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"@storybook/addon-a11y": "^8.2.9",
"@storybook/addon-essentials": "^8.2.9",
"@storybook/addon-interactions": "^8.2.9",
"@storybook/addon-links": "^8.2.9",
"@storybook/addon-onboarding": "^8.2.9",
"@storybook/blocks": "^8.2.9",
"@storybook/react": "^8.2.9",
"@storybook/react-vite": "^8.2.9",
"@storybook/test": "^8.2.9",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitejs/plugin-react": "^4.3.1",
"esbuild": "^0.24.0",
"eslint-plugin-storybook": "^0.9.0",
"esbuild": "^0.23.0",
"eslint-plugin-storybook": "^0.8.0",
"prop-types": "^15.8.1",
"storybook": "^8.3.3",
"tsup": "^8.3.0",
"vite": "^5.4.8"
"storybook": "^8.2.9",
"tsup": "^8.2.4",
"vite": "^5.4.1"
}
}

View File

@@ -1,5 +1,5 @@
/** @type {import('tailwindcss').Config} */
import base from "../../packages/ui/tailwind.config";
import base from "../../packages/config-tailwind/tailwind.config";
export default {
...base,

View File

@@ -9,7 +9,7 @@ import { useEffect } from "react";
import { cn } from "@formbricks/lib/cn";
import { TEnvironment } from "@formbricks/types/environment";
import { TProductConfigChannel } from "@formbricks/types/product";
import { Button } from "@formbricks/ui/components/Button";
import { Button } from "@formbricks/ui/Button";
import { OnboardingSetupInstructions } from "./OnboardingSetupInstructions";
interface ConnectWithFormbricksProps {

View File

@@ -7,9 +7,9 @@ import { FormProvider, useForm } from "react-hook-form";
import { toast } from "react-hot-toast";
import { z } from "zod";
import { TOrganization } from "@formbricks/types/organizations";
import { Button } from "@formbricks/ui/components/Button";
import { FormControl, FormError, FormField, FormItem, FormLabel } from "@formbricks/ui/components/Form";
import { Input } from "@formbricks/ui/components/Input";
import { Button } from "@formbricks/ui/Button";
import { FormControl, FormError, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
import { Input } from "@formbricks/ui/Input";
interface InviteOrganizationMemberProps {
organization: TOrganization;

View File

@@ -4,10 +4,10 @@ import "prismjs/themes/prism.css";
import { useState } from "react";
import toast from "react-hot-toast";
import { TProductConfigChannel } from "@formbricks/types/product";
import { Button } from "@formbricks/ui/components/Button";
import { CodeBlock } from "@formbricks/ui/components/CodeBlock";
import { TabBar } from "@formbricks/ui/components/TabBar";
import { Html5Icon, NpmIcon } from "@formbricks/ui/components/icons";
import { Button } from "@formbricks/ui/Button";
import { CodeBlock } from "@formbricks/ui/CodeBlock";
import { TabBar } from "@formbricks/ui/TabBar";
import { Html5Icon, NpmIcon } from "@formbricks/ui/icons";
const tabs = [
{ id: "html", label: "HTML", icon: <Html5Icon /> },

View File

@@ -5,8 +5,8 @@ import { notFound, redirect } from "next/navigation";
import { authOptions } from "@formbricks/lib/authOptions";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface InvitePageProps {
params: {

View File

@@ -1,10 +1,11 @@
import { ConnectWithFormbricks } from "@/app/(app)/(onboarding)/environments/[environmentId]/connect/components/ConnectWithFormbricks";
import { getCustomHeadline } from "@/app/(app)/(onboarding)/lib/utils";
import { XIcon } from "lucide-react";
import { WEBAPP_URL } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface ConnectPageProps {
params: {
@@ -25,11 +26,14 @@ const Page = async ({ params }: ConnectPageProps) => {
}
const channel = product.config.channel || null;
const industry = product.config.industry || null;
const customHeadline = getCustomHeadline(channel, industry);
return (
<div className="flex min-h-full flex-col items-center justify-center py-10">
<Header
title={`Let's connect your product with Formbricks`}
title={`Let's connect your ${customHeadline} with Formbricks`}
subtitle="It takes less than 4 minutes, pinky promise!"
/>
<div className="space-y-4 text-center">

View File

@@ -12,7 +12,7 @@ import { TProduct } from "@formbricks/types/product";
import { TSurveyCreateInput } from "@formbricks/types/surveys/types";
import { TXMTemplate } from "@formbricks/types/templates";
import { TUser } from "@formbricks/types/user";
import { createSurveyAction } from "@formbricks/ui/components/TemplateList/actions";
import { createSurveyAction } from "@formbricks/ui/TemplateList/actions";
interface XMTemplateListProps {
product: TProduct;

View File

@@ -6,8 +6,8 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils";
import { getProductByEnvironmentId, getProducts } from "@formbricks/lib/product/service";
import { getUser } from "@formbricks/lib/user/service";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface XMTemplatePageProps {
params: {

View File

@@ -1,12 +1,13 @@
import { TProductConfigChannel } from "@formbricks/types/product";
import { TProductConfigChannel, TProductConfigIndustry } from "@formbricks/types/product";
export const getCustomHeadline = (channel?: TProductConfigChannel) => {
switch (channel) {
case "website":
return "Let's get the most out of your website traffic!";
case "app":
return "Let's research what your users need!";
default:
return "You maintain a product, how exciting!";
}
export const getCustomHeadline = (channel?: TProductConfigChannel, industry?: TProductConfigIndustry) => {
const combinations = {
"website+eCommerce": "web shop",
"website+saas": "landing page",
"website+other": "website",
"app+eCommerce": "shopping app",
"app+saas": "SaaS app",
"app+other": "app",
};
return combinations[`${channel}+${industry}`] || "product";
};

View File

@@ -7,7 +7,7 @@ import { canUserAccessOrganization } from "@formbricks/lib/organization/auth";
import { getOrganization } from "@formbricks/lib/organization/service";
import { getUser } from "@formbricks/lib/user/service";
import { AuthorizationError } from "@formbricks/types/errors";
import { ToasterClient } from "@formbricks/ui/components/ToasterClient";
import { ToasterClient } from "@formbricks/ui/ToasterClient";
const ProductOnboardingLayout = async ({ children, params }) => {
const session = await getServerSession(authOptions);

View File

@@ -1,8 +1,8 @@
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
import { GlobeIcon, GlobeLockIcon, LinkIcon, XIcon } from "lucide-react";
import { getProducts } from "@formbricks/lib/product/service";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface ChannelPageProps {
params: {

View File

@@ -3,8 +3,8 @@ import { HeartIcon, MonitorIcon, ShoppingCart, XIcon } from "lucide-react";
import { notFound } from "next/navigation";
import { getProducts } from "@formbricks/lib/product/service";
import { TProductConfigChannel } from "@formbricks/types/product";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface IndustryPageProps {
params: {

View File

@@ -1,8 +1,8 @@
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
import { HeartIcon, ListTodoIcon, XIcon } from "lucide-react";
import { getProducts } from "@formbricks/lib/product/service";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface ModePageProps {
params: {

View File

@@ -1,5 +1,6 @@
"use client";
import { getCustomHeadline } from "@/app/(app)/(onboarding)/lib/utils";
import { createProductAction } from "@/app/(app)/environments/[environmentId]/actions";
import { zodResolver } from "@hookform/resolvers/zod";
import Image from "next/image";
@@ -7,7 +8,7 @@ import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { toast } from "react-hot-toast";
import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { FORMBRICKS_SURVEYS_FILTERS_KEY_LS } from "@formbricks/lib/localStorage";
import { FORMBRICKS_PRODUCT_ID_LS, FORMBRICKS_SURVEYS_FILTERS_KEY_LS } from "@formbricks/lib/localStorage";
import { PREVIEW_SURVEY } from "@formbricks/lib/styling/constants";
import {
TProductConfigChannel,
@@ -16,8 +17,8 @@ import {
TProductUpdateInput,
ZProductUpdateInput,
} from "@formbricks/types/product";
import { Button } from "@formbricks/ui/components/Button";
import { ColorPicker } from "@formbricks/ui/components/ColorPicker";
import { Button } from "@formbricks/ui/Button";
import { ColorPicker } from "@formbricks/ui/ColorPicker";
import {
FormControl,
FormDescription,
@@ -26,9 +27,9 @@ import {
FormItem,
FormLabel,
FormProvider,
} from "@formbricks/ui/components/Form";
import { Input } from "@formbricks/ui/components/Input";
import { SurveyInline } from "@formbricks/ui/components/Survey";
} from "@formbricks/ui/Form";
import { Input } from "@formbricks/ui/Input";
import { SurveyInline } from "@formbricks/ui/Survey";
interface ProductSettingsProps {
organizationId: string;
@@ -64,6 +65,8 @@ export const ProductSettings = ({
);
if (productionEnvironment) {
if (typeof window !== "undefined") {
localStorage.setItem(FORMBRICKS_PRODUCT_ID_LS, productionEnvironment.productId);
// Rmove filters when creating a new product
localStorage.removeItem(FORMBRICKS_SURVEYS_FILTERS_KEY_LS);
}
@@ -130,7 +133,9 @@ export const ProductSettings = ({
<FormItem className="w-full space-y-4">
<div>
<FormLabel>Product name</FormLabel>
<FormDescription>What is your product called?</FormDescription>
<FormDescription>
What is your {getCustomHeadline(channel, industry)} called?
</FormDescription>
</div>
<FormControl>
<div>

View File

@@ -3,9 +3,10 @@ import { ProductSettings } from "@/app/(app)/(onboarding)/organizations/[organiz
import { XIcon } from "lucide-react";
import { DEFAULT_BRAND_COLOR } from "@formbricks/lib/constants";
import { getProducts } from "@formbricks/lib/product/service";
import { startsWithVowel } from "@formbricks/lib/utils/strings";
import { TProductConfigChannel, TProductConfigIndustry, TProductMode } from "@formbricks/types/product";
import { Button } from "@formbricks/ui/components/Button";
import { Header } from "@formbricks/ui/components/Header";
import { Button } from "@formbricks/ui/Button";
import { Header } from "@formbricks/ui/Header";
interface ProductSettingsPageProps {
params: {
@@ -23,7 +24,7 @@ const Page = async ({ params, searchParams }: ProductSettingsPageProps) => {
const industry = searchParams.industry || null;
const mode = searchParams.mode || "surveys";
const customHeadline = getCustomHeadline(channel);
const customHeadline = getCustomHeadline(channel, industry);
const products = await getProducts(params.organizationId);
return (
@@ -35,7 +36,7 @@ const Page = async ({ params, searchParams }: ProductSettingsPageProps) => {
/>
) : (
<Header
title={customHeadline}
title={`You maintain ${startsWithVowel(customHeadline) ? "an " + customHeadline : "a " + customHeadline}, how exciting!`}
subtitle="Get 2x more responses matching surveys with your brand and UI"
/>
)}

View File

@@ -2,7 +2,7 @@ import { LucideProps } from "lucide-react";
import Link from "next/link";
import { ForwardRefExoticComponent, RefAttributes } from "react";
import { cn } from "@formbricks/lib/cn";
import { OptionCard } from "@formbricks/ui/components/OptionCard";
import { OptionCard } from "@formbricks/ui/OptionCard";
interface OnboardingOptionsContainerProps {
options: {

View File

@@ -9,8 +9,8 @@ import { getEnvironment } from "@formbricks/lib/environment/service";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getUser } from "@formbricks/lib/user/service";
import { AuthorizationError } from "@formbricks/types/errors";
import { DevEnvironmentBanner } from "@formbricks/ui/components/DevEnvironmentBanner";
import { ToasterClient } from "@formbricks/ui/components/ToasterClient";
import { DevEnvironmentBanner } from "@formbricks/ui/DevEnvironmentBanner";
import { ToasterClient } from "@formbricks/ui/ToasterClient";
const EnvLayout = async ({ children, params }) => {
const session = await getServerSession(authOptions);

View File

@@ -2,7 +2,7 @@
import { TActionClass } from "@formbricks/types/action-classes";
import { TSurvey } from "@formbricks/types/surveys/types";
import { ModalWithTabs } from "@formbricks/ui/components/ModalWithTabs";
import { ModalWithTabs } from "@formbricks/ui/ModalWithTabs";
import { CreateNewActionTab } from "./CreateNewActionTab";
import { SavedActionsTab } from "./SavedActionsTab";

View File

@@ -4,9 +4,9 @@ import { PlusIcon } from "lucide-react";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyAddressQuestion } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/Button";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
interface AddressQuestionFormProps {
localSurvey: TSurvey;

View File

@@ -6,9 +6,9 @@ import { UseFormReturn } from "react-hook-form";
import { cn } from "@formbricks/lib/cn";
import { TProductStyling } from "@formbricks/types/product";
import { TSurveyStyling } from "@formbricks/types/surveys/types";
import { Badge } from "@formbricks/ui/components/Badge";
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/components/Form";
import { Slider } from "@formbricks/ui/components/Slider";
import { Badge } from "@formbricks/ui/Badge";
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
import { Slider } from "@formbricks/ui/Slider";
import { SurveyBgSelectorTab } from "./SurveyBgSelectorTab";
interface BackgroundStylingCardProps {

View File

@@ -4,10 +4,10 @@ import { useState } from "react";
import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyCTAQuestion } from "@formbricks/types/surveys/types";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { OptionsSwitch } from "@formbricks/ui/OptionsSwitch";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
const options = [
{

View File

@@ -3,11 +3,11 @@ import { useEffect, useState } from "react";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyCalQuestion } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
interface CalQuestionFormProps {
localSurvey: TSurvey;

View File

@@ -8,12 +8,12 @@ import { cn } from "@formbricks/lib/cn";
import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
import { TProduct, TProductStyling } from "@formbricks/types/product";
import { TSurveyStyling, TSurveyType } from "@formbricks/types/surveys/types";
import { Badge } from "@formbricks/ui/components/Badge";
import { CardArrangementTabs } from "@formbricks/ui/components/CardArrangementTabs";
import { ColorPicker } from "@formbricks/ui/components/ColorPicker";
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/components/Form";
import { Slider } from "@formbricks/ui/components/Slider";
import { Switch } from "@formbricks/ui/components/Switch";
import { Badge } from "@formbricks/ui/Badge";
import { CardArrangementTabs } from "@formbricks/ui/CardArrangementTabs";
import { ColorPicker } from "@formbricks/ui/ColorPicker";
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
import { Slider } from "@formbricks/ui/Slider";
import { Switch } from "@formbricks/ui/Switch";
type CardStylingSettingsProps = {
open: boolean;

View File

@@ -1,5 +1,5 @@
import { useState } from "react";
import { ColorPicker } from "@formbricks/ui/components/ColorPicker";
import { ColorPicker } from "@formbricks/ui/ColorPicker";
interface ColorSurveyBgProps {
handleBgChange: (bg: string, bgType: string) => void;

View File

@@ -4,6 +4,7 @@ import {
replaceEndingCardHeadlineRecall,
} from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
import { createId } from "@paralleldrive/cuid2";
import { debounce } from "lodash";
import {
ArrowDownIcon,
ArrowUpIcon,
@@ -13,19 +14,19 @@ import {
SplitIcon,
TrashIcon,
} from "lucide-react";
import { useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { duplicateLogicItem } from "@formbricks/lib/surveyLogic/utils";
import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyLogic, TSurveyQuestion } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Button } from "@formbricks/ui/Button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@formbricks/ui/components/DropdownMenu";
import { Label } from "@formbricks/ui/components/Label";
} from "@formbricks/ui/DropdownMenu";
import { Label } from "@formbricks/ui/Label";
interface ConditionalLogicProps {
localSurvey: TSurvey;
@@ -42,6 +43,16 @@ export function ConditionalLogic({
questionIdx,
updateQuestion,
}: ConditionalLogicProps) {
const [questionLogic, setQuestionLogic] = useState(question.logic);
const debouncedUpdateQuestion = useMemo(() => debounce(updateQuestion, 500), [updateQuestion]);
useEffect(() => {
debouncedUpdateQuestion(questionIdx, {
logic: questionLogic,
});
}, [questionLogic]);
const transformedSurvey = useMemo(() => {
let modifiedSurvey = replaceHeadlineRecall(localSurvey, "default", attributeClasses);
modifiedSurvey = replaceEndingCardHeadlineRecall(modifiedSurvey, "default", attributeClasses);
@@ -49,6 +60,10 @@ export function ConditionalLogic({
return modifiedSurvey;
}, [localSurvey, attributeClasses]);
const updateQuestionLogic = (_questionIdx: number, updatedAttributes: any) => {
setQuestionLogic(updatedAttributes.logic);
};
const addLogic = () => {
const operator = getDefaultOperatorForQuestion(question);
@@ -77,37 +92,37 @@ export function ConditionalLogic({
],
};
updateQuestion(questionIdx, {
logic: [...(question?.logic ?? []), initialCondition],
updateQuestionLogic(questionIdx, {
logic: [...(questionLogic ?? []), initialCondition],
});
};
const handleRemoveLogic = (logicItemIdx: number) => {
const logicCopy = structuredClone(question.logic ?? []);
const logicCopy = structuredClone(questionLogic ?? []);
logicCopy.splice(logicItemIdx, 1);
updateQuestion(questionIdx, {
updateQuestionLogic(questionIdx, {
logic: logicCopy,
});
};
const moveLogic = (from: number, to: number) => {
const logicCopy = structuredClone(question.logic ?? []);
const logicCopy = structuredClone(questionLogic ?? []);
const [movedItem] = logicCopy.splice(from, 1);
logicCopy.splice(to, 0, movedItem);
updateQuestion(questionIdx, {
updateQuestionLogic(questionIdx, {
logic: logicCopy,
});
};
const duplicateLogic = (logicItemIdx: number) => {
const logicCopy = structuredClone(question.logic ?? []);
const logicCopy = structuredClone(questionLogic ?? []);
const logicItem = logicCopy[logicItemIdx];
const newLogicItem = duplicateLogicItem(logicItem);
logicCopy.splice(logicItemIdx + 1, 0, newLogicItem);
updateQuestion(questionIdx, {
updateQuestionLogic(questionIdx, {
logic: logicCopy,
});
};
@@ -119,20 +134,21 @@ export function ConditionalLogic({
<SplitIcon className="h-4 w-4 rotate-90" />
</Label>
{question.logic && question.logic.length > 0 && (
{questionLogic && questionLogic.length > 0 && (
<div className="mt-2 flex flex-col gap-4">
{question.logic.map((logicItem, logicItemIdx) => (
{questionLogic.map((logicItem, logicItemIdx) => (
<div
key={logicItem.id}
className="flex w-full grow items-start gap-2 rounded-lg border border-slate-200 bg-slate-50 p-4">
<LogicEditor
localSurvey={transformedSurvey}
logicItem={logicItem}
updateQuestion={updateQuestion}
updateQuestion={updateQuestionLogic}
question={question}
questionLogic={questionLogic}
questionIdx={questionIdx}
logicIdx={logicItemIdx}
isLast={logicItemIdx === (question.logic ?? []).length - 1}
isLast={logicItemIdx === (questionLogic ?? []).length - 1}
/>
<DropdownMenu>
@@ -159,7 +175,7 @@ export function ConditionalLogic({
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={logicItemIdx === (question.logic ?? []).length - 1}
disabled={logicItemIdx === (questionLogic ?? []).length - 1}
onClick={() => {
moveLogic(logicItemIdx, logicItemIdx + 1);
}}>

View File

@@ -4,8 +4,8 @@ import { useState } from "react";
import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyConsentQuestion } from "@formbricks/types/surveys/types";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
interface ConsentQuestionFormProps {
localSurvey: TSurvey;

View File

@@ -11,13 +11,13 @@ import {
ZActionClassInput,
} from "@formbricks/types/action-classes";
import { TSurvey } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { FormControl, FormError, FormField, FormItem, FormLabel } from "@formbricks/ui/components/Form";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { TabToggle } from "@formbricks/ui/components/TabToggle";
import { CodeActionForm } from "@formbricks/ui/components/organisms/CodeActionForm";
import { NoCodeActionForm } from "@formbricks/ui/components/organisms/NoCodeActionForm";
import { Button } from "@formbricks/ui/Button";
import { FormControl, FormError, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { TabToggle } from "@formbricks/ui/TabToggle";
import { CodeActionForm } from "@formbricks/ui/organisms/CodeActionForm";
import { NoCodeActionForm } from "@formbricks/ui/organisms/NoCodeActionForm";
import { createActionClassAction } from "../actions";
interface CreateNewActionTabProps {

View File

@@ -2,10 +2,10 @@ import { PlusIcon } from "lucide-react";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyDateQuestion } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Button } from "@formbricks/ui/Button";
import { Label } from "@formbricks/ui/Label";
import { OptionsSwitch } from "@formbricks/ui/OptionsSwitch";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
interface IDateQuestionFormProps {
localSurvey: TSurvey;

View File

@@ -14,8 +14,8 @@ import { recallToHeadline } from "@formbricks/lib/utils/recall";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TOrganizationBillingPlan } from "@formbricks/types/organizations";
import { TSurvey, TSurveyEndScreenCard, TSurveyRedirectUrlCard } from "@formbricks/types/surveys/types";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
import { TooltipRenderer } from "@formbricks/ui/components/Tooltip";
import { OptionsSwitch } from "@formbricks/ui/OptionsSwitch";
import { TooltipRenderer } from "@formbricks/ui/Tooltip";
interface EditEndingCardProps {
localSurvey: TSurvey;

View File

@@ -8,10 +8,10 @@ import { LocalizedEditor } from "@formbricks/ee/multi-language/components/locali
import { cn } from "@formbricks/lib/cn";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyWelcomeCard } from "@formbricks/types/surveys/types";
import { FileInput } from "@formbricks/ui/components/FileInput";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
import { FileInput } from "@formbricks/ui/FileInput";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Switch } from "@formbricks/ui/Switch";
interface EditWelcomeCardProps {
localSurvey: TSurvey;

View File

@@ -18,7 +18,7 @@ import {
TSurveyQuestionTypeEnum,
TSurveyRedirectUrlCard,
} from "@formbricks/types/surveys/types";
import { ConfirmationModal } from "@formbricks/ui/components/ConfirmationModal";
import { ConfirmationModal } from "@formbricks/ui/ConfirmationModal";
import {
DropdownMenu,
DropdownMenuContent,
@@ -27,7 +27,7 @@ import {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@formbricks/ui/components/DropdownMenu";
} from "@formbricks/ui/DropdownMenu";
interface EditorCardMenuProps {
survey: TSurvey;

View File

@@ -4,10 +4,10 @@ import { useState } from "react";
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyEndScreenCard } from "@formbricks/types/surveys/types";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Switch } from "@formbricks/ui/Switch";
interface EndScreenFormProps {
localSurvey: TSurvey;

View File

@@ -11,10 +11,10 @@ import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TAllowedFileExtension, ZAllowedFileExtension } from "@formbricks/types/common";
import { TProduct } from "@formbricks/types/product";
import { TSurvey, TSurveyFileUploadQuestion } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
interface FileUploadFormProps {
localSurvey: TSurvey;

View File

@@ -9,9 +9,9 @@ import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
import { mixColor } from "@formbricks/lib/utils/colors";
import { TProductStyling } from "@formbricks/types/product";
import { TSurveyStyling } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { ColorPicker } from "@formbricks/ui/components/ColorPicker";
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/components/Form";
import { Button } from "@formbricks/ui/Button";
import { ColorPicker } from "@formbricks/ui/ColorPicker";
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
type FormStylingSettingsProps = {
open: boolean;

View File

@@ -9,11 +9,11 @@ import { cn } from "@formbricks/lib/cn";
import { extractRecallInfo } from "@formbricks/lib/utils/recall";
import { TSurvey, TSurveyHiddenFields } from "@formbricks/types/surveys/types";
import { validateId } from "@formbricks/types/surveys/validation";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { Switch } from "@formbricks/ui/components/Switch";
import { Tag } from "@formbricks/ui/components/Tag";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { Switch } from "@formbricks/ui/Switch";
import { Tag } from "@formbricks/ui/Tag";
interface HiddenFieldsCardProps {
localSurvey: TSurvey;

View File

@@ -10,9 +10,9 @@ import { TEnvironment } from "@formbricks/types/environment";
import { TProduct } from "@formbricks/types/product";
import { TSegment } from "@formbricks/types/segment";
import { TSurvey, TSurveyType } from "@formbricks/types/surveys/types";
import { Badge } from "@formbricks/ui/components/Badge";
import { Label } from "@formbricks/ui/components/Label";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/components/RadioGroup";
import { Badge } from "@formbricks/ui/Badge";
import { Label } from "@formbricks/ui/Label";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/RadioGroup";
interface HowToSendCardProps {
localSurvey: TSurvey;

View File

@@ -1,4 +1,4 @@
import { FileInput } from "@formbricks/ui/components/FileInput";
import { FileInput } from "@formbricks/ui/FileInput";
interface UploadImageSurveyBgProps {
environmentId: string;

View File

@@ -8,6 +8,7 @@ interface LogicEditorProps {
logicItem: TSurveyLogic;
updateQuestion: (questionIdx: number, updatedAttributes: any) => void;
question: TSurveyQuestion;
questionLogic: TSurveyLogic[];
questionIdx: number;
logicIdx: number;
isLast: boolean;
@@ -18,6 +19,7 @@ export function LogicEditor({
logicItem,
updateQuestion,
question,
questionLogic,
questionIdx,
logicIdx,
isLast,
@@ -35,7 +37,7 @@ export function LogicEditor({
<LogicEditorActions
logicItem={logicItem}
logicIdx={logicIdx}
question={question}
questionLogic={questionLogic}
updateQuestion={updateQuestion}
localSurvey={localSurvey}
questionIdx={questionIdx}

View File

@@ -0,0 +1,390 @@
import {
actionObjectiveOptions,
getActionOperatorOptions,
} from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
import {
CopyIcon,
EllipsisVerticalIcon,
EyeOffIcon,
FileDigitIcon,
FileType2Icon,
PlusIcon,
TrashIcon,
} from "lucide-react";
import React, { useCallback, useMemo } from "react";
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
import { questionIconMapping } from "@formbricks/lib/utils/questions";
import {
TActionNumberVariableCalculateOperator,
TActionObjective,
TActionTextVariableCalculateOperator,
TActionVariableValueType,
TSurvey,
TSurveyLogicAction,
TSurveyQuestion,
TSurveyQuestionTypeEnum,
} from "@formbricks/types/surveys/types";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@formbricks/ui/DropdownMenu";
import { InputCombobox, TComboboxGroupedOption, TComboboxOption } from "@formbricks/ui/InputCombobox";
interface LogicEditorActionProps {
action: TSurveyLogicAction;
actionIdx: number;
handleObjectiveChange: (actionIdx: number, val: TActionObjective) => void;
handleValuesChange: (actionIdx: number, values: any) => void;
handleActionsChange: (operation: "remove" | "addBelow" | "duplicate", actionIdx: number) => void;
isRemoveDisabled: boolean;
questions: TSurveyQuestion[];
endings: TSurvey["endings"];
variables: TSurvey["variables"];
questionIdx: number;
hiddenFields: {
enabled: boolean;
fieldIds?: string[] | undefined;
};
}
const _LogicEditorAction = ({
action,
actionIdx,
handleActionsChange,
handleObjectiveChange,
handleValuesChange,
isRemoveDisabled,
questions,
endings,
variables,
questionIdx,
hiddenFields,
}: LogicEditorActionProps) => {
const actionTargetOptions = useMemo((): TComboboxOption[] => {
let filteredQuestions = questions.filter((_, idx) => idx !== questionIdx);
if (action.objective === "requireAnswer") {
filteredQuestions = filteredQuestions.filter((question) => !question.required);
}
const questionOptions = filteredQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
};
});
if (action.objective === "requireAnswer") return questionOptions;
const endingCardOptions = endings.map((ending) => {
return {
label:
ending.type === "endScreen"
? getLocalizedValue(ending.headline, "default") || "End Screen"
: ending.label || "Redirect Thank you card",
value: ending.id,
};
});
return [...questionOptions, ...endingCardOptions];
}, [action.objective, endings, questionIdx, questions]);
const actionVariableOptions = useMemo((): TComboboxOption[] => {
return variables.map((variable) => {
return {
icon: variable.type === "number" ? FileDigitIcon : FileType2Icon,
label: variable.name,
value: variable.id,
meta: {
variableType: variable.type,
},
};
});
}, [variables]);
const getActionValueOptions = useCallback(
(variableId: string): TComboboxGroupedOption[] => {
const hiddenFieldIds = hiddenFields?.fieldIds ?? [];
const hiddenFieldsOptions = hiddenFieldIds.map((field) => {
return {
icon: EyeOffIcon,
label: field,
value: field,
meta: {
type: "hiddenField",
},
};
});
const selectedVariable = variables.find((variable) => variable.id === variableId);
const filteredVariables = variables.filter((variable) => variable.id !== variableId);
if (!selectedVariable) return [];
if (selectedVariable.type === "text") {
const allowedQuestions = questions.filter((question) =>
[
TSurveyQuestionTypeEnum.OpenText,
TSurveyQuestionTypeEnum.MultipleChoiceSingle,
TSurveyQuestionTypeEnum.Rating,
TSurveyQuestionTypeEnum.NPS,
TSurveyQuestionTypeEnum.Date,
].includes(question.type)
);
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
type: "question",
},
};
});
const stringVariables = filteredVariables.filter((variable) => variable.type === "text");
const variableOptions = stringVariables.map((variable) => {
return {
icon: FileType2Icon,
label: variable.name,
value: variable.id,
meta: {
type: "variable",
},
};
});
const groupedOptions: TComboboxGroupedOption[] = [];
if (questionOptions.length > 0) {
groupedOptions.push({
label: "Questions",
value: "questions",
options: questionOptions,
});
}
if (variableOptions.length > 0) {
groupedOptions.push({
label: "Variables",
value: "variables",
options: variableOptions,
});
}
if (hiddenFieldsOptions.length > 0) {
groupedOptions.push({
label: "Hidden Fields",
value: "hiddenFields",
options: hiddenFieldsOptions,
});
}
return groupedOptions;
} else if (selectedVariable.type === "number") {
const allowedQuestions = questions.filter((question) =>
[TSurveyQuestionTypeEnum.Rating, TSurveyQuestionTypeEnum.NPS].includes(question.type)
);
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
type: "question",
},
};
});
const numberVariables = filteredVariables.filter((variable) => variable.type === "number");
const variableOptions = numberVariables.map((variable) => {
return {
icon: FileDigitIcon,
label: variable.name,
value: variable.id,
meta: {
type: "variable",
},
};
});
const groupedOptions: TComboboxGroupedOption[] = [];
if (questionOptions.length > 0) {
groupedOptions.push({
label: "Questions",
value: "questions",
options: questionOptions,
});
}
if (variableOptions.length > 0) {
groupedOptions.push({
label: "Variables",
value: "variables",
options: variableOptions,
});
}
if (hiddenFieldsOptions.length > 0) {
groupedOptions.push({
label: "Hidden Fields",
value: "hiddenFields",
options: hiddenFieldsOptions,
});
}
return groupedOptions;
}
return [];
},
[hiddenFields?.fieldIds, questions, variables]
);
return (
<div key={action.id} className="flex grow items-center justify-between gap-x-2">
<div className="block w-9 shrink-0">{actionIdx === 0 ? "Then" : "and"}</div>
<div className="flex grow items-center gap-x-2">
<InputCombobox
id={`action-${actionIdx}-objective`}
key={`objective-${action.id}`}
showSearch={false}
options={actionObjectiveOptions}
value={action.objective}
onChangeValue={(val: TActionObjective) => {
handleObjectiveChange(actionIdx, val);
}}
comboboxClasses="grow"
/>
{action.objective !== "calculate" && (
<InputCombobox
id={`action-${actionIdx}-target`}
key={`target-${action.id}`}
showSearch={false}
options={actionTargetOptions}
value={action.target}
onChangeValue={(val: string) => {
handleValuesChange(actionIdx, {
target: val,
});
}}
comboboxClasses="grow"
/>
)}
{action.objective === "calculate" && (
<>
<InputCombobox
id={`action-${actionIdx}-variableId`}
key={`variableId-${action.id}`}
showSearch={false}
options={actionVariableOptions}
value={action.variableId}
onChangeValue={(val: string) => {
handleValuesChange(actionIdx, {
variableId: val,
value: {
type: "static",
value: "",
},
});
}}
comboboxClasses="grow"
emptyDropdownText="Add a variable to calculate"
/>
<InputCombobox
id={`action-${actionIdx}-operator`}
key={`operator-${action.id}`}
showSearch={false}
options={getActionOperatorOptions(variables.find((v) => v.id === action.variableId)?.type)}
value={action.operator}
onChangeValue={(
val: TActionTextVariableCalculateOperator | TActionNumberVariableCalculateOperator
) => {
handleValuesChange(actionIdx, {
operator: val,
});
}}
comboboxClasses="grow"
/>
<InputCombobox
id={`action-${actionIdx}-value`}
key={`value-${action.id}`}
withInput={true}
clearable={true}
value={action.value?.value ?? ""}
inputProps={{
placeholder: "Value",
type: variables.find((v) => v.id === action.variableId)?.type || "text",
}}
groupedOptions={getActionValueOptions(action.variableId)}
onChangeValue={(val, option, fromInput) => {
const fieldType = option?.meta?.type as TActionVariableValueType;
if (!fromInput && fieldType !== "static") {
handleValuesChange(actionIdx, {
value: {
type: fieldType,
value: val as string,
},
});
} else if (fromInput) {
handleValuesChange(actionIdx, {
value: {
type: "static",
value: val as string,
},
});
}
}}
comboboxClasses="grow shrink-0"
/>
</>
)}
</div>
<DropdownMenu>
<DropdownMenuTrigger id={`actions-${actionIdx}-dropdown`}>
<EllipsisVerticalIcon className="h-4 w-4 text-slate-700 hover:text-slate-950" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleActionsChange("addBelow", actionIdx);
}}>
<PlusIcon className="h-4 w-4" />
Add action below
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={isRemoveDisabled}
onClick={() => {
handleActionsChange("remove", actionIdx);
}}>
<TrashIcon className="h-4 w-4" />
Remove
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleActionsChange("duplicate", actionIdx);
}}>
<CopyIcon className="h-4 w-4" />
Duplicate
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};
export const LogicEditorAction = React.memo(_LogicEditorAction);

View File

@@ -1,236 +1,110 @@
import {
actionObjectiveOptions,
getActionOperatorOptions,
getActionTargetOptions,
getActionValueOptions,
getActionVariableOptions,
} from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
import { LogicEditorAction } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/LogicEditorAction";
import { createId } from "@paralleldrive/cuid2";
import { CopyIcon, CornerDownRightIcon, EllipsisVerticalIcon, PlusIcon, TrashIcon } from "lucide-react";
import { CornerDownRightIcon } from "lucide-react";
import React, { useCallback, useMemo } from "react";
import { getUpdatedActionBody } from "@formbricks/lib/surveyLogic/utils";
import {
TActionNumberVariableCalculateOperator,
TActionObjective,
TActionTextVariableCalculateOperator,
TActionVariableValueType,
TSurvey,
TSurveyLogic,
TSurveyLogicAction,
TSurveyQuestion,
} from "@formbricks/types/surveys/types";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@formbricks/ui/components/DropdownMenu";
import { InputCombobox } from "@formbricks/ui/components/InputCombobox";
import { TActionObjective, TSurvey, TSurveyLogic, TSurveyLogicAction } from "@formbricks/types/surveys/types";
interface LogicEditorActions {
localSurvey: TSurvey;
logicItem: TSurveyLogic;
logicIdx: number;
question: TSurveyQuestion;
questionLogic: TSurveyLogic[];
updateQuestion: (questionIdx: number, updatedAttributes: any) => void;
questionIdx: number;
}
export function LogicEditorActions({
export const LogicEditorActions = ({
localSurvey,
logicItem,
logicIdx,
question,
questionLogic,
updateQuestion,
questionIdx,
}: LogicEditorActions) {
}: LogicEditorActions) => {
const actions = logicItem.actions;
const handleActionsChange = (
operation: "remove" | "addBelow" | "duplicate" | "update",
actionIdx: number,
action?: TSurveyLogicAction
) => {
const logicCopy = structuredClone(question.logic) ?? [];
const currentLogicItem = logicCopy[logicIdx];
const actionsClone = currentLogicItem.actions;
const handleActionsChange = useCallback(
(
operation: "remove" | "addBelow" | "duplicate" | "update",
actionIdx: number,
action?: TSurveyLogicAction
) => {
const currentLogicCopy = structuredClone(logicItem);
const actionsClone = currentLogicCopy.actions;
switch (operation) {
case "remove":
actionsClone.splice(actionIdx, 1);
break;
case "addBelow":
actionsClone.splice(actionIdx + 1, 0, { id: createId(), objective: "jumpToQuestion", target: "" });
break;
case "duplicate":
actionsClone.splice(actionIdx + 1, 0, { ...actionsClone[actionIdx], id: createId() });
break;
case "update":
if (!action) return;
actionsClone[actionIdx] = action;
break;
}
switch (operation) {
case "remove":
actionsClone.splice(actionIdx, 1);
break;
case "addBelow":
actionsClone.splice(actionIdx + 1, 0, {
id: createId(),
objective: "jumpToQuestion",
target: "",
});
break;
case "duplicate":
actionsClone.splice(actionIdx + 1, 0, { ...actionsClone[actionIdx], id: createId() });
break;
case "update":
if (!action) return;
actionsClone[actionIdx] = action;
break;
}
updateQuestion(questionIdx, {
logic: logicCopy,
});
};
const updatedLogic = questionLogic.map((item, idx) => (idx === logicIdx ? currentLogicCopy : item));
const handleObjectiveChange = (actionIdx: number, objective: TActionObjective) => {
const action = actions[actionIdx];
const actionBody = getUpdatedActionBody(action, objective);
handleActionsChange("update", actionIdx, actionBody);
};
updateQuestion(questionIdx, {
logic: updatedLogic,
});
},
[logicIdx, logicItem, questionIdx, questionLogic]
);
const handleValuesChange = (actionIdx: number, values: Partial<TSurveyLogicAction>) => {
const action = actions[actionIdx];
const actionBody = { ...action, ...values } as TSurveyLogicAction;
handleActionsChange("update", actionIdx, actionBody);
};
const handleObjectiveChange = useCallback(
(actionIdx: number, objective: TActionObjective) => {
const action = actions[actionIdx];
const actionBody = getUpdatedActionBody(action, objective);
handleActionsChange("update", actionIdx, actionBody);
},
[actions]
);
const handleValuesChange = useCallback(
(actionIdx: number, values: Partial<TSurveyLogicAction>) => {
const action = actions[actionIdx];
const actionBody = { ...action, ...values } as TSurveyLogicAction;
handleActionsChange("update", actionIdx, actionBody);
},
[actions]
);
const questions = useMemo(() => localSurvey.questions, [localSurvey.questions]);
const endings = useMemo(() => localSurvey.endings, [localSurvey.endings]);
const variables = useMemo(() => localSurvey.variables, [localSurvey.variables]);
const hiddenFields = useMemo(() => localSurvey.hiddenFields, [localSurvey.hiddenFields]);
return (
<div className="flex grow gap-2">
<CornerDownRightIcon className="mt-3 h-4 w-4 shrink-0" />
<div className="flex grow flex-col gap-y-2">
{actions?.map((action, idx) => (
<div key={action.id} className="flex grow items-center justify-between gap-x-2">
<div className="block w-9 shrink-0">{idx === 0 ? "Then" : "and"}</div>
<div className="flex grow items-center gap-x-2">
<InputCombobox
id={`action-${idx}-objective`}
key={`objective-${action.id}`}
showSearch={false}
options={actionObjectiveOptions}
value={action.objective}
onChangeValue={(val: TActionObjective) => {
handleObjectiveChange(idx, val);
}}
comboboxClasses="grow"
/>
{action.objective !== "calculate" && (
<InputCombobox
id={`action-${idx}-target`}
key={`target-${action.id}`}
showSearch={false}
options={getActionTargetOptions(action, localSurvey, questionIdx)}
value={action.target}
onChangeValue={(val: string) => {
handleValuesChange(idx, {
target: val,
});
}}
comboboxClasses="grow"
/>
)}
{action.objective === "calculate" && (
<>
<InputCombobox
id={`action-${idx}-variableId`}
key={`variableId-${action.id}`}
showSearch={false}
options={getActionVariableOptions(localSurvey)}
value={action.variableId}
onChangeValue={(val: string) => {
handleValuesChange(idx, {
variableId: val,
value: {
type: "static",
value: "",
},
});
}}
comboboxClasses="grow"
emptyDropdownText="Add a variable to calculate"
/>
<InputCombobox
id={`action-${idx}-operator`}
key={`operator-${action.id}`}
showSearch={false}
options={getActionOperatorOptions(
localSurvey.variables.find((v) => v.id === action.variableId)?.type
)}
value={action.operator}
onChangeValue={(
val: TActionTextVariableCalculateOperator | TActionNumberVariableCalculateOperator
) => {
handleValuesChange(idx, {
operator: val,
});
}}
comboboxClasses="grow"
/>
<InputCombobox
id={`action-${idx}-value`}
key={`value-${action.id}`}
withInput={true}
clearable={true}
value={action.value?.value ?? ""}
inputProps={{
placeholder: "Value",
type: localSurvey.variables.find((v) => v.id === action.variableId)?.type || "text",
}}
groupedOptions={getActionValueOptions(action.variableId, localSurvey)}
onChangeValue={(val, option, fromInput) => {
const fieldType = option?.meta?.type as TActionVariableValueType;
if (!fromInput && fieldType !== "static") {
handleValuesChange(idx, {
value: {
type: fieldType,
value: val as string,
},
});
} else if (fromInput) {
handleValuesChange(idx, {
value: {
type: "static",
value: val as string,
},
});
}
}}
comboboxClasses="grow shrink-0"
/>
</>
)}
</div>
<DropdownMenu>
<DropdownMenuTrigger id={`actions-${idx}-dropdown`}>
<EllipsisVerticalIcon className="h-4 w-4 text-slate-700 hover:text-slate-950" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleActionsChange("addBelow", idx);
}}>
<PlusIcon className="h-4 w-4" />
Add action below
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={actions.length === 1}
onClick={() => {
handleActionsChange("remove", idx);
}}>
<TrashIcon className="h-4 w-4" />
Remove
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleActionsChange("duplicate", idx);
}}>
<CopyIcon className="h-4 w-4" />
Duplicate
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<LogicEditorAction
action={action}
actionIdx={idx}
handleActionsChange={handleActionsChange}
handleObjectiveChange={handleObjectiveChange}
handleValuesChange={handleValuesChange}
endings={endings}
isRemoveDisabled={actions.length === 1}
questions={questions}
variables={variables}
questionIdx={questionIdx}
hiddenFields={hiddenFields}
/>
))}
</div>
</div>
);
}
};

View File

@@ -30,8 +30,8 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@formbricks/ui/components/DropdownMenu";
import { InputCombobox, TComboboxOption } from "@formbricks/ui/components/InputCombobox";
} from "@formbricks/ui/DropdownMenu";
import { InputCombobox, TComboboxOption } from "@formbricks/ui/InputCombobox";
interface LogicEditorConditionsProps {
conditions: TConditionGroup;
@@ -88,7 +88,7 @@ export function LogicEditorConditions({
const logicItem = logicCopy[logicIdx];
removeCondition(logicItem.conditions, resourceId);
// Remove the logic item if there are zero conditions left
// Remove the logic item if there are no conditions left
if (logicItem.conditions.conditions.length === 0) {
logicCopy.splice(logicIdx, 1);
}

View File

@@ -4,9 +4,9 @@ import { PlusIcon, TrashIcon } from "lucide-react";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TI18nString, TSurvey, TSurveyMatrixQuestion } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Button } from "@formbricks/ui/Button";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { isLabelValidForAllLanguages } from "../lib/validation";
interface MatrixQuestionFormProps {

View File

@@ -16,16 +16,10 @@ import {
TSurveyMultipleChoiceQuestion,
TSurveyQuestionTypeEnum,
} from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@formbricks/ui/components/Select";
import { Button } from "@formbricks/ui/Button";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui/Select";
import { QuestionOptionChoice } from "./QuestionOptionChoice";
interface OpenQuestionFormProps {

View File

@@ -4,9 +4,9 @@ import { PlusIcon } from "lucide-react";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyNPSQuestion } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/Button";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
interface NPSQuestionFormProps {
localSurvey: TSurvey;

View File

@@ -8,10 +8,10 @@ import {
TSurveyOpenTextQuestion,
TSurveyOpenTextQuestionInputType,
} from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { OptionsSwitch } from "@formbricks/ui/components/OptionsSwitch";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Button } from "@formbricks/ui/Button";
import { Label } from "@formbricks/ui/Label";
import { OptionsSwitch } from "@formbricks/ui/OptionsSwitch";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
const questionTypes = [
{ value: "text", label: "Text", icon: <MessageSquareTextIcon className="h-4 w-4" /> },

View File

@@ -4,11 +4,11 @@ import { cn } from "@formbricks/lib/cn";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyPictureSelectionQuestion } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { FileInput } from "@formbricks/ui/components/FileInput";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
import { Button } from "@formbricks/ui/Button";
import { FileInput } from "@formbricks/ui/FileInput";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Switch } from "@formbricks/ui/Switch";
interface PictureSelectionFormProps {
localSurvey: TSurvey;

View File

@@ -2,9 +2,9 @@
import { cn } from "@formbricks/lib/cn";
import { TPlacement } from "@formbricks/types/common";
import { Label } from "@formbricks/ui/components/Label";
import { getPlacementStyle } from "@formbricks/ui/components/PreviewSurvey/lib/utils";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/components/RadioGroup";
import { Label } from "@formbricks/ui/Label";
import { getPlacementStyle } from "@formbricks/ui/PreviewSurvey/lib/utils";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/RadioGroup";
const placements = [
{ name: "Bottom Right", value: "bottomRight", disabled: false },

View File

@@ -18,9 +18,9 @@ import {
TSurveyQuestion,
TSurveyQuestionTypeEnum,
} from "@formbricks/types/surveys/types";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { Switch } from "@formbricks/ui/components/Switch";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Switch } from "@formbricks/ui/Switch";
import { AddressQuestionForm } from "./AddressQuestionForm";
import { AdvancedSettings } from "./AdvancedSettings";
import { CTAQuestionForm } from "./CTAQuestionForm";

View File

@@ -12,7 +12,7 @@ import {
TSurveyQuestionChoice,
TSurveyRankingQuestion,
} from "@formbricks/types/surveys/types";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { isLabelValidForAllLanguages } from "../lib/validation";
interface ChoiceProps {

View File

@@ -264,6 +264,7 @@ export const QuestionsView = ({
}
}
});
setLocalSurvey(updatedSurvey);
validateSurveyQuestion(updatedSurvey.questions[questionIdx]);
};

View File

@@ -13,16 +13,10 @@ import {
TSurvey,
TSurveyRankingQuestion,
} from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@formbricks/ui/components/Select";
import { Button } from "@formbricks/ui/Button";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui/Select";
import { QuestionOptionChoice } from "./QuestionOptionChoice";
interface RankingQuestionFormProps {

View File

@@ -2,10 +2,10 @@ import { HashIcon, PlusIcon, SmileIcon, StarIcon } from "lucide-react";
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TSurvey, TSurveyRatingQuestion } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { Label } from "@formbricks/ui/components/Label";
import { QuestionFormInput } from "@formbricks/ui/components/QuestionFormInput";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/Button";
import { Label } from "@formbricks/ui/Label";
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
import { Dropdown } from "./RatingTypeDropdown";
interface RatingQuestionFormProps {

View File

@@ -5,10 +5,10 @@ import { CheckIcon } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { TSurvey } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/components/RadioGroup";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/RadioGroup";
interface DisplayOption {
id: "displayOnce" | "displayMultiple" | "respondMultiple" | "displaySome";

View File

@@ -1,6 +1,6 @@
import { TSurveyRedirectUrlCard } from "@formbricks/types/surveys/types";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
interface RedirectUrlFormProps {
endingCard: TSurveyRedirectUrlCard;

View File

@@ -7,11 +7,11 @@ import { KeyboardEventHandler, useEffect, useState } from "react";
import toast from "react-hot-toast";
import { cn } from "@formbricks/lib/cn";
import { TSurvey } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { DatePicker } from "@formbricks/ui/components/DatePicker";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { Switch } from "@formbricks/ui/components/Switch";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { DatePicker } from "@formbricks/ui/DatePicker";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { Switch } from "@formbricks/ui/Switch";
interface ResponseOptionsCardProps {
localSurvey: TSurvey;

View File

@@ -2,7 +2,7 @@ import { Code2Icon, MousePointerClickIcon, SparklesIcon } from "lucide-react";
import { useState } from "react";
import { TActionClass } from "@formbricks/types/action-classes";
import { TSurvey } from "@formbricks/types/surveys/types";
import { Input } from "@formbricks/ui/components/Input";
import { Input } from "@formbricks/ui/Input";
interface SavedActionsTabProps {
actionClasses: TActionClass[];

View File

@@ -8,8 +8,8 @@ import { TEnvironment } from "@formbricks/types/environment";
import { TProduct, TProductStyling } from "@formbricks/types/product";
import { TBaseStyling } from "@formbricks/types/styling";
import { TSurvey, TSurveyStyling } from "@formbricks/types/surveys/types";
import { AlertDialog } from "@formbricks/ui/components/AlertDialog";
import { Button } from "@formbricks/ui/components/Button";
import { AlertDialog } from "@formbricks/ui/AlertDialog";
import { Button } from "@formbricks/ui/Button";
import {
FormControl,
FormDescription,
@@ -17,8 +17,8 @@ import {
FormItem,
FormLabel,
FormProvider,
} from "@formbricks/ui/components/Form";
import { Switch } from "@formbricks/ui/components/Switch";
} from "@formbricks/ui/Form";
import { Switch } from "@formbricks/ui/Switch";
import { BackgroundStylingCard } from "./BackgroundStylingCard";
import { CardStylingSettings } from "./CardStylingSettings";
import { FormStylingSettings } from "./FormStylingSettings";

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { TabBar } from "@formbricks/ui/components/TabBar";
import { TabBar } from "@formbricks/ui/TabBar";
import { AnimatedSurveyBg } from "./AnimatedSurveyBg";
import { ColorSurveyBg } from "./ColorSurveyBg";
import { UploadImageSurveyBg } from "./ImageSurveyBg";

View File

@@ -12,7 +12,7 @@ import { TOrganizationBillingPlan } from "@formbricks/types/organizations";
import { TProduct } from "@formbricks/types/product";
import { TSegment } from "@formbricks/types/segment";
import { TSurvey, TSurveyEditorTabs, TSurveyStyling } from "@formbricks/types/surveys/types";
import { PreviewSurvey } from "@formbricks/ui/components/PreviewSurvey";
import { PreviewSurvey } from "@formbricks/ui/PreviewSurvey";
import { refetchProductAction } from "../actions";
import { LoadingSkeleton } from "./LoadingSkeleton";
import { QuestionsAudienceTabs } from "./QuestionsStylingSettingsTabs";

View File

@@ -20,10 +20,10 @@ import {
ZSurveyEndScreenCard,
ZSurveyRedirectUrlCard,
} from "@formbricks/types/surveys/types";
import { AlertDialog } from "@formbricks/ui/components/AlertDialog";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/components/Tooltip";
import { AlertDialog } from "@formbricks/ui/AlertDialog";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
import { updateSurveyAction } from "../actions";
import { isSurveyValid } from "../lib/validation";

View File

@@ -6,8 +6,8 @@ import Link from "next/link";
import { useState } from "react";
import { TPlacement } from "@formbricks/types/common";
import { TSurvey, TSurveyProductOverwrites } from "@formbricks/types/surveys/types";
import { Label } from "@formbricks/ui/components/Label";
import { Switch } from "@formbricks/ui/components/Switch";
import { Label } from "@formbricks/ui/Label";
import { Switch } from "@formbricks/ui/Switch";
import { Placement } from "./Placement";
interface SurveyPlacementCardProps {

View File

@@ -8,17 +8,11 @@ import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import { extractRecallInfo } from "@formbricks/lib/utils/recall";
import { TSurvey, TSurveyVariable } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { FormControl, FormField, FormItem, FormProvider } from "@formbricks/ui/components/Form";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@formbricks/ui/components/Select";
import { Button } from "@formbricks/ui/Button";
import { FormControl, FormField, FormItem, FormProvider } from "@formbricks/ui/Form";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui/Select";
interface SurveyVariablesCardItemProps {
variable?: TSurveyVariable;

View File

@@ -13,15 +13,15 @@ import { isAdvancedSegment } from "@formbricks/lib/segment/utils";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TBaseFilter, TSegment, TSegmentCreateInput, TSegmentUpdateInput } from "@formbricks/types/segment";
import { TSurvey } from "@formbricks/types/surveys/types";
import { AlertDialog } from "@formbricks/ui/components/AlertDialog";
import { BasicAddFilterModal } from "@formbricks/ui/components/BasicAddFilterModal";
import { BasicSegmentEditor } from "@formbricks/ui/components/BasicSegmentEditor";
import { Button } from "@formbricks/ui/components/Button";
import { LoadSegmentModal } from "@formbricks/ui/components/LoadSegmentModal";
import { SaveAsNewSegmentModal } from "@formbricks/ui/components/SaveAsNewSegmentModal";
import { SegmentTitle } from "@formbricks/ui/components/SegmentTitle";
import { TargetingIndicator } from "@formbricks/ui/components/TargetingIndicator";
import { UpgradePlanNotice } from "@formbricks/ui/components/UpgradePlanNotice";
import { AlertDialog } from "@formbricks/ui/AlertDialog";
import { BasicAddFilterModal } from "@formbricks/ui/BasicAddFilterModal";
import { BasicSegmentEditor } from "@formbricks/ui/BasicSegmentEditor";
import { Button } from "@formbricks/ui/Button";
import { LoadSegmentModal } from "@formbricks/ui/LoadSegmentModal";
import { SaveAsNewSegmentModal } from "@formbricks/ui/SaveAsNewSegmentModal";
import { SegmentTitle } from "@formbricks/ui/SegmentTitle";
import { TargetingIndicator } from "@formbricks/ui/TargetingIndicator";
import { UpgradePlanNotice } from "@formbricks/ui/UpgradePlanNotice";
import {
cloneBasicSegmentAction,
createBasicSegmentAction,

View File

@@ -6,9 +6,9 @@ import UnsplashImage from "next/image";
import { useEffect, useRef, useState } from "react";
import toast from "react-hot-toast";
import { TSurveyBackgroundBgType } from "@formbricks/types/surveys/types";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { LoadingSpinner } from "@formbricks/ui/components/LoadingSpinner";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { LoadingSpinner } from "@formbricks/ui/LoadingSpinner";
import { getImagesFromUnsplashAction, triggerDownloadUnsplashImageAction } from "../actions";
interface ImageFromUnsplashSurveyBgProps {

View File

@@ -4,9 +4,9 @@ import { useState } from "react";
import toast from "react-hot-toast";
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys/types";
import { validateId } from "@formbricks/types/surveys/validation";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
interface UpdateQuestionIdProps {
localSurvey: TSurvey;

View File

@@ -14,9 +14,9 @@ import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { TActionClass } from "@formbricks/types/action-classes";
import { TMembershipRole } from "@formbricks/types/memberships";
import { TSurvey } from "@formbricks/types/surveys/types";
import { AdvancedOptionToggle } from "@formbricks/ui/components/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { AddActionModal } from "./AddActionModal";
interface WhenToSendCardProps {

View File

@@ -1,8 +1,8 @@
import { EyeOffIcon, FileDigitIcon, FileType2Icon } from "lucide-react";
import { HTMLInputTypeAttribute } from "react";
import { HTMLInputTypeAttribute, useMemo } from "react";
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
import { isConditionGroup } from "@formbricks/lib/surveyLogic/utils";
import { questionTypes } from "@formbricks/lib/utils/questions";
import { questionIconMapping, questionTypes } from "@formbricks/lib/utils/questions";
import { recallToHeadline } from "@formbricks/lib/utils/recall";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import {
@@ -18,7 +18,7 @@ import {
TSurveyQuestionTypeEnum,
TSurveyVariable,
} from "@formbricks/types/surveys/types";
import { TComboboxGroupedOption, TComboboxOption } from "@formbricks/ui/components/InputCombobox";
import { TComboboxGroupedOption, TComboboxOption } from "@formbricks/ui/InputCombobox";
import { TLogicRuleOption, logicRules } from "./logicRuleEngine";
// formats the text to highlight specific parts of the text with slashes
@@ -40,14 +40,6 @@ export const formatTextWithSlashes = (text: string) => {
});
};
const questionIconMapping = questionTypes.reduce(
(prev, curr) => ({
...prev,
[curr.id]: curr.icon,
}),
{}
);
export const getConditionValueOptions = (
localSurvey: TSurvey,
currQuestionIdx: number
@@ -756,55 +748,6 @@ export const getMatchValueProps = (
return { show: false, options: [] };
};
export const getActionTargetOptions = (
action: TSurveyLogicAction,
localSurvey: TSurvey,
currQuestionIdx: number
): TComboboxOption[] => {
let questions = localSurvey.questions.filter((_, idx) => idx !== currQuestionIdx);
if (action.objective === "requireAnswer") {
questions = questions.filter((question) => !question.required);
}
const questionOptions = questions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
};
});
if (action.objective === "requireAnswer") return questionOptions;
const endingCardOptions = localSurvey.endings.map((ending) => {
return {
label:
ending.type === "endScreen"
? getLocalizedValue(ending.headline, "default") || "End Screen"
: ending.label || "Redirect Thank you card",
value: ending.id,
};
});
return [...questionOptions, ...endingCardOptions];
};
export const getActionVariableOptions = (localSurvey: TSurvey): TComboboxOption[] => {
const variables = localSurvey.variables ?? [];
return variables.map((variable) => {
return {
icon: variable.type === "number" ? FileDigitIcon : FileType2Icon,
label: variable.name,
value: variable.id,
meta: {
variableType: variable.type,
},
};
});
};
export const getActionOperatorOptions = (variableType?: TSurveyVariable["type"]): TComboboxOption[] => {
if (variableType === "number") {
return [
@@ -844,151 +787,6 @@ export const getActionOperatorOptions = (variableType?: TSurveyVariable["type"])
return [];
};
export const getActionValueOptions = (variableId: string, localSurvey: TSurvey): TComboboxGroupedOption[] => {
const hiddenFields = localSurvey.hiddenFields?.fieldIds ?? [];
let variables = localSurvey.variables ?? [];
const questions = localSurvey.questions;
const hiddenFieldsOptions = hiddenFields.map((field) => {
return {
icon: EyeOffIcon,
label: field,
value: field,
meta: {
type: "hiddenField",
},
};
});
const selectedVariable = variables.find((variable) => variable.id === variableId);
variables = variables.filter((variable) => variable.id !== variableId);
if (!selectedVariable) return [];
if (selectedVariable.type === "text") {
const allowedQuestions = questions.filter((question) =>
[
TSurveyQuestionTypeEnum.OpenText,
TSurveyQuestionTypeEnum.MultipleChoiceSingle,
TSurveyQuestionTypeEnum.Rating,
TSurveyQuestionTypeEnum.NPS,
TSurveyQuestionTypeEnum.Date,
].includes(question.type)
);
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
type: "question",
},
};
});
const stringVariables = variables.filter((variable) => variable.type === "text");
const variableOptions = stringVariables.map((variable) => {
return {
icon: FileType2Icon,
label: variable.name,
value: variable.id,
meta: {
type: "variable",
},
};
});
const groupedOptions: TComboboxGroupedOption[] = [];
if (questionOptions.length > 0) {
groupedOptions.push({
label: "Questions",
value: "questions",
options: questionOptions,
});
}
if (variableOptions.length > 0) {
groupedOptions.push({
label: "Variables",
value: "variables",
options: variableOptions,
});
}
if (hiddenFieldsOptions.length > 0) {
groupedOptions.push({
label: "Hidden Fields",
value: "hiddenFields",
options: hiddenFieldsOptions,
});
}
return groupedOptions;
} else if (selectedVariable.type === "number") {
const allowedQuestions = questions.filter((question) =>
[TSurveyQuestionTypeEnum.Rating, TSurveyQuestionTypeEnum.NPS].includes(question.type)
);
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
type: "question",
},
};
});
const numberVariables = variables.filter((variable) => variable.type === "number");
const variableOptions = numberVariables.map((variable) => {
return {
icon: FileDigitIcon,
label: variable.name,
value: variable.id,
meta: {
type: "variable",
},
};
});
const groupedOptions: TComboboxGroupedOption[] = [];
if (questionOptions.length > 0) {
groupedOptions.push({
label: "Questions",
value: "questions",
options: questionOptions,
});
}
if (variableOptions.length > 0) {
groupedOptions.push({
label: "Variables",
value: "variables",
options: variableOptions,
});
}
if (hiddenFieldsOptions.length > 0) {
groupedOptions.push({
label: "Hidden Fields",
value: "hiddenFields",
options: hiddenFieldsOptions,
});
}
return groupedOptions;
}
return [];
};
const isUsedInLeftOperand = (
leftOperand: TLeftOperand,
type: "question" | "hiddenField" | "variable",

View File

@@ -12,7 +12,7 @@ import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
import { getResponseCountBySurveyId } from "@formbricks/lib/response/service";
import { getSegments } from "@formbricks/lib/segment/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { ErrorComponent } from "@formbricks/ui/components/ErrorComponent";
import { ErrorComponent } from "@formbricks/ui/ErrorComponent";
import { SurveyEditor } from "./components/SurveyEditor";
export const generateMetadata = async ({ params }) => {

View File

@@ -2,7 +2,7 @@
import { ArrowLeftIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { Button } from "@formbricks/ui/components/Button";
import { Button } from "@formbricks/ui/Button";
export const BackButton = () => {
const router = useRouter();

View File

@@ -7,9 +7,9 @@ import type { TEnvironment } from "@formbricks/types/environment";
import type { TProduct, TProductConfigChannel, TProductConfigIndustry } from "@formbricks/types/product";
import type { TTemplate, TTemplateRole } from "@formbricks/types/templates";
import { TUser } from "@formbricks/types/user";
import { PreviewSurvey } from "@formbricks/ui/components/PreviewSurvey";
import { SearchBar } from "@formbricks/ui/components/SearchBar";
import { TemplateList } from "@formbricks/ui/components/TemplateList";
import { PreviewSurvey } from "@formbricks/ui/PreviewSurvey";
import { SearchBox } from "@formbricks/ui/SearchBox";
import { TemplateList } from "@formbricks/ui/TemplateList";
import { minimalSurvey } from "../../lib/minimalSurvey";
type TemplateContainerWithPreviewProps = {
@@ -39,11 +39,14 @@ export const TemplateContainerWithPreview = ({
<div className="mb-3 ml-6 mt-6 flex flex-col items-center justify-between md:flex-row md:items-end">
<h1 className="text-2xl font-bold text-slate-800">Create a new survey</h1>
<div className="px-6">
<SearchBar
<SearchBox
autoFocus
value={templateSearch ?? ""}
onChange={setTemplateSearch}
onChange={(e) => setTemplateSearch(e.target.value)}
placeholder={"Search..."}
className="border-slate-700"
className="block rounded-md border border-slate-100 bg-white shadow-sm focus:border-slate-500 focus:outline-none focus:ring-0 sm:text-sm md:w-auto"
type="search"
name="search"
/>
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { LoadingSpinner } from "@formbricks/ui/components/LoadingSpinner";
import { LoadingSpinner } from "@formbricks/ui/LoadingSpinner";
const Loading = () => {
return <LoadingSpinner />;

View File

@@ -1,8 +1,8 @@
"use client";
import { useEffect, useState } from "react";
import { Button } from "@formbricks/ui/components/Button";
import { Confetti } from "@formbricks/ui/components/Confetti";
import { Button } from "@formbricks/ui/Button";
import { Confetti } from "@formbricks/ui/Confetti";
interface ConfirmationPageProps {
environmentId: string;

View File

@@ -1,5 +1,5 @@
import { ConfirmationPage } from "@/app/(app)/billing-confirmation/components/ConfirmationPage";
import { PageContentWrapper } from "@formbricks/ui/components/PageContentWrapper";
import { PageContentWrapper } from "@formbricks/ui/PageContentWrapper";
export const dynamic = "force-dynamic";

View File

@@ -6,9 +6,9 @@ import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { convertDateTimeStringShort } from "@formbricks/lib/time";
import { capitalizeFirstLetter } from "@formbricks/lib/utils/strings";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { ErrorComponent } from "@formbricks/ui/components/ErrorComponent";
import { Label } from "@formbricks/ui/components/Label";
import { LoadingSpinner } from "@formbricks/ui/components/LoadingSpinner";
import { ErrorComponent } from "@formbricks/ui/ErrorComponent";
import { Label } from "@formbricks/ui/Label";
import { LoadingSpinner } from "@formbricks/ui/LoadingSpinner";
import { getSegmentsByAttributeClassAction } from "../actions";
interface EventActivityTabProps {

View File

@@ -2,7 +2,7 @@
import { useMemo, useState } from "react";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { Switch } from "@formbricks/ui/components/Switch";
import { Switch } from "@formbricks/ui/Switch";
import { AttributeDetailModal } from "./AttributeDetailModal";
import { AttributeClassDataRow } from "./AttributeRowData";
import { AttributeTableHeading } from "./AttributeTableHeading";

View File

@@ -1,6 +1,6 @@
import { TagIcon } from "lucide-react";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { ModalWithTabs } from "@formbricks/ui/components/ModalWithTabs";
import { ModalWithTabs } from "@formbricks/ui/ModalWithTabs";
import { AttributeActivityTab } from "./AttributeActivityTab";
import { AttributeSettingsTab } from "./AttributeSettingsTab";

View File

@@ -1,6 +1,6 @@
import { TagIcon } from "lucide-react";
import { timeSinceConditionally } from "@formbricks/lib/time";
import { Badge } from "@formbricks/ui/components/Badge";
import { Badge } from "@formbricks/ui/Badge";
export const AttributeClassDataRow = ({ attributeClass }) => {
return (

View File

@@ -6,9 +6,9 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { updateAttributeClass } from "@formbricks/lib/attributeClass/service";
import { Button } from "@formbricks/ui/components/Button";
import { Input } from "@formbricks/ui/components/Input";
import { Label } from "@formbricks/ui/components/Label";
import { Button } from "@formbricks/ui/Button";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
interface AttributeSettingsTabProps {
attributeClass: AttributeClass;

View File

@@ -1,5 +1,5 @@
import { HelpCircleIcon } from "lucide-react";
import { Button } from "@formbricks/ui/components/Button";
import { Button } from "@formbricks/ui/Button";
export const HowToAddAttributesButton = () => {
return (

View File

@@ -1,4 +1,4 @@
import { Modal } from "@formbricks/ui/components/Modal";
import { Modal } from "@formbricks/ui/Modal";
interface UploadAttributesModalProps {
open: boolean;

View File

@@ -1,7 +1,7 @@
import { PersonSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation";
import { TagIcon } from "lucide-react";
import { PageContentWrapper } from "@formbricks/ui/components/PageContentWrapper";
import { PageHeader } from "@formbricks/ui/components/PageHeader";
import { PageContentWrapper } from "@formbricks/ui/PageContentWrapper";
import { PageHeader } from "@formbricks/ui/PageHeader";
const Loading = () => {
return (

View File

@@ -4,9 +4,9 @@ import { Metadata } from "next";
import { notFound } from "next/navigation";
import { getAttributeClasses } from "@formbricks/lib/attributeClass/service";
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
import { Button } from "@formbricks/ui/components/Button";
import { PageContentWrapper } from "@formbricks/ui/components/PageContentWrapper";
import { PageHeader } from "@formbricks/ui/components/PageHeader";
import { Button } from "@formbricks/ui/Button";
import { PageContentWrapper } from "@formbricks/ui/PageContentWrapper";
import { PageHeader } from "@formbricks/ui/PageHeader";
import { AttributeClassesTable } from "./components/AttributeClassesTable";
export const metadata: Metadata = {

View File

@@ -5,8 +5,8 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { deletePersonAction } from "@formbricks/ui/components/DataTable/actions";
import { DeleteDialog } from "@formbricks/ui/components/DeleteDialog";
import { deletePersonAction } from "@formbricks/ui/DataTable/actions";
import { DeleteDialog } from "@formbricks/ui/DeleteDialog";
interface DeletePersonButtonProps {
environmentId: string;

Some files were not shown because too many files have changed in this diff Show More