question id can now be changed in advanced settings

This commit is contained in:
Matthias Nannt
2023-04-17 12:19:52 +02:00
parent c118fbb0bd
commit a7fd1fc754
4 changed files with 95 additions and 5 deletions

View File

@@ -45,9 +45,11 @@ Formbricks helps you apply best practices from data-driven work and experience m
| --- | --------------------------------------------- |
| 👷 | Multiple-Choice Multi-Select Question Type |
| 👷 | NPS Question Type |
| 👷 | Filter Audience by Attributes |
| 👷 | Share Surveys via Link |
| 🗒️ | Rating Scale (Numbers + Emojis) Question Type |
| 🗒️ | Filter Audience by Attributes |
| 🗒️ | Share Surveys via Link |
| 🗒️ | Advanced Response Filtering & Analysis |
| 🗒️ | Zapier, Slack & Posthog Integration |
_👷 In Progress | 🗒️ Up Next_

View File

@@ -1,18 +1,21 @@
"use client";
import { Label } from "@formbricks/ui";
import { Switch } from "@formbricks/ui";
import { getQuestionTypeName } from "@/lib/questions";
import { cn } from "@formbricks/lib/cn";
import type { Question } from "@formbricks/types/questions";
import type { Survey } from "@formbricks/types/surveys";
import { Label, Switch } from "@formbricks/ui";
import { Bars3BottomLeftIcon } from "@heroicons/react/24/solid";
import * as Collapsible from "@radix-ui/react-collapsible";
import { useState } from "react";
import { Draggable } from "react-beautiful-dnd";
import MultipleChoiceSingleForm from "./MultipleChoiceSingleForm";
import OpenQuestionForm from "./OpenQuestionForm";
import QuestionDropdown from "./QuestionDropdown";
import UpdateQuestionId from "./UpdateQuestionId";
interface QuestionCardProps {
localSurvey: Survey;
question: Question;
questionIdx: number;
updateQuestion: (questionIdx: number, updatedAttributes: any) => void;
@@ -23,6 +26,7 @@ interface QuestionCardProps {
}
export default function QuestionCard({
localSurvey,
question,
questionIdx,
updateQuestion,
@@ -32,6 +36,7 @@ export default function QuestionCard({
lastQuestion,
}: QuestionCardProps) {
const open = activeQuestionId === question.id;
const [openAdvanced, setOpenAdvanced] = useState(false);
return (
<Draggable draggableId={question.id} index={questionIdx}>
{(provided) => (
@@ -108,6 +113,24 @@ export default function QuestionCard({
lastQuestion={lastQuestion}
/>
) : null}
<div className="mt-4 border-t border-slate-200">
<Collapsible.Root open={openAdvanced} onOpenChange={setOpenAdvanced} className="mt-3">
<Collapsible.CollapsibleTrigger className="text-sm text-slate-800">
{openAdvanced ? "Hide Advanced Settings" : "Show Advanced Settings"}
</Collapsible.CollapsibleTrigger>
<Collapsible.CollapsibleContent className="space-y-2">
<div className="mt-3">
<UpdateQuestionId
question={question}
questionIdx={questionIdx}
localSurvey={localSurvey}
updateQuestion={updateQuestion}
/>
</div>
</Collapsible.CollapsibleContent>
</Collapsible.Root>
</div>
</Collapsible.CollapsibleContent>
</Collapsible.Root>
</div>

View File

@@ -6,6 +6,8 @@ import AddQuestionButton from "./AddQuestionButton";
import QuestionCard from "./QuestionCard";
import { StrictModeDroppable } from "./StrictModeDroppable";
import EditThankYouCard from "./EditThankYouCard";
import { createId } from "@paralleldrive/cuid2";
import { useMemo } from "react";
interface QuestionsViewProps {
localSurvey: Survey;
@@ -20,6 +22,13 @@ export default function QuestionsView({
localSurvey,
setLocalSurvey,
}: QuestionsViewProps) {
const internalQuestionIdMap = useMemo(() => {
return localSurvey.questions.reduce((acc, question) => {
acc[question.id] = createId();
return acc;
}, {});
}, []);
const updateQuestion = (questionIdx: number, updatedAttributes: any) => {
const updatedSurvey = JSON.parse(JSON.stringify(localSurvey));
updatedSurvey.questions[questionIdx] = {
@@ -27,12 +36,20 @@ export default function QuestionsView({
...updatedAttributes,
};
setLocalSurvey(updatedSurvey);
if ("id" in updatedAttributes) {
// relink the question to internal Id
internalQuestionIdMap[updatedAttributes.id] =
internalQuestionIdMap[localSurvey.questions[questionIdx].id];
delete internalQuestionIdMap[localSurvey.questions[questionIdx].id];
setActiveQuestionId(updatedAttributes.id);
}
};
const deleteQuestion = (questionIdx: number) => {
const updatedSurvey = JSON.parse(JSON.stringify(localSurvey));
updatedSurvey.questions.splice(questionIdx, 1);
setLocalSurvey(updatedSurvey);
delete internalQuestionIdMap[localSurvey.questions[questionIdx].id];
};
const addQuestion = (question: any) => {
@@ -40,6 +57,7 @@ export default function QuestionsView({
updatedSurvey.questions.push(question);
setLocalSurvey(updatedSurvey);
setActiveQuestionId(question.id);
internalQuestionIdMap[question.id] = createId();
};
const onDragEnd = (result) => {
@@ -64,7 +82,8 @@ export default function QuestionsView({
{localSurvey.questions.map((question, questionIdx) => (
// display a question form
<QuestionCard
key={question.id}
key={internalQuestionIdMap[question.id]}
localSurvey={localSurvey}
question={question}
questionIdx={questionIdx}
updateQuestion={updateQuestion}

View File

@@ -0,0 +1,46 @@
"use client";
import { Button, Input, Label } from "@formbricks/ui";
import { CheckIcon } from "@heroicons/react/24/solid";
import { useState } from "react";
export default function UpdateQuestionId({ localSurvey, question, questionIdx, updateQuestion }) {
const [currentValue, setCurrentValue] = useState(question.id);
const saveAction = () => {
// check if id is unique
const questionIds = localSurvey.questions.map((q) => q.id);
if (questionIds.includes(currentValue)) {
alert("Question Identifier must be unique within the survey.");
setCurrentValue(question.id);
return;
}
updateQuestion(questionIdx, { id: currentValue });
};
return (
<div>
<Label className="block" htmlFor="questionId">
Internal Question Identifier
</Label>
<div className="mt-2 inline-flex w-full">
<Input
id="questionId"
name="questionId"
value={currentValue}
onChange={(e) => setCurrentValue(e.target.value)}
disabled={localSurvey.status !== "draft"}
/>
{localSurvey.status === "draft" && (
<Button
variant="primary"
className="ml-2 bg-slate-600 text-white hover:bg-slate-700 disabled:bg-slate-400"
onClick={saveAction}
disabled={currentValue === question.id}>
<CheckIcon className="h-4 w-4" />
</Button>
)}
</div>
</div>
);
}