mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 02:10:12 -06:00
fix: survey editor validation (#2749)
Co-authored-by: Matti Nannt <mail@matthiasnannt.com> Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com> Co-authored-by: Johannes <johannes@formbricks.com>
This commit is contained in:
@@ -27,7 +27,7 @@ import { TActionClassInput } from "@formbricks/types/actionClasses";
|
||||
import { AuthorizationError } from "@formbricks/types/errors";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TBaseFilters, TSegmentUpdateInput, ZSegmentFilters } from "@formbricks/types/segment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const surveyMutateAction = async (survey: TSurvey): Promise<TSurvey> => {
|
||||
return await updateSurvey(survey);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { TActionClass } from "@formbricks/types/actionClasses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { ModalWithTabs } from "@formbricks/ui/ModalWithTabs";
|
||||
import { CreateNewActionTab } from "./CreateNewActionTab";
|
||||
import { SavedActionsTab } from "./SavedActionsTab";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyAddressQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyAddressQuestion } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
import { LogicEditor } from "./LogicEditor";
|
||||
import { UpdateQuestionId } from "./UpdateQuestionId";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { CheckIcon } from "lucide-react";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TProductStyling } from "@formbricks/types/product";
|
||||
import { TSurveyStyling } from "@formbricks/types/surveys";
|
||||
import { TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
|
||||
import { Slider } from "@formbricks/ui/Slider";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useState } from "react";
|
||||
import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyCTAQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyCTAQuestion } from "@formbricks/types/surveys/types";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { PlusIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyCalQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyCalQuestion } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Checkbox } from "@formbricks/ui/Checkbox";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { UseFormReturn } from "react-hook-form";
|
||||
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";
|
||||
import { TSurveyStyling, TSurveyType } from "@formbricks/types/surveys/types";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
import { CardArrangementTabs } from "@formbricks/ui/CardArrangementTabs";
|
||||
import { ColorPicker } from "@formbricks/ui/ColorPicker";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useState } from "react";
|
||||
import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyConsentQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyConsentQuestion } from "@formbricks/types/surveys/types";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
TActionClassInputCode,
|
||||
ZActionClassInput,
|
||||
} from "@formbricks/types/actionClasses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { FormControl, FormError, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyDateQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyDateQuestion } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useState } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useState } from "react";
|
||||
import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { FileInput } from "@formbricks/ui/FileInput";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useGetBillingInfo } from "@formbricks/lib/organization/hooks/useGetBill
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TAllowedFileExtension, ZAllowedFileExtension } from "@formbricks/types/common";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSurvey, TSurveyFileUploadQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyFileUploadQuestion } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
|
||||
@@ -8,7 +8,7 @@ import { cn } from "@formbricks/lib/cn";
|
||||
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";
|
||||
import { TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { ColorPicker } from "@formbricks/ui/ColorPicker";
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@formbricks/ui/Form";
|
||||
|
||||
@@ -4,13 +4,13 @@ import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TSurvey, TSurveyHiddenFields } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyHiddenFields } from "@formbricks/types/surveys/types";
|
||||
import { validateId } from "@formbricks/types/surveys/validation";
|
||||
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";
|
||||
import { validateId } from "../lib/validation";
|
||||
|
||||
interface HiddenFieldsCardProps {
|
||||
localSurvey: TSurvey;
|
||||
@@ -119,14 +119,24 @@ export const HiddenFieldsCard = ({
|
||||
e.preventDefault();
|
||||
const existingQuestionIds = localSurvey.questions.map((question) => question.id);
|
||||
const existingHiddenFieldIds = localSurvey.hiddenFields.fieldIds ?? [];
|
||||
if (validateId("Hidden field", hiddenField, existingQuestionIds, existingHiddenFieldIds)) {
|
||||
updateSurvey({
|
||||
fieldIds: [...(localSurvey.hiddenFields?.fieldIds || []), hiddenField],
|
||||
enabled: true,
|
||||
});
|
||||
toast.success("Hidden field added successfully");
|
||||
setHiddenField("");
|
||||
const validateIdError = validateId(
|
||||
"Hidden field",
|
||||
hiddenField,
|
||||
existingQuestionIds,
|
||||
existingHiddenFieldIds
|
||||
);
|
||||
|
||||
if (validateIdError) {
|
||||
toast.error(validateIdError);
|
||||
return;
|
||||
}
|
||||
|
||||
updateSurvey({
|
||||
fieldIds: [...(localSurvey.hiddenFields?.fieldIds || []), hiddenField],
|
||||
enabled: true,
|
||||
});
|
||||
toast.success("Hidden field added successfully");
|
||||
setHiddenField("");
|
||||
}}>
|
||||
<Label htmlFor="headline">Hidden Field</Label>
|
||||
<div className="mt-2 flex gap-2">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { cn } from "@formbricks/lib/cn";
|
||||
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";
|
||||
import { TSurvey, TSurveyType } from "@formbricks/types/surveys/types";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/RadioGroup";
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TSurveyLogicCondition,
|
||||
TSurveyQuestion,
|
||||
TSurveyQuestionTypeEnum,
|
||||
} from "@formbricks/types/surveys";
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { createI18nString, extractLanguageCodes, getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TI18nString, TSurvey, TSurveyMatrixQuestion } from "@formbricks/types/surveys";
|
||||
import { TI18nString, TSurvey, TSurveyMatrixQuestion } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
@@ -79,25 +78,6 @@ export const MatrixQuestionForm = ({
|
||||
}
|
||||
};
|
||||
|
||||
const checkForDuplicateLabels = () => {
|
||||
const rowLabels = question.rows
|
||||
.map((row) => getLocalizedValue(row, selectedLanguageCode))
|
||||
.filter((label) => label.trim() !== "");
|
||||
|
||||
const columnLabels = question.columns
|
||||
.map((column) => getLocalizedValue(column, selectedLanguageCode))
|
||||
.filter((label) => label.trim() !== "");
|
||||
|
||||
const duplicateRowLabels = rowLabels.filter((label, index, array) => array.indexOf(label) !== index);
|
||||
const duplicateColumnLabels = columnLabels.filter(
|
||||
(label, index, array) => array.indexOf(label) !== index
|
||||
);
|
||||
|
||||
if (duplicateRowLabels.length > 0 || duplicateColumnLabels.length > 0) {
|
||||
toast.error("Duplicate row or column labels");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form>
|
||||
<QuestionFormInput
|
||||
@@ -164,7 +144,6 @@ export const MatrixQuestionForm = ({
|
||||
updateMatrixLabel={updateMatrixLabel}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
onBlur={checkForDuplicateLabels}
|
||||
isInvalid={
|
||||
isInvalid && !isLabelValidForAllLanguages(question.rows[index], localSurvey.languages)
|
||||
}
|
||||
@@ -207,7 +186,6 @@ export const MatrixQuestionForm = ({
|
||||
updateMatrixLabel={updateMatrixLabel}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
onBlur={checkForDuplicateLabels}
|
||||
isInvalid={
|
||||
isInvalid && !isLabelValidForAllLanguages(question.columns[index], localSurvey.languages)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
TSurvey,
|
||||
TSurveyMultipleChoiceQuestion,
|
||||
TSurveyQuestionTypeEnum,
|
||||
} from "@formbricks/types/surveys";
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
@@ -68,20 +68,6 @@ export const MultipleChoiceQuestionForm = ({
|
||||
},
|
||||
};
|
||||
|
||||
const findDuplicateLabel = () => {
|
||||
for (let i = 0; i < question.choices.length; i++) {
|
||||
for (let j = i + 1; j < question.choices.length; j++) {
|
||||
if (
|
||||
getLocalizedValue(question.choices[i].label, selectedLanguageCode).trim() ===
|
||||
getLocalizedValue(question.choices[j].label, selectedLanguageCode).trim()
|
||||
) {
|
||||
return getLocalizedValue(question.choices[i].label, selectedLanguageCode).trim(); // Return the duplicate label
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const updateChoice = (choiceIdx: number, updatedAttributes: { label: TI18nString }) => {
|
||||
const newLabel = updatedAttributes.label.en;
|
||||
const oldLabel = question.choices[choiceIdx].label;
|
||||
@@ -267,13 +253,11 @@ export const MultipleChoiceQuestionForm = ({
|
||||
updateChoice={updateChoice}
|
||||
deleteChoice={deleteChoice}
|
||||
addChoice={addChoice}
|
||||
setisInvalidValue={setisInvalidValue}
|
||||
isInvalid={isInvalid}
|
||||
localSurvey={localSurvey}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
surveyLanguages={surveyLanguages}
|
||||
findDuplicateLabel={findDuplicateLabel}
|
||||
question={question}
|
||||
updateQuestion={updateQuestion}
|
||||
surveyLanguageCodes={surveyLanguageCodes}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyNPSQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyNPSQuestion } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
TSurvey,
|
||||
TSurveyOpenTextQuestion,
|
||||
TSurveyOpenTextQuestionInputType,
|
||||
} from "@formbricks/types/surveys";
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PlusIcon } from "lucide-react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyPictureSelectionQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyPictureSelectionQuestion } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { FileInput } from "@formbricks/ui/FileInput";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
|
||||
@@ -10,7 +10,12 @@ import { cn } from "@formbricks/lib/cn";
|
||||
import { recallToHeadline } from "@formbricks/lib/utils/recall";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TI18nString, TSurvey, TSurveyQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
TSurveyQuestion,
|
||||
TSurveyQuestionTypeEnum,
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
import { Switch } from "@formbricks/ui/Switch";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createId } from "@paralleldrive/cuid2";
|
||||
import { ArrowDownIcon, ArrowUpIcon, CopyIcon, EllipsisIcon, TrashIcon } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSurveyQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { ConfirmationModal } from "@formbricks/ui/ConfirmationModal";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { QuestionCard } from "./QuestionCard";
|
||||
|
||||
interface QuestionsDraggableProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PaintbrushIcon, Rows3Icon, SettingsIcon } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TSurveyEditorTabs } from "@formbricks/types/surveys";
|
||||
import { TSurveyEditorTabs } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface Tab {
|
||||
id: TSurveyEditorTabs;
|
||||
|
||||
@@ -17,13 +17,9 @@ import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
|
||||
import { checkForEmptyFallBackValue, extractRecallInfo } from "@formbricks/lib/utils/recall";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys";
|
||||
import {
|
||||
findQuestionsWithCyclicLogic,
|
||||
isCardValid,
|
||||
validateQuestion,
|
||||
validateSurveyQuestionsInBatch,
|
||||
} from "../lib/validation";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
import { findQuestionsWithCyclicLogic } from "@formbricks/types/surveys/validation";
|
||||
import { isCardValid, validateQuestion, validateSurveyQuestionsInBatch } from "../lib/validation";
|
||||
import { AddQuestionButton } from "./AddQuestionButton";
|
||||
import { EditThankYouCard } from "./EditThankYouCard";
|
||||
import { EditWelcomeCard } from "./EditWelcomeCard";
|
||||
@@ -37,7 +33,7 @@ interface QuestionsViewProps {
|
||||
setActiveQuestionId: (questionId: string | null) => void;
|
||||
product: TProduct;
|
||||
invalidQuestions: string[] | null;
|
||||
setInvalidQuestions: (invalidQuestions: string[] | null) => void;
|
||||
setInvalidQuestions: React.Dispatch<SetStateAction<string[] | null>>;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (languageCode: string) => void;
|
||||
isMultiLanguageAllowed?: boolean;
|
||||
@@ -92,19 +88,24 @@ export const QuestionsView = ({
|
||||
if (invalidQuestions === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isFirstQuestion = question.id === localSurvey.questions[0].id;
|
||||
let temp = structuredClone(invalidQuestions);
|
||||
|
||||
if (validateQuestion(question, surveyLanguages, isFirstQuestion)) {
|
||||
// If question is valid, we now check for cyclic logic
|
||||
const questionsWithCyclicLogic = findQuestionsWithCyclicLogic(localSurvey.questions);
|
||||
if (!questionsWithCyclicLogic.includes(question.id)) {
|
||||
temp = invalidQuestions.filter((id) => id !== question.id);
|
||||
setInvalidQuestions(temp);
|
||||
|
||||
if (questionsWithCyclicLogic.includes(question.id) && !invalidQuestions.includes(question.id)) {
|
||||
setInvalidQuestions([...invalidQuestions, question.id]);
|
||||
return;
|
||||
}
|
||||
} else if (!invalidQuestions.includes(question.id)) {
|
||||
temp.push(question.id);
|
||||
setInvalidQuestions(temp);
|
||||
|
||||
setInvalidQuestions(invalidQuestions.filter((id) => id !== question.id));
|
||||
return;
|
||||
}
|
||||
|
||||
setInvalidQuestions([...invalidQuestions, question.id]);
|
||||
return;
|
||||
};
|
||||
|
||||
const updateQuestion = (questionIdx: number, updatedAttributes: any) => {
|
||||
@@ -125,6 +126,7 @@ export const QuestionsView = ({
|
||||
delete internalQuestionIdMap[localSurvey.questions[questionIdx].id];
|
||||
setActiveQuestionId(updatedAttributes.id);
|
||||
}
|
||||
|
||||
updatedSurvey.questions[questionIdx] = {
|
||||
...updatedSurvey.questions[questionIdx],
|
||||
...updatedAttributes,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { HashIcon, PlusIcon, SmileIcon, StarIcon } from "lucide-react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyRatingQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyRatingQuestion } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
|
||||
@@ -6,7 +6,7 @@ import Link from "next/link";
|
||||
import { KeyboardEventHandler, useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { DatePicker } from "@formbricks/ui/DatePicker";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
@@ -293,7 +293,12 @@ export const ResponseOptionsCard = ({
|
||||
};
|
||||
|
||||
const handleInputResponse = (e) => {
|
||||
const updatedSurvey = { ...localSurvey, autoComplete: parseInt(e.target.value) };
|
||||
let value = parseInt(e.target.value);
|
||||
if (Number.isNaN(value) || value < 1) {
|
||||
value = 1;
|
||||
}
|
||||
|
||||
const updatedSurvey = { ...localSurvey, autoComplete: value };
|
||||
setLocalSurvey(updatedSurvey);
|
||||
};
|
||||
|
||||
@@ -343,7 +348,7 @@ export const ResponseOptionsCard = ({
|
||||
description="Automatically close the survey after a certain number of responses."
|
||||
childBorder={true}>
|
||||
<label htmlFor="autoCompleteResponses" className="cursor-pointer bg-slate-50 p-4">
|
||||
<p className="text-sm text-slate-700">
|
||||
<p className="text-sm font-semibold text-slate-700">
|
||||
Automatically mark the survey as complete after
|
||||
<Input
|
||||
autoFocus
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Code2Icon, MousePointerClickIcon, SparklesIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { TActionClass } from "@formbricks/types/actionClasses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
|
||||
interface SavedActionsTabProps {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { GripVerticalIcon, PlusIcon, TrashIcon } from "lucide-react";
|
||||
import toast from "react-hot-toast";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { createI18nString } from "@formbricks/lib/i18n/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
@@ -10,7 +9,7 @@ import {
|
||||
TSurvey,
|
||||
TSurveyLanguage,
|
||||
TSurveyMultipleChoiceQuestion,
|
||||
} from "@formbricks/types/surveys";
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { QuestionFormInput } from "@formbricks/ui/QuestionFormInput";
|
||||
import { isLabelValidForAllLanguages } from "../lib/validation";
|
||||
|
||||
@@ -24,13 +23,11 @@ interface ChoiceProps {
|
||||
updateChoice: (choiceIdx: number, updatedAttributes: { label: TI18nString }) => void;
|
||||
deleteChoice: (choiceIdx: number) => void;
|
||||
addChoice: (choiceIdx: number) => void;
|
||||
setisInvalidValue: (value: string | null) => void;
|
||||
isInvalid: boolean;
|
||||
localSurvey: TSurvey;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (language: string) => void;
|
||||
surveyLanguages: TSurveyLanguage[];
|
||||
findDuplicateLabel: () => string | null;
|
||||
question: TSurveyMultipleChoiceQuestion;
|
||||
updateQuestion: (questionIdx: number, updatedAttributes: Partial<TSurveyMultipleChoiceQuestion>) => void;
|
||||
surveyLanguageCodes: string[];
|
||||
@@ -47,10 +44,8 @@ export const SelectQuestionChoice = ({
|
||||
questionIdx,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
setisInvalidValue,
|
||||
surveyLanguages,
|
||||
updateChoice,
|
||||
findDuplicateLabel,
|
||||
question,
|
||||
surveyLanguageCodes,
|
||||
updateQuestion,
|
||||
@@ -83,15 +78,6 @@ export const SelectQuestionChoice = ({
|
||||
localSurvey={localSurvey}
|
||||
questionIdx={questionIdx}
|
||||
value={choice.label}
|
||||
onBlur={() => {
|
||||
const duplicateLabel = findDuplicateLabel();
|
||||
if (duplicateLabel) {
|
||||
toast.error("Duplicate choices");
|
||||
setisInvalidValue(duplicateLabel);
|
||||
} else {
|
||||
setisInvalidValue(null);
|
||||
}
|
||||
}}
|
||||
updateChoice={updateChoice}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TMembershipRole } from "@formbricks/types/memberships";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { HowToSendCard } from "./HowToSendCard";
|
||||
import { RecontactOptionsCard } from "./RecontactOptionsCard";
|
||||
import { ResponseOptionsCard } from "./ResponseOptionsCard";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
|
||||
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";
|
||||
import { TSurvey, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { AlertDialog } from "@formbricks/ui/AlertDialog";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TMembershipRole } from "@formbricks/types/memberships";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import { TSurvey, TSurveyEditorTabs, TSurveyStyling } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyEditorTabs, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { PreviewSurvey } from "@formbricks/ui/PreviewSurvey";
|
||||
import { refetchProductAction } from "../actions";
|
||||
import { LoadingSkeleton } from "./LoadingSkeleton";
|
||||
|
||||
@@ -7,10 +7,11 @@ import { useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { createSegmentAction } from "@formbricks/ee/advanced-targeting/lib/actions";
|
||||
import { getLanguageLabel } from "@formbricks/lib/i18n/utils";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import { TSurvey, TSurveyEditorTabs } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyEditorTabs, TSurveyQuestion, ZSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AlertDialog } from "@formbricks/ui/AlertDialog";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
@@ -25,7 +26,7 @@ interface SurveyMenuBarProps {
|
||||
environment: TEnvironment;
|
||||
activeId: TSurveyEditorTabs;
|
||||
setActiveId: React.Dispatch<React.SetStateAction<TSurveyEditorTabs>>;
|
||||
setInvalidQuestions: (invalidQuestions: string[]) => void;
|
||||
setInvalidQuestions: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
product: TProduct;
|
||||
responseCount: number;
|
||||
selectedLanguageCode: string;
|
||||
@@ -43,7 +44,6 @@ export const SurveyMenuBar = ({
|
||||
product,
|
||||
responseCount,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
}: SurveyMenuBarProps) => {
|
||||
const router = useRouter();
|
||||
const [audiencePrompt, setAudiencePrompt] = useState(true);
|
||||
@@ -53,8 +53,6 @@ export const SurveyMenuBar = ({
|
||||
const [isSurveySaving, setIsSurveySaving] = useState(false);
|
||||
const cautionText = "This survey received responses.";
|
||||
|
||||
const faultyQuestions: string[] = [];
|
||||
|
||||
useEffect(() => {
|
||||
if (audiencePrompt && activeId === "settings") {
|
||||
setAudiencePrompt(false);
|
||||
@@ -140,20 +138,65 @@ export const SurveyMenuBar = ({
|
||||
return localSurvey.segment;
|
||||
};
|
||||
|
||||
const handleSurveySave = async () => {
|
||||
const validateSurveyWithZod = (): boolean => {
|
||||
const localSurveyValidation = ZSurvey.safeParse(localSurvey);
|
||||
if (!localSurveyValidation.success) {
|
||||
const currentError = localSurveyValidation.error.errors[0];
|
||||
if (currentError.path[0] === "questions") {
|
||||
const questionIdx = currentError.path[1];
|
||||
const question: TSurveyQuestion = localSurvey.questions[questionIdx];
|
||||
if (question) {
|
||||
setInvalidQuestions((prevInvalidQuestions) =>
|
||||
prevInvalidQuestions ? [...prevInvalidQuestions, question.id] : [question.id]
|
||||
);
|
||||
}
|
||||
} else if (currentError.path[0] === "welcomeCard") {
|
||||
setInvalidQuestions((prevInvalidQuestions) =>
|
||||
prevInvalidQuestions ? [...prevInvalidQuestions, "start"] : ["start"]
|
||||
);
|
||||
} else if (currentError.path[0] === "thankYouCard") {
|
||||
setInvalidQuestions((prevInvalidQuestions) =>
|
||||
prevInvalidQuestions ? [...prevInvalidQuestions, "end"] : ["end"]
|
||||
);
|
||||
}
|
||||
|
||||
if (currentError.code === "custom") {
|
||||
const params = currentError.params ?? ({} as { invalidLanguageCodes: string[] });
|
||||
if (params.invalidLanguageCodes && params.invalidLanguageCodes.length) {
|
||||
const invalidLanguageLabels = params.invalidLanguageCodes.map(
|
||||
(invalidLanguage: string) => getLanguageLabel(invalidLanguage) ?? invalidLanguage
|
||||
);
|
||||
|
||||
toast.error(`${currentError.message} ${invalidLanguageLabels.join(", ")}`);
|
||||
} else {
|
||||
toast.error(currentError.message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
toast.error(currentError.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSurveySave = async (): Promise<boolean> => {
|
||||
setIsSurveySaving(true);
|
||||
|
||||
const isSurveyValidatedWithZod = validateSurveyWithZod();
|
||||
|
||||
if (!isSurveyValidatedWithZod) {
|
||||
setIsSurveySaving(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (
|
||||
!isSurveyValid(
|
||||
localSurvey,
|
||||
faultyQuestions,
|
||||
setInvalidQuestions,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode
|
||||
)
|
||||
) {
|
||||
const isSurveyValidResult = isSurveyValid(localSurvey, selectedLanguageCode);
|
||||
if (!isSurveyValidResult) {
|
||||
setIsSurveySaving(false);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
localSurvey.questions = localSurvey.questions.map((question) => {
|
||||
@@ -168,31 +211,36 @@ export const SurveyMenuBar = ({
|
||||
setLocalSurvey(updatedSurvey);
|
||||
|
||||
toast.success("Changes saved.");
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setIsSurveySaving(false);
|
||||
toast.error(`Error saving changes`);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveAndGoBack = async () => {
|
||||
await handleSurveySave();
|
||||
router.back();
|
||||
const isSurveySaved = await handleSurveySave();
|
||||
if (isSurveySaved) {
|
||||
router.back();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSurveyPublish = async () => {
|
||||
setIsSurveyPublishing(true);
|
||||
|
||||
const isSurveyValidatedWithZod = validateSurveyWithZod();
|
||||
|
||||
if (!isSurveyValidatedWithZod) {
|
||||
setIsSurveyPublishing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (
|
||||
!isSurveyValid(
|
||||
localSurvey,
|
||||
faultyQuestions,
|
||||
setInvalidQuestions,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode
|
||||
)
|
||||
) {
|
||||
const isSurveyValidResult = isSurveyValid(localSurvey, selectedLanguageCode);
|
||||
if (!isSurveyValidResult) {
|
||||
setIsSurveyPublishing(false);
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +310,8 @@ export const SurveyMenuBar = ({
|
||||
variant="secondary"
|
||||
className="mr-3"
|
||||
loading={isSurveySaving}
|
||||
onClick={() => handleSurveySave()}>
|
||||
onClick={() => handleSurveySave()}
|
||||
type="submit">
|
||||
Save
|
||||
</Button>
|
||||
{localSurvey.status !== "draft" && (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { CheckIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { TPlacement } from "@formbricks/types/common";
|
||||
import { TSurvey, TSurveyProductOverwrites } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyProductOverwrites } from "@formbricks/types/surveys/types";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { Switch } from "@formbricks/ui/Switch";
|
||||
import { Placement } from "./Placement";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
|
||||
import { isAdvancedSegment } from "@formbricks/lib/segment/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TBaseFilter, TSegment, TSegmentCreateInput, TSegmentUpdateInput } from "@formbricks/types/segment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AlertDialog } from "@formbricks/ui/AlertDialog";
|
||||
import { BasicAddFilterModal } from "@formbricks/ui/BasicAddFilterModal";
|
||||
import { BasicSegmentEditor } from "@formbricks/ui/BasicSegmentEditor";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { SearchIcon } from "lucide-react";
|
||||
import UnsplashImage from "next/image";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TSurveyBackgroundBgType } from "@formbricks/types/surveys";
|
||||
import { TSurveyBackgroundBgType } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
import { LoadingSpinner } from "@formbricks/ui/LoadingSpinner";
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
import { validateId } from "@formbricks/types/surveys/validation";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
import { validateId } from "../lib/validation";
|
||||
|
||||
interface UpdateQuestionIdProps {
|
||||
localSurvey: TSurvey;
|
||||
@@ -35,14 +35,20 @@ export const UpdateQuestionId = ({
|
||||
|
||||
const questionIds = localSurvey.questions.map((q) => q.id);
|
||||
const hiddenFieldIds = localSurvey.hiddenFields.fieldIds ?? [];
|
||||
if (validateId("Question", currentValue, questionIds, hiddenFieldIds)) {
|
||||
setIsInputInvalid(false);
|
||||
toast.success("Question ID updated.");
|
||||
updateQuestion(questionIdx, { id: currentValue });
|
||||
setPrevValue(currentValue); // after successful update, set current value as previous value
|
||||
} else {
|
||||
|
||||
const validateIdError = validateId("Question", currentValue, questionIds, hiddenFieldIds);
|
||||
|
||||
if (validateIdError) {
|
||||
setIsInputInvalid(true);
|
||||
toast.error(validateIdError);
|
||||
setCurrentValue(prevValue);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsInputInvalid(false);
|
||||
toast.success("Question ID updated.");
|
||||
updateQuestion(questionIdx, { id: currentValue });
|
||||
setPrevValue(currentValue); // after successful update, set current value as previous value
|
||||
};
|
||||
|
||||
const isButtonDisabled = () => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { TActionClass } from "@formbricks/types/actionClasses";
|
||||
import { TMembershipRole } from "@formbricks/types/memberships";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AdvancedOptionToggle } from "@formbricks/ui/AdvancedOptionToggle";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
@@ -86,7 +86,9 @@ export const WhenToSendCard = ({
|
||||
const handleInputSeconds = (e: any) => {
|
||||
let value = parseInt(e.target.value);
|
||||
|
||||
if (value < 1) value = 1;
|
||||
if (value < 1 || Number.isNaN(value)) {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
const updatedSurvey = { ...localSurvey, autoClose: value };
|
||||
setLocalSurvey(updatedSurvey);
|
||||
@@ -94,27 +96,28 @@ export const WhenToSendCard = ({
|
||||
|
||||
const handleTriggerDelay = (e: any) => {
|
||||
let value = parseInt(e.target.value);
|
||||
|
||||
if (value < 1 || Number.isNaN(value)) {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
const updatedSurvey = { ...localSurvey, delay: value };
|
||||
setLocalSurvey(updatedSurvey);
|
||||
};
|
||||
|
||||
const handleRandomizerInput = (e) => {
|
||||
let value: number | null = null;
|
||||
let value = parseFloat(e.target.value);
|
||||
|
||||
if (e.target.value !== "") {
|
||||
value = parseFloat(e.target.value);
|
||||
|
||||
if (Number.isNaN(value)) {
|
||||
value = 1;
|
||||
}
|
||||
|
||||
if (value < 0.01) value = 0.01;
|
||||
if (value > 100) value = 100;
|
||||
|
||||
// Round value to two decimal places. eg: 10.555(and higher like 10.556) -> 10.56 and 10.554(and lower like 10.553) ->10.55
|
||||
value = Math.round(value * 100) / 100;
|
||||
if (Number.isNaN(value)) {
|
||||
value = 0.01;
|
||||
}
|
||||
|
||||
if (value < 0.01) value = 0.01;
|
||||
if (value > 100) value = 100;
|
||||
|
||||
// Round value to two decimal places. eg: 10.555(and higher like 10.556) -> 10.56 and 10.554(and lower like 10.553) ->10.55
|
||||
value = Math.round(value * 100) / 100;
|
||||
|
||||
const updatedSurvey = { ...localSurvey, displayPercentage: value };
|
||||
setLocalSurvey(updatedSurvey);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// extend this object in order to add more validation rules
|
||||
import { isEqual } from "lodash";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { extractLanguageCodes, getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { checkForEmptyFallBackValue } from "@formbricks/lib/utils/recall";
|
||||
@@ -15,11 +14,10 @@ import {
|
||||
TSurveyOpenTextQuestion,
|
||||
TSurveyPictureSelectionQuestion,
|
||||
TSurveyQuestion,
|
||||
TSurveyQuestionTypeEnum,
|
||||
TSurveyQuestions,
|
||||
TSurveyThankYouCard,
|
||||
TSurveyWelcomeCard,
|
||||
} from "@formbricks/types/surveys";
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { findLanguageCodesForDuplicateLabels } from "@formbricks/types/surveys/validation";
|
||||
|
||||
// Utility function to check if label is valid for all required languages
|
||||
export const isLabelValidForAllLanguages = (
|
||||
@@ -39,22 +37,16 @@ const handleI18nCheckForMultipleChoice = (
|
||||
question: TSurveyMultipleChoiceQuestion,
|
||||
languages: TSurveyLanguage[]
|
||||
): boolean => {
|
||||
return question.choices.every((choice) => isLabelValidForAllLanguages(choice.label, languages));
|
||||
};
|
||||
const invalidLangCodes = findLanguageCodesForDuplicateLabels(
|
||||
question.choices.map((choice) => choice.label),
|
||||
languages
|
||||
);
|
||||
|
||||
const hasDuplicates = (labels: TI18nString[]) => {
|
||||
const flattenedLabels = labels
|
||||
.map((label) =>
|
||||
Object.keys(label)
|
||||
.map((lang) => {
|
||||
const text = label[lang].trim().toLowerCase();
|
||||
return text && `${lang}:${text}`;
|
||||
})
|
||||
.filter((text) => text)
|
||||
)
|
||||
.flat();
|
||||
const uniqueLabels = new Set(flattenedLabels);
|
||||
return uniqueLabels.size !== flattenedLabels.length;
|
||||
if (invalidLangCodes.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return question.choices.every((choice) => isLabelValidForAllLanguages(choice.label, languages));
|
||||
};
|
||||
|
||||
const handleI18nCheckForMatrixLabels = (
|
||||
@@ -63,13 +55,13 @@ const handleI18nCheckForMatrixLabels = (
|
||||
): boolean => {
|
||||
const rowsAndColumns = [...question.rows, ...question.columns];
|
||||
|
||||
if (hasDuplicates(question.rows)) {
|
||||
const invalidRowsLangCodes = findLanguageCodesForDuplicateLabels(question.rows, languages);
|
||||
const invalidColumnsLangCodes = findLanguageCodesForDuplicateLabels(question.columns, languages);
|
||||
|
||||
if (invalidRowsLangCodes.length > 0 || invalidColumnsLangCodes.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasDuplicates(question.columns)) {
|
||||
return false;
|
||||
}
|
||||
return rowsAndColumns.every((label) => isLabelValidForAllLanguages(label, languages));
|
||||
};
|
||||
|
||||
@@ -192,281 +184,13 @@ export const isCardValid = (
|
||||
);
|
||||
};
|
||||
|
||||
export const isValidUrl = (string: string): boolean => {
|
||||
try {
|
||||
new URL(string);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to validate question ID and Hidden field Id
|
||||
export const validateId = (
|
||||
type: "Hidden field" | "Question",
|
||||
field: string,
|
||||
existingQuestionIds: string[],
|
||||
existingHiddenFieldIds: string[]
|
||||
): boolean => {
|
||||
if (field.trim() === "") {
|
||||
toast.error(`Please enter a ${type} Id.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const combinedIds = [...existingQuestionIds, ...existingHiddenFieldIds];
|
||||
|
||||
if (combinedIds.findIndex((id) => id.toLowerCase() === field.toLowerCase()) !== -1) {
|
||||
toast.error(`${type} Id already exists in questions or hidden fields.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const forbiddenIds = [
|
||||
"userId",
|
||||
"source",
|
||||
"suid",
|
||||
"end",
|
||||
"start",
|
||||
"welcomeCard",
|
||||
"hidden",
|
||||
"verifiedEmail",
|
||||
"multiLanguage",
|
||||
"embed",
|
||||
];
|
||||
if (forbiddenIds.includes(field)) {
|
||||
toast.error(`${type} Id not allowed.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (field.includes(" ")) {
|
||||
toast.error(`${type} Id not allowed, avoid using spaces.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(field)) {
|
||||
toast.error(`${type} Id not allowed, use only alphanumeric characters, hyphens, or underscores.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Checks if there is a cycle present in the survey data logic and returns all questions responsible for the cycle.
|
||||
export const findQuestionsWithCyclicLogic = (questions: TSurveyQuestions): string[] => {
|
||||
const visited: Record<string, boolean> = {};
|
||||
const recStack: Record<string, boolean> = {};
|
||||
const cyclicQuestions: Set<string> = new Set();
|
||||
|
||||
const checkForCyclicLogic = (questionId: string): boolean => {
|
||||
if (!visited[questionId]) {
|
||||
visited[questionId] = true;
|
||||
recStack[questionId] = true;
|
||||
|
||||
const question = questions.find((question) => question.id === questionId);
|
||||
if (question && question.logic && question.logic.length > 0) {
|
||||
for (const logic of question.logic) {
|
||||
const destination = logic.destination;
|
||||
if (!destination) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!visited[destination] && checkForCyclicLogic(destination)) {
|
||||
cyclicQuestions.add(questionId);
|
||||
return true;
|
||||
} else if (recStack[destination]) {
|
||||
cyclicQuestions.add(questionId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle default behavior
|
||||
const nextQuestionIndex = questions.findIndex((question) => question.id === questionId) + 1;
|
||||
const nextQuestion = questions[nextQuestionIndex];
|
||||
if (nextQuestion && !visited[nextQuestion.id] && checkForCyclicLogic(nextQuestion.id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recStack[questionId] = false;
|
||||
return false;
|
||||
};
|
||||
|
||||
for (const question of questions) {
|
||||
const questionId = question.id;
|
||||
checkForCyclicLogic(questionId);
|
||||
}
|
||||
|
||||
return Array.from(cyclicQuestions);
|
||||
};
|
||||
|
||||
export const isSurveyValid = (
|
||||
survey: TSurvey,
|
||||
faultyQuestions: string[],
|
||||
setInvalidQuestions: (questions: string[]) => void,
|
||||
selectedLanguageCode: string,
|
||||
setSelectedLanguageCode: (languageCode: string) => void
|
||||
) => {
|
||||
const existingQuestionIds = new Set();
|
||||
|
||||
// Ensuring at least one question is added to the survey.
|
||||
if (survey.questions.length === 0) {
|
||||
toast.error("Please add at least one question");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checking the validity of the welcome and thank-you cards if they are enabled.
|
||||
if (survey.welcomeCard.enabled) {
|
||||
if (!isCardValid(survey.welcomeCard, "start", survey.languages)) {
|
||||
faultyQuestions.push("start");
|
||||
}
|
||||
}
|
||||
|
||||
if (survey.thankYouCard.enabled) {
|
||||
if (!isCardValid(survey.thankYouCard, "end", survey.languages)) {
|
||||
faultyQuestions.push("end");
|
||||
}
|
||||
}
|
||||
|
||||
// Verifying that any provided PIN is exactly four digits long.
|
||||
const pin = survey.pin;
|
||||
if (pin && pin.toString().length !== 4) {
|
||||
toast.error("PIN must be a four digit number.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assessing each question for completeness and correctness,
|
||||
for (let index = 0; index < survey.questions.length; index++) {
|
||||
const question = survey.questions[index];
|
||||
const isFirstQuestion = index === 0;
|
||||
const isValid = validateQuestion(question, survey.languages, isFirstQuestion);
|
||||
|
||||
if (!isValid) {
|
||||
faultyQuestions.push(question.id);
|
||||
}
|
||||
}
|
||||
|
||||
// if there are any faulty questions, the user won't be allowed to save the survey
|
||||
if (faultyQuestions.length > 0) {
|
||||
setInvalidQuestions(faultyQuestions);
|
||||
setSelectedLanguageCode("default");
|
||||
toast.error("Please check for empty fields or duplicate labels");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const question of survey.questions) {
|
||||
const existingLogicConditions = new Set();
|
||||
|
||||
if (existingQuestionIds.has(question.id)) {
|
||||
toast.error("There are 2 identical question IDs. Please update one.");
|
||||
return false;
|
||||
}
|
||||
existingQuestionIds.add(question.id);
|
||||
|
||||
if (
|
||||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceSingle ||
|
||||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceMulti
|
||||
) {
|
||||
const haveSameChoices =
|
||||
question.choices.some((element) => element.label[selectedLanguageCode]?.trim() === "") ||
|
||||
question.choices.some((element, index) =>
|
||||
question.choices
|
||||
.slice(index + 1)
|
||||
.some(
|
||||
(nextElement) =>
|
||||
nextElement.label[selectedLanguageCode]?.trim() === element.label[selectedLanguageCode].trim()
|
||||
)
|
||||
);
|
||||
|
||||
if (haveSameChoices) {
|
||||
toast.error("You have empty or duplicate choices.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const logic of question.logic || []) {
|
||||
const validFields = ["condition", "destination", "value"].filter(
|
||||
(field) => logic[field] !== undefined
|
||||
).length;
|
||||
|
||||
if (validFields < 2) {
|
||||
setInvalidQuestions([question.id]);
|
||||
toast.error("Incomplete logic jumps detected: Fill or remove them in the Questions tab.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (question.required && logic.condition === "skipped") {
|
||||
toast.error("A logic condition is missing: Please update or delete it in the Questions tab.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const thisLogic = `${logic.condition}-${logic.value}`;
|
||||
if (existingLogicConditions.has(thisLogic)) {
|
||||
setInvalidQuestions([question.id]);
|
||||
toast.error(
|
||||
"There are two competing logic conditons: Please update or delete one in the Questions tab."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
existingLogicConditions.add(thisLogic);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking the validity of redirection URLs to ensure they are properly formatted.
|
||||
if (
|
||||
survey.redirectUrl &&
|
||||
!survey.redirectUrl.includes("https://") &&
|
||||
!survey.redirectUrl.includes("http://")
|
||||
) {
|
||||
toast.error("Please enter a valid URL for redirecting respondents.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the user segment filters
|
||||
const localSurveySegment = {
|
||||
id: survey.segment?.id,
|
||||
filters: survey.segment?.filters,
|
||||
title: survey.segment?.title,
|
||||
description: survey.segment?.description,
|
||||
};
|
||||
|
||||
const surveySegment = {
|
||||
id: survey.segment?.id,
|
||||
filters: survey.segment?.filters,
|
||||
title: survey.segment?.title,
|
||||
description: survey.segment?.description,
|
||||
};
|
||||
|
||||
// if the non-private segment in the survey and the strippedSurvey are different, don't save
|
||||
if (!survey.segment?.isPrivate && !isEqual(localSurveySegment, surveySegment)) {
|
||||
toast.error("Please save the audience filters before saving the survey");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!!survey.segment?.filters?.length) {
|
||||
const parsedFilters = ZSegmentFilters.safeParse(survey.segment.filters);
|
||||
if (!parsedFilters.success) {
|
||||
const errMsg =
|
||||
parsedFilters.error.issues.find((issue) => issue.code === "custom")?.message ||
|
||||
"Invalid targeting: Please check your audience filters";
|
||||
toast.error(errMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const isSurveyValid = (survey: TSurvey, selectedLanguageCode: string) => {
|
||||
const questionWithEmptyFallback = checkForEmptyFallBackValue(survey, selectedLanguageCode);
|
||||
if (questionWithEmptyFallback) {
|
||||
toast.error("Fallback missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detecting any cyclic dependencies in survey logic.
|
||||
const questionsWithCyclicLogic = findQuestionsWithCyclicLogic(survey.questions);
|
||||
if (questionsWithCyclicLogic.length > 0) {
|
||||
setInvalidQuestions(questionsWithCyclicLogic);
|
||||
toast.error("Cyclic logic detected. Please fix it before saving.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (survey.type === "app" && survey.segment?.id === "temp") {
|
||||
const { filters } = survey.segment;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const minimalSurvey: TSurvey = {
|
||||
id: "someUniqueId1",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getResponsesByPersonId } from "@formbricks/lib/response/service";
|
||||
import { getSurveys } from "@formbricks/lib/survey/service";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
|
||||
interface ResponseSectionProps {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useEffect, useState } from "react";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TResponse } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TResponse } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { EmptySpaceFiller } from "@formbricks/ui/EmptySpaceFiller";
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
TIntegrationAirtableInput,
|
||||
TIntegrationAirtableTables,
|
||||
} from "@formbricks/types/integration/airtable";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AdditionalIntegrationSettings } from "@formbricks/ui/AdditionalIntegrationSettings";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@formbricks/ui/Alert";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
@@ -8,7 +8,7 @@ import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { ConnectIntegration } from "@formbricks/ui/ConnectIntegration";
|
||||
|
||||
interface AirtableWrapperProps {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { DeleteDialog } from "@formbricks/ui/DeleteDialog";
|
||||
import { EmptySpaceFiller } from "@formbricks/ui/EmptySpaceFiller";
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TIntegrationGoogleSheetsConfigData,
|
||||
TIntegrationGoogleSheetsInput,
|
||||
} from "@formbricks/types/integration/googleSheet";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AdditionalIntegrationSettings } from "@formbricks/ui/AdditionalIntegrationSettings";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Checkbox } from "@formbricks/ui/Checkbox";
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
TIntegrationGoogleSheets,
|
||||
TIntegrationGoogleSheetsConfigData,
|
||||
} from "@formbricks/types/integration/googleSheet";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { ConnectIntegration } from "@formbricks/ui/ConnectIntegration";
|
||||
import { AddIntegrationModal } from "./AddIntegrationModal";
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
TIntegrationNotionConfigData,
|
||||
TIntegrationNotionDatabase,
|
||||
} from "@formbricks/types/integration/notion";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { DropdownSelector } from "@formbricks/ui/DropdownSelector";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
TIntegrationNotionConfigData,
|
||||
TIntegrationNotionDatabase,
|
||||
} from "@formbricks/types/integration/notion";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { ConnectIntegration } from "@formbricks/ui/ConnectIntegration";
|
||||
import { authorize } from "../lib/notion";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const TYPE_MAPPING = {
|
||||
[TSurveyQuestionTypeEnum.CTA]: ["checkbox"],
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
TIntegrationSlackConfigData,
|
||||
TIntegrationSlackInput,
|
||||
} from "@formbricks/types/integration/slack";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { AdditionalIntegrationSettings } from "@formbricks/ui/AdditionalIntegrationSettings";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Checkbox } from "@formbricks/ui/Checkbox";
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import { TIntegrationSlack, TIntegrationSlackConfigData } from "@formbricks/types/integration/slack";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { ConnectIntegration } from "@formbricks/ui/ConnectIntegration";
|
||||
|
||||
interface SlackWrapperProps {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Webhook } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { AddWebhookModal } from "./AddWebhookModal";
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { TPipelineTrigger } from "@formbricks/types/pipelines";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TWebhookInput } from "@formbricks/types/webhooks";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Checkbox } from "@formbricks/ui/Checkbox";
|
||||
|
||||
interface SurveyCheckboxGroupProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WebhookOverviewTab } from "@/app/(app)/environments/[environmentId]/integrations/webhooks/components/WebhookOverviewTab";
|
||||
import { WebhookSettingsTab } from "@/app/(app)/environments/[environmentId]/integrations/webhooks/components/WebhookSettingsTab";
|
||||
import { Webhook } from "lucide-react";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TWebhook } from "@formbricks/types/webhooks";
|
||||
import { ModalWithTabs } from "@formbricks/ui/ModalWithTabs";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { convertDateTimeStringShort } from "@formbricks/lib/time";
|
||||
import { capitalizeFirstLetter } from "@formbricks/lib/utils/strings";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TWebhook } from "@formbricks/types/webhooks";
|
||||
import { Label } from "@formbricks/ui/Label";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { timeSinceConditionally } from "@formbricks/lib/time";
|
||||
import { capitalizeFirstLetter } from "@formbricks/lib/utils/strings";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TWebhook } from "@formbricks/types/webhooks";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { TPipelineTrigger } from "@formbricks/types/pipelines";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TWebhook, TWebhookInput } from "@formbricks/types/webhooks";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { DeleteDialog } from "@formbricks/ui/DeleteDialog";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { WebhookModal } from "@/app/(app)/environments/[environmentId]/integrations/webhooks/components/WebhookDetailModal";
|
||||
import { useState } from "react";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TWebhook } from "@formbricks/types/webhooks";
|
||||
import { EmptySpaceFiller } from "@formbricks/ui/EmptySpaceFiller";
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SubmitHandler, UseFormReturn, useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { COLOR_DEFAULTS, PREVIEW_SURVEY } from "@formbricks/lib/styling/constants";
|
||||
import { TProduct, TProductStyling, ZProductStyling } from "@formbricks/types/product";
|
||||
import { TSurvey, TSurveyStyling, TSurveyType } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyStyling, TSurveyType } from "@formbricks/types/surveys/types";
|
||||
import { AlertDialog } from "@formbricks/ui/AlertDialog";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Variants, motion } from "framer-motion";
|
||||
import { useRef, useState } from "react";
|
||||
import type { TProduct } from "@formbricks/types/product";
|
||||
import { TSurvey, TSurveyType } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyType } from "@formbricks/types/surveys/types";
|
||||
import { ClientLogo } from "@formbricks/ui/ClientLogo";
|
||||
import { MediaBackground } from "@formbricks/ui/MediaBackground";
|
||||
import { Modal } from "@formbricks/ui/PreviewSurvey/components/Modal";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getResponseCountBySurveyId, getResponses, getSurveySummary } from "@for
|
||||
import { canUserAccessSurvey } from "@formbricks/lib/survey/auth";
|
||||
import { AuthorizationError } from "@formbricks/types/errors";
|
||||
import { TResponse, TResponseFilterCriteria } from "@formbricks/types/responses";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const revalidateSurveyIdPath = async (environmentId: string, surveyId: string) => {
|
||||
revalidatePath(`/environments/${environmentId}/surveys/${surveyId}`);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { useParams, useSearchParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TResponse } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getMembershipByUserIdOrganizationIdAction } from "@formbricks/lib/membe
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TResponse } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { EmptySpaceFiller } from "@formbricks/ui/EmptySpaceFiller";
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from "next/link";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys/types";
|
||||
import { AddressResponse } from "@formbricks/ui/AddressResponse";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryCta } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryCta } from "@formbricks/types/surveys/types";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryCal } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryCal } from "@formbricks/types/surveys/types";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryConsent } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryConsent } from "@formbricks/types/surveys/types";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { formatDateWithOrdinal } from "@formbricks/lib/utils/datetime";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryDate } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryDate } from "@formbricks/types/surveys/types";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { getOriginalFileNameFromUrl } from "@formbricks/lib/storage/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys/types";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState } from "react";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurveyQuestionSummaryHiddenFields } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestionSummaryHiddenFields } from "@formbricks/types/surveys/types";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryMatrix } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryMatrix } from "@formbricks/types/surveys/types";
|
||||
import { TooltipRenderer } from "@formbricks/ui/Tooltip";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryMultipleChoice, TSurveyType } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryMultipleChoice, TSurveyType } from "@formbricks/types/surveys/types";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryNps } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryNps } from "@formbricks/types/surveys/types";
|
||||
import { HalfCircle, ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState } from "react";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys/types";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Image from "next/image";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryPictureSelection } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryPictureSelection } from "@formbricks/types/surveys/types";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { questionTypes } from "@/app/lib/questions";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import { recallToHeadline } from "@formbricks/lib/utils/recall";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummary } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummary } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface HeadProps {
|
||||
questionSummary: TSurveyQuestionSummary;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]
|
||||
import { CircleSlash2, SmileIcon, StarIcon } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryRating } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionSummaryRating } from "@formbricks/types/surveys/types";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { RatingResponse } from "@formbricks/ui/RatingResponse";
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { BellRing, BlocksIcon, Code2Icon, LinkIcon, MailIcon, UsersRound } from
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
import { Dialog, DialogContent } from "@formbricks/ui/Dialog";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { Confetti } from "@formbricks/ui/Confetti";
|
||||
import { ShareEmbedSurvey } from "./ShareEmbedSurvey";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TimerIcon } from "lucide-react";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
|
||||
|
||||
interface SummaryDropOffsProps {
|
||||
|
||||
@@ -13,9 +13,9 @@ import { PictureChoiceSummary } from "@/app/(app)/environments/[environmentId]/s
|
||||
import { RatingSummary } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/RatingSummary";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { EmptySpaceFiller } from "@formbricks/ui/EmptySpaceFiller";
|
||||
import { SkeletonLoader } from "@formbricks/ui/SkeletonLoader";
|
||||
import { AddressSummary } from "./AddressSummary";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
|
||||
|
||||
interface SummaryMetadataProps {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey, TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { SummaryList } from "./SummaryList";
|
||||
import { SummaryMetadata } from "./SummaryMetadata";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SurveyStatusDropdown } from "@/app/(app)/environments/[environmentId]/s
|
||||
import { ShareIcon, SquarePenIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UrlShortenerForm } from "@/app/(app)/environments/[environmentId]/components/UrlShortenerForm";
|
||||
import Link from "next/link";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { ShareSurveyLink } from "@formbricks/ui/ShareSurveyLink";
|
||||
|
||||
interface LinkTabProps {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { updateSurvey } from "@formbricks/lib/survey/service";
|
||||
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
|
||||
import { AuthorizationError } from "@formbricks/types/errors";
|
||||
import { TResponseFilterCriteria } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const getResponsesDownloadUrlAction = async (
|
||||
surveyId: string,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useClickOutside } from "@formbricks/lib/utils/hooks/useClickOutside";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Calendar } from "@formbricks/ui/Calendar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ChevronDown, ChevronUp, X } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { useClickOutside } from "@formbricks/lib/utils/hooks/useClickOutside";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandItem, CommandList } from "@formbricks/ui/Command";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import * as React from "react";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { useClickOutside } from "@formbricks/lib/utils/hooks/useClickOutside";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
|
||||
@@ -14,7 +14,7 @@ import { TrashIcon } from "lucide-react";
|
||||
import { ChevronDown, ChevronUp, Plus } from "lucide-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Checkbox } from "@formbricks/ui/Checkbox";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/Popover";
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { CopyIcon, DownloadIcon, GlobeIcon, LinkIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CheckCircle2Icon, PauseCircleIcon, PlayCircleIcon } from "lucide-react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui/Select";
|
||||
import { SurveyStatusIndicator } from "@formbricks/ui/SurveyStatusIndicator";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { TIntegrationNotion, TIntegrationNotionConfigData } from "@formbricks/ty
|
||||
import { TIntegrationSlack } from "@formbricks/types/integration/slack";
|
||||
import { TPipelineInput } from "@formbricks/types/pipelines";
|
||||
import { TResponseMeta } from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
|
||||
const convertMetaObjectToString = (metadata: TResponseMeta): string => {
|
||||
let result: string[] = [];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user