mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-28 17:40:45 -05:00
Improve UX with multiple tweaks (#258)
This commit is contained in:
@@ -34,7 +34,7 @@ export default function MultipleChoiceSingleQuestion({
|
||||
<Subheader subheader={question.subheader} questionId={question.id} />
|
||||
<div className="mt-4">
|
||||
<fieldset>
|
||||
<legend className="sr-only">Choices</legend>
|
||||
<legend className="sr-only">Options</legend>
|
||||
<div className="relative space-y-2 rounded-md">
|
||||
{question.choices &&
|
||||
question.choices.map((choice) => (
|
||||
|
||||
@@ -47,6 +47,7 @@ import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import AddProductModal from "./AddProductModal";
|
||||
import FaveIcon from "@/app/favicon.ico";
|
||||
|
||||
interface EnvironmentsNavbarProps {
|
||||
environmentId: string;
|
||||
@@ -113,7 +114,7 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
links: [
|
||||
{
|
||||
icon: AdjustmentsVerticalIcon,
|
||||
label: "Survey Settings",
|
||||
label: "Product Settings",
|
||||
href: `/environments/${environmentId}/settings/product`,
|
||||
},
|
||||
{
|
||||
@@ -201,9 +202,10 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
<div className="flex h-14 justify-between">
|
||||
<div className="flex space-x-4 py-2">
|
||||
<Link
|
||||
href={`/environments/${environmentId}/surveys/templates`}
|
||||
className="from-brand-light to-brand-dark my-1 flex items-center justify-center rounded-md bg-gradient-to-b px-1 text-white transition-all ease-in-out hover:scale-105">
|
||||
<PlusIcon className="h-6 w-6" />
|
||||
href={`/environments/${environmentId}/surveys/`}
|
||||
className=" flex items-center justify-center rounded-md bg-gradient-to-b text-white transition-all ease-in-out hover:scale-105">
|
||||
{/* <PlusIcon className="h-6 w-6" /> */}
|
||||
<Image src={FaveIcon} width={30} height={30} alt="faveicon" />
|
||||
</Link>
|
||||
{navigation.map((item) => (
|
||||
<Link
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function PricingTable({ environmentId, session }: PricingTablePro
|
||||
</div>
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="float-right -mt-2 mr-6 animate-bounce rounded-full bg-slate-700 px-3 py-1 text-xs font-semibold text-slate-50">
|
||||
<div className="float-right -mt-2 mr-6 rounded-full bg-slate-700 px-3 py-1 text-xs font-semibold text-slate-50">
|
||||
Limited Early Bird Deal
|
||||
</div>
|
||||
<div className="rounded-lg border border-slate-300 bg-slate-100 shadow-sm">
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ export default function AddQuestionButton({ addQuestion, environmentId }: AddQue
|
||||
)}>
|
||||
<Collapsible.CollapsibleTrigger asChild className="group h-full w-full">
|
||||
<div className="inline-flex">
|
||||
<div className="bg-brand-dark flex w-10 items-center justify-center rounded-l-lg group-aria-expanded:rounded-bl-none">
|
||||
<div className="bg-brand-dark flex w-10 items-center justify-center rounded-l-lg group-aria-expanded:rounded-bl-none group-aria-expanded:rounded-br">
|
||||
<PlusIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div className="px-4 py-3">
|
||||
@@ -47,7 +47,7 @@ export default function AddQuestionButton({ addQuestion, environmentId }: AddQue
|
||||
{questionTypes.map((questionType) => (
|
||||
<button
|
||||
key={questionType.id}
|
||||
className="inline-flex items-center px-4 py-2 text-sm font-medium text-slate-700 last:mb-2 hover:bg-slate-100"
|
||||
className="mx-2 inline-flex items-center rounded p-0.5 px-4 py-2 font-medium text-slate-700 last:mb-2 hover:bg-slate-100 hover:text-slate-800"
|
||||
onClick={() => {
|
||||
addQuestion({
|
||||
id: createId(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Survey } from "@formbricks/types/surveys";
|
||||
import HowToSendCard from "./HowToSendCard";
|
||||
import RecontactOptionsCard from "./RecontactOptionsCard";
|
||||
import ResponseOptionsCard from "./ResponseOptionsCard";
|
||||
/* import ResponseOptionsCard from "./ResponseOptionsCard"; */
|
||||
import WhenToSendCard from "./WhenToSendCard";
|
||||
import WhoToSendCard from "./WhoToSendCard";
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function AudienceView({ environmentId, localSurvey, setLocalSurve
|
||||
environmentId={environmentId}
|
||||
/>
|
||||
|
||||
<ResponseOptionsCard />
|
||||
{/* <ResponseOptionsCard /> */}
|
||||
|
||||
<RecontactOptionsCard
|
||||
localSurvey={localSurvey}
|
||||
|
||||
+5
-1
@@ -44,7 +44,11 @@ export default function CTAQuestionForm({
|
||||
onChange={(e) => updateQuestion(questionIdx, { subheader: e.target.value })}
|
||||
/> */}
|
||||
<Editor
|
||||
getText={() => md.render(question.html || "")}
|
||||
getText={() =>
|
||||
md.render(
|
||||
question.html || "We would love to talk to you and learn more about how you use our product."
|
||||
)
|
||||
}
|
||||
setText={(value: string) => {
|
||||
updateQuestion(questionIdx, { html: value });
|
||||
}}
|
||||
|
||||
@@ -25,9 +25,9 @@ const options = [
|
||||
},
|
||||
{
|
||||
id: "link",
|
||||
name: "Shareable Link",
|
||||
name: "Link survey",
|
||||
icon: LinkIcon,
|
||||
description: "Creates a personalized survey link to share around.",
|
||||
description: "Creates a standalone survey to share via link.",
|
||||
comingSoon: false,
|
||||
},
|
||||
{
|
||||
@@ -52,7 +52,7 @@ interface HowToSendCardProps {
|
||||
}
|
||||
|
||||
export default function HowToSendCard({ localSurvey, setLocalSurvey }: HowToSendCardProps) {
|
||||
const [open, setOpen] = useState(true);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const setSurveyType = (type: string) => {
|
||||
const updatedSurvey = JSON.parse(JSON.stringify(localSurvey));
|
||||
@@ -79,7 +79,7 @@ export default function HowToSendCard({ localSurvey, setLocalSurvey }: HowToSend
|
||||
<div>
|
||||
<p className="font-semibold text-slate-800">How to ask</p>
|
||||
<p className="mt-1 truncate text-sm text-slate-500">
|
||||
Choose how you want to reach your audience.
|
||||
In-app survey, link survey or email survey.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ export default function MultipleChoiceMultiForm({
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Label htmlFor="choices">Choices</Label>
|
||||
<Label htmlFor="choices">Options</Label>
|
||||
<div className="mt-2 space-y-2" id="choices">
|
||||
{question.choices &&
|
||||
question.choices.map((choice, choiceIdx) => (
|
||||
@@ -77,7 +77,7 @@ export default function MultipleChoiceMultiForm({
|
||||
id={choice.id}
|
||||
name={choice.id}
|
||||
value={choice.label}
|
||||
placeholder={`Choice ${choiceIdx + 1}`}
|
||||
placeholder={`Option ${choiceIdx + 1}`}
|
||||
onChange={(e) => updateChoice(choiceIdx, { label: e.target.value })}
|
||||
/>
|
||||
{question.choices && question.choices.length > 2 && (
|
||||
@@ -89,7 +89,7 @@ export default function MultipleChoiceMultiForm({
|
||||
</div>
|
||||
))}
|
||||
<Button variant="secondary" type="button" onClick={() => addChoice()}>
|
||||
Add Choice
|
||||
Add Option
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ export default function MultipleChoiceSingleForm({
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Label htmlFor="choices">Choices</Label>
|
||||
<Label htmlFor="choices">Options</Label>
|
||||
<div className="mt-2 space-y-2" id="choices">
|
||||
{question.choices &&
|
||||
question.choices.map((choice, choiceIdx) => (
|
||||
@@ -77,7 +77,7 @@ export default function MultipleChoiceSingleForm({
|
||||
id={choice.id}
|
||||
name={choice.id}
|
||||
value={choice.label}
|
||||
placeholder={`Choice ${choiceIdx + 1}`}
|
||||
placeholder={`Option ${choiceIdx + 1}`}
|
||||
onChange={(e) => updateChoice(choiceIdx, { label: e.target.value })}
|
||||
/>
|
||||
{question.choices && question.choices.length > 2 && (
|
||||
@@ -89,7 +89,7 @@ export default function MultipleChoiceSingleForm({
|
||||
</div>
|
||||
))}
|
||||
<Button variant="secondary" type="button" onClick={() => addChoice()}>
|
||||
Add Choice
|
||||
Add Option
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,13 @@ 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 {
|
||||
ListBulletIcon,
|
||||
ChatBubbleBottomCenterTextIcon,
|
||||
CursorArrowRippleIcon,
|
||||
PresentationChartBarIcon,
|
||||
QueueListIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useState } from "react";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
@@ -72,7 +78,19 @@ export default function QuestionCard({
|
||||
className="flex cursor-pointer justify-between p-4 hover:bg-slate-50">
|
||||
<div>
|
||||
<div className="inline-flex">
|
||||
<Bars3BottomLeftIcon className="-ml-0.5 mr-2 h-5 w-5 text-slate-400" />
|
||||
<div className="-ml-0.5 mr-2 h-5 w-5 text-slate-400">
|
||||
{question.type === "openText" ? (
|
||||
<ChatBubbleBottomCenterTextIcon />
|
||||
) : question.type === "multipleChoiceSingle" ? (
|
||||
<QueueListIcon />
|
||||
) : question.type === "multipleChoiceMulti" ? (
|
||||
<ListBulletIcon />
|
||||
) : question.type === "nps" ? (
|
||||
<PresentationChartBarIcon />
|
||||
) : question.type === "cta" ? (
|
||||
<CursorArrowRippleIcon />
|
||||
) : null}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-semibold">
|
||||
{question.headline || getQuestionTypeName(question.type)}
|
||||
|
||||
@@ -4,7 +4,7 @@ import SurveyStatusDropdown from "@/components/shared/SurveyStatusDropdown";
|
||||
import { useSurveyMutation } from "@/lib/surveys/mutateSurveys";
|
||||
import type { Survey } from "@formbricks/types/surveys";
|
||||
import { Button, Input } from "@formbricks/ui";
|
||||
import { UserGroupIcon } from "@heroicons/react/24/solid";
|
||||
import { UserGroupIcon, ArrowLeftIcon } from "@heroicons/react/24/solid";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -43,6 +43,9 @@ export default function SurveyMenuBar({
|
||||
return (
|
||||
<div className="border-b border-slate-200 bg-white px-5 py-3 sm:flex sm:items-center sm:justify-between">
|
||||
<div className="flex space-x-2 whitespace-nowrap">
|
||||
<Button variant="minimal" className="px-0" onClick={() => router.back()}>
|
||||
<ArrowLeftIcon className="h-5 w-5 text-slate-700" />
|
||||
</Button>
|
||||
<Input
|
||||
defaultValue={localSurvey.name}
|
||||
onChange={(e) => {
|
||||
@@ -60,9 +63,6 @@ export default function SurveyMenuBar({
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex sm:ml-4 sm:mt-0">
|
||||
<Button variant="minimal" className="mr-3" onClick={() => router.back()}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="mr-3"
|
||||
|
||||
+13
-4
@@ -4,7 +4,15 @@ import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { useEventClasses } from "@/lib/eventClasses/eventClasses";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import type { Survey } from "@formbricks/types/surveys";
|
||||
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui";
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
SelectSeparator,
|
||||
} from "@formbricks/ui";
|
||||
import { CheckCircleIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/solid";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -99,9 +107,6 @@ export default function WhenToSendCard({ environmentId, localSurvey, setLocalSur
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{eventClasses.map((eventClass) => (
|
||||
<SelectItem value={eventClass.id}>{eventClass.name}</SelectItem>
|
||||
))}
|
||||
<button
|
||||
className="flex w-full items-center space-x-2 rounded-md p-1 text-sm font-semibold text-slate-800 hover:bg-slate-100 hover:text-slate-500"
|
||||
value="none"
|
||||
@@ -111,6 +116,10 @@ export default function WhenToSendCard({ environmentId, localSurvey, setLocalSur
|
||||
<PlusIcon className="mr-1 h-5 w-5" />
|
||||
Add Action
|
||||
</button>
|
||||
<SelectSeparator />
|
||||
{eventClasses.map((eventClass) => (
|
||||
<SelectItem value={eventClass.id}>{eventClass.name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="mx-2 text-sm">action is performed</p>
|
||||
|
||||
@@ -29,7 +29,7 @@ interface WhoToSendToCardProps {
|
||||
}
|
||||
|
||||
export default function WhoToSendToCard({ localSurvey }: WhoToSendToCardProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
if (localSurvey.type === "link") {
|
||||
return null;
|
||||
@@ -50,9 +50,7 @@ export default function WhoToSendToCard({ localSurvey }: WhoToSendToCardProps) {
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-800">Who to ask</p>
|
||||
<p className="mt-1 truncate text-sm text-slate-500">
|
||||
Decide which group of you users can be surveyed.
|
||||
</p>
|
||||
<p className="mt-1 truncate text-sm text-slate-500">Filter your users based on attributes.</p>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible.CollapsibleTrigger>
|
||||
|
||||
+3
-7
@@ -1,6 +1,5 @@
|
||||
import Modal from "@/components/shared/Modal";
|
||||
import { Survey } from "@formbricks/types/surveys";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||
import { ClipboardDocumentIcon } from "@heroicons/react/24/solid";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -25,7 +24,9 @@ export default function LinkSurveyModal({ survey, open, setOpen }: LinkSurveyMod
|
||||
<p className="relative mt-3 w-full rounded-lg border border-teal-300 bg-teal-50 p-3 text-center text-slate-800">
|
||||
{`${window.location.protocol}//${window.location.host}/s/${survey.id}`}
|
||||
<ClipboardDocumentIcon
|
||||
className="absolute right-3 top-1/3 h-4 w-4 cursor-pointer text-slate-500 hover:text-slate-700"
|
||||
className="absolute right-3 top-1/3 h-5 w-5 cursor-pointer text-slate-700 hover:text-slate-900"
|
||||
title="Copy survey link to clipboard"
|
||||
aria-label="Copy survey link to clipboard"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
`${window.location.protocol}//${window.location.host}/s/${survey.id}`
|
||||
@@ -37,11 +38,6 @@ export default function LinkSurveyModal({ survey, open, setOpen }: LinkSurveyMod
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-6">
|
||||
<Button variant="primary" onClick={() => setOpen(false)} className="w-full justify-center">
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
+3
-4
@@ -126,10 +126,9 @@ export default function SummaryMetadata({ surveyId, environmentId }) {
|
||||
<ShareIcon className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
{environment.widgetSetupCompleted ||
|
||||
(survey.type === "link" && (
|
||||
<SurveyStatusDropdown surveyId={surveyId} environmentId={environmentId} />
|
||||
))}
|
||||
{environment.widgetSetupCompleted && (
|
||||
<SurveyStatusDropdown surveyId={surveyId} environmentId={environmentId} />
|
||||
)}
|
||||
<Button className="ml-1.5 h-full" href={`/environments/${environmentId}/surveys/${surveyId}/edit`}>
|
||||
<PencilSquareIcon className="mr-2 h-5 w-5 text-white" /> Edit Survey
|
||||
</Button>
|
||||
|
||||
@@ -59,6 +59,23 @@ export default function TemplateList({ environmentId }: { environmentId: string
|
||||
))}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const newTemplate = replacePresetPlaceholders(customSurvey, product);
|
||||
setActiveTemplate(newTemplate);
|
||||
setActiveQuestionId(newTemplate.preset.questions[0].id);
|
||||
}}
|
||||
className={cn(
|
||||
activeTemplate?.name === customSurvey.name
|
||||
? "ring-brand border-transparent ring-2"
|
||||
: "hover:border-brand-dark border-dashed border-slate-300",
|
||||
"duration-120 group relative rounded-lg border-2 bg-transparent p-8 transition-colors duration-150"
|
||||
)}>
|
||||
<PlusCircleIcon className="text-brand-dark h-8 w-8 transition-all duration-150 group-hover:scale-110" />
|
||||
<h3 className="text-md mb-1 mt-3 text-left font-bold text-slate-700 ">{customSurvey.name}</h3>
|
||||
<p className="text-left text-xs text-slate-600 ">{customSurvey.description}</p>
|
||||
</button>
|
||||
{templates
|
||||
.filter((template) => selectedFilter === "All" || template.category === selectedFilter)
|
||||
.map((template: Template) => (
|
||||
@@ -82,21 +99,6 @@ export default function TemplateList({ environmentId }: { environmentId: string
|
||||
<p className="text-left text-xs text-slate-600">{template.description}</p>
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const newTemplate = replacePresetPlaceholders(customSurvey, product);
|
||||
setActiveTemplate(newTemplate);
|
||||
setActiveQuestionId(newTemplate.preset.questions[0].id);
|
||||
}}
|
||||
className={cn(
|
||||
activeTemplate?.name === customSurvey.name && "ring-brand ring-2",
|
||||
"duration-120 hover:border-brand-dark group relative rounded-lg border-2 border-dashed border-slate-300 bg-transparent p-8 transition-colors duration-150"
|
||||
)}>
|
||||
<PlusCircleIcon className="text-brand-dark h-8 w-8 transition-all duration-150 group-hover:scale-110" />
|
||||
<h3 className="text-md mb-1 mt-3 text-left font-bold text-slate-700 ">{customSurvey.name}</h3>
|
||||
<p className="text-left text-xs text-slate-600 ">{customSurvey.description}</p>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
<aside className="group relative hidden h-full flex-1 flex-shrink-0 overflow-hidden border-l border-slate-200 bg-slate-200 shadow-inner md:flex md:flex-col">
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@formbricks/ui";
|
||||
import type { Template } from "@formbricks/types/templates";
|
||||
import { createSurvey } from "@/lib/surveys/surveys";
|
||||
import type { Template } from "@formbricks/types/templates";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/solid";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
@@ -22,16 +23,19 @@ export default function TemplateMenuBar({ activeTemplate, environmentId }: Templ
|
||||
|
||||
return (
|
||||
<div className="border-b border-slate-200 bg-white px-5 py-3 sm:flex sm:items-center sm:justify-between">
|
||||
<h1 className="font-slate-700 text-lg font-semibold">Start with a template</h1>
|
||||
<div className="mt-3 flex sm:ml-4 sm:mt-0">
|
||||
<Button variant="secondary" className="mr-3" onClick={() => router.back()}>
|
||||
Cancel
|
||||
<div className="flex items-center space-x-4">
|
||||
<Button variant="minimal" className="px-0" onClick={() => router.back()}>
|
||||
<ArrowLeftIcon className="h-5 w-5 text-slate-700" />
|
||||
</Button>
|
||||
<h1 className="font-slate-700 text-lg font-semibold">Start with a template</h1>
|
||||
</div>
|
||||
<div className="mt-3 flex sm:ml-4 sm:mt-0">
|
||||
<Button
|
||||
variant="highlight"
|
||||
disabled={activeTemplate === null}
|
||||
loading={loading}
|
||||
onClick={() => addSurvey(activeTemplate)}>
|
||||
onClick={() => addSurvey(activeTemplate)}
|
||||
EndIcon={ArrowRightIcon}>
|
||||
Create Survey
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function MultipleChoiceMultiQuestion({
|
||||
<Subheader subheader={question.subheader} questionId={question.id} />
|
||||
<div className="mt-4">
|
||||
<fieldset>
|
||||
<legend className="sr-only">Choices</legend>
|
||||
<legend className="sr-only">Options</legend>
|
||||
<div className="relative space-y-2 rounded-md bg-white">
|
||||
{question.choices &&
|
||||
question.choices.map((choice) => (
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function MultipleChoiceSingleQuestion({
|
||||
<Subheader subheader={question.subheader} questionId={question.id} />
|
||||
<div className="mt-4">
|
||||
<fieldset>
|
||||
<legend className="sr-only">Choices</legend>
|
||||
<legend className="sr-only">Options</legend>
|
||||
<div className="relative space-y-2 rounded-md">
|
||||
{question.choices &&
|
||||
question.choices.map((choice, idx) => (
|
||||
|
||||
@@ -12,8 +12,15 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@formbricks/ui";
|
||||
import { CheckCircleIcon, PauseCircleIcon, PlayCircleIcon } from "@heroicons/react/24/solid";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
PauseCircleIcon,
|
||||
PlayCircleIcon,
|
||||
PencilSquareIcon,
|
||||
ArchiveBoxIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import toast from "react-hot-toast";
|
||||
import { Badge } from "@formbricks/ui";
|
||||
|
||||
export default function SurveyStatusDropdown({
|
||||
surveyId,
|
||||
@@ -40,10 +47,24 @@ export default function SurveyStatusDropdown({
|
||||
{survey.status === "draft" || survey.status === "archived" ? (
|
||||
<div className="flex items-center">
|
||||
<SurveyStatusIndicator status={survey.status} environmentId={environmentId} />
|
||||
<span className="mr-3 italic text-slate-500">
|
||||
{survey.status === "draft" && "Survey drafted"}
|
||||
{survey.status === "archived" && "Survey archived"}
|
||||
</span>
|
||||
{survey.status === "draft" && (
|
||||
<Badge
|
||||
text="Draft"
|
||||
type="gray"
|
||||
size="normal"
|
||||
StartIcon={PencilSquareIcon}
|
||||
startIconClassName="mr-2"
|
||||
/>
|
||||
)}
|
||||
{survey.status === "archived" && (
|
||||
<Badge
|
||||
text="Archived"
|
||||
type="gray"
|
||||
size="normal"
|
||||
StartIcon={ArchiveBoxIcon}
|
||||
startIconClassName="mr-2"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Select
|
||||
@@ -72,7 +93,7 @@ export default function SurveyStatusDropdown({
|
||||
<div className="flex items-center">
|
||||
<SurveyStatusIndicator status={survey.status} environmentId={environmentId} />
|
||||
<span className="ml-2 text-sm text-slate-700">
|
||||
{survey.status === "draft" && "Survey drafted"}
|
||||
{survey.status === "draft" && "Survey draft"}
|
||||
{survey.status === "inProgress" && "Collecting insights"}
|
||||
{survey.status === "paused" && "Survey paused"}
|
||||
{survey.status === "completed" && "Survey complete"}
|
||||
|
||||
@@ -33,8 +33,8 @@ export default function WidgetStatusIndicator({ environmentId, type }: WidgetSta
|
||||
notImplemented: {
|
||||
icon: ArrowDownIcon,
|
||||
color: "slate",
|
||||
title: "Not implemented yet.",
|
||||
subtitle: "Formbricks widget not yet implemented.",
|
||||
title: "Connect Formbricks to your app.",
|
||||
subtitle: "You have not yet connected Formbricks to your app. Follow setup guide.",
|
||||
},
|
||||
running: { icon: CheckIcon, color: "green", title: "Receiving data.", subtitle: "Last action received:" },
|
||||
issue: {
|
||||
|
||||
+22
-15
@@ -1,8 +1,9 @@
|
||||
import {
|
||||
Bars3BottomLeftIcon,
|
||||
ChartPieIcon,
|
||||
ListBulletIcon,
|
||||
ArrowRightOnRectangleIcon,
|
||||
ChatBubbleBottomCenterTextIcon,
|
||||
CursorArrowRippleIcon,
|
||||
PresentationChartBarIcon,
|
||||
QueueListIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { replaceQuestionPresetPlaceholders } from "./templates";
|
||||
@@ -18,42 +19,46 @@ export type QuestionType = {
|
||||
export const questionTypes: QuestionType[] = [
|
||||
{
|
||||
id: "openText",
|
||||
label: "Open text",
|
||||
label: "Free text",
|
||||
description: "A single line of text",
|
||||
icon: Bars3BottomLeftIcon,
|
||||
icon: ChatBubbleBottomCenterTextIcon,
|
||||
preset: {
|
||||
placeholder: "Type your answer here...",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "multipleChoiceSingle",
|
||||
label: "Multiple Choice Single-Select",
|
||||
label: "Single-Select",
|
||||
description: "A single choice from a list of options (radio buttons)",
|
||||
icon: ListBulletIcon,
|
||||
icon: QueueListIcon,
|
||||
preset: {
|
||||
headline: "What do you do?",
|
||||
subheader: "Can't do both.",
|
||||
choices: [
|
||||
{ id: createId(), label: "" },
|
||||
{ id: createId(), label: "" },
|
||||
{ id: createId(), label: "Eat the cake 🍰" },
|
||||
{ id: createId(), label: "Have the cake 🎂" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "multipleChoiceMulti",
|
||||
label: "Multiple Choice Multi-Select",
|
||||
label: "Multi-Select",
|
||||
description: "Number of choices from a list of options (checkboxes)",
|
||||
icon: ListBulletIcon,
|
||||
preset: {
|
||||
headline: "What's important on vacay?",
|
||||
choices: [
|
||||
{ id: createId(), label: "" },
|
||||
{ id: createId(), label: "" },
|
||||
{ id: createId(), label: "Sun ☀️" },
|
||||
{ id: createId(), label: "Ocean 🌊" },
|
||||
{ id: createId(), label: "Palms 🌴" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "nps",
|
||||
label: "Net Promoter Score (NPS)",
|
||||
label: "Net Promoter Score® (NPS)",
|
||||
description: "Rate satisfaction on a 0-10 scale",
|
||||
icon: ChartPieIcon,
|
||||
icon: PresentationChartBarIcon,
|
||||
preset: {
|
||||
headline: "How likely are you to recommend {{productName}} to a friend or colleague?",
|
||||
lowerLabel: "Not at all likely",
|
||||
@@ -64,8 +69,10 @@ export const questionTypes: QuestionType[] = [
|
||||
id: "cta",
|
||||
label: "Call-to-Action",
|
||||
description: "Ask your users to perform an action",
|
||||
icon: ArrowRightOnRectangleIcon,
|
||||
icon: CursorArrowRippleIcon,
|
||||
preset: {
|
||||
headline: "You are one of our power users!",
|
||||
buttonLabel: "Book interview",
|
||||
buttonExternal: false,
|
||||
dismissButtonLabel: "Skip",
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function MultipleChoiceMultiQuestion({
|
||||
<Subheader subheader={question.subheader} questionId={question.id} />
|
||||
<div className="fb-mt-4">
|
||||
<fieldset>
|
||||
<legend className="fb-sr-only">Choices</legend>
|
||||
<legend className="fb-sr-only">Options</legend>
|
||||
<div className="fb-relative fb-space-y-2 fb-rounded-md fb-bg-white">
|
||||
{question.choices &&
|
||||
question.choices.map((choice) => (
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function MultipleChoiceSingleQuestion({
|
||||
<Subheader subheader={question.subheader} questionId={question.id} />
|
||||
<div className="fb-mt-4">
|
||||
<fieldset>
|
||||
<legend className="fb-sr-only">Choices</legend>
|
||||
<legend className="fb-sr-only">Options</legend>
|
||||
<div className="fb-relative fb-space-y-2 fb-rounded-md fb-bg-white">
|
||||
{question.choices &&
|
||||
question.choices.map((choice, idx) => (
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function NPSQuestion({ question, onSubmit, lastQuestion, brandCol
|
||||
<Subheader subheader={question.subheader} questionId={question.id} />
|
||||
<div className="fb-my-4">
|
||||
<fieldset>
|
||||
<legend className="fb-sr-only">Choices</legend>
|
||||
<legend className="fb-sr-only">Options</legend>
|
||||
<div className="fb-flex">
|
||||
{Array.from({ length: 11 }, (_, i) => i).map((number) => (
|
||||
<label
|
||||
|
||||
@@ -137,9 +137,7 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
{EndIcon && (
|
||||
<EndIcon className={cn("-mr-1 inline h-5 w-5 ltr:ml-2 rtl:mr-2", endIconClassName || "")} />
|
||||
)}
|
||||
{EndIcon && <EndIcon className={cn("-mr-1 ml-2 inline h-5 w-5 rtl:mr-2", endIconClassName || "")} />}
|
||||
</>
|
||||
);
|
||||
return props.href ? (
|
||||
|
||||
@@ -66,7 +66,7 @@ const editorConfig = {
|
||||
export const Editor = (props: TextEditorProps) => {
|
||||
const editable = props.editable ?? true;
|
||||
return (
|
||||
<div className="editor rounded-md">
|
||||
<div className="editor cursor-text rounded-md">
|
||||
<LexicalComposer initialConfig={{ ...editorConfig, editable }}>
|
||||
<div className="editor-container rounded-md p-0">
|
||||
<ToolbarPlugin
|
||||
@@ -84,7 +84,9 @@ export const Editor = (props: TextEditorProps) => {
|
||||
style={{ height: props.height }}>
|
||||
<RichTextPlugin
|
||||
contentEditable={<ContentEditable style={{ height: props.height }} className="editor-input" />}
|
||||
placeholder={<div className="text-muted -mt-11 p-3 text-sm">{props.placeholder || ""}</div>}
|
||||
placeholder={
|
||||
<div className="text-muted -mt-11 cursor-text p-3 text-sm">{props.placeholder || ""}</div>
|
||||
}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
<ListPlugin />
|
||||
|
||||
Reference in New Issue
Block a user