mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-07 14:21:52 -05:00
fixes coderabbit feedback
This commit is contained in:
+2
-2
@@ -103,7 +103,7 @@ const evaluateLogicAndGetNextElementId = (
|
||||
data: TResponseData,
|
||||
localVariables: TResponseVariables,
|
||||
currentElementIndex: number,
|
||||
currQuesTemp: TSurveyElement,
|
||||
currElementTemp: TSurveyElement,
|
||||
selectedLanguage: string | null
|
||||
): {
|
||||
nextElementId: string | undefined;
|
||||
@@ -115,7 +115,7 @@ const evaluateLogicAndGetNextElementId = (
|
||||
|
||||
let firstJumpTarget: string | undefined;
|
||||
|
||||
const { block: currentBlock } = findElementLocation(localSurvey, currQuesTemp.id);
|
||||
const { block: currentBlock } = findElementLocation(localSurvey, currElementTemp.id);
|
||||
|
||||
if (currentBlock?.logic && currentBlock.logic.length > 0) {
|
||||
for (const logic of currentBlock.logic) {
|
||||
|
||||
+17
-17
@@ -10,35 +10,35 @@ import { parseRecallInfo } from "@/lib/utils/recall";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
|
||||
|
||||
interface ElementSkipProps {
|
||||
skippedQuestions: string[] | undefined;
|
||||
skippedElements: string[] | undefined;
|
||||
status: string;
|
||||
questions: TSurveyElement[];
|
||||
isFirstQuestionAnswered?: boolean;
|
||||
elements: TSurveyElement[];
|
||||
isFirstElementAnswered?: boolean;
|
||||
responseData: TResponseData;
|
||||
}
|
||||
|
||||
export const ElementSkip = ({
|
||||
skippedQuestions,
|
||||
skippedElements,
|
||||
status,
|
||||
questions,
|
||||
isFirstQuestionAnswered,
|
||||
elements,
|
||||
isFirstElementAnswered,
|
||||
responseData,
|
||||
}: ElementSkipProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
{skippedQuestions && (
|
||||
{skippedElements && (
|
||||
<div className="my-2 flex w-full px-2 text-sm text-slate-400">
|
||||
{status === "welcomeCard" && (
|
||||
<div className="mb-2 flex">
|
||||
{
|
||||
<div
|
||||
className={`relative flex ${
|
||||
isFirstQuestionAnswered ? "h-[100%]" : "h-[200%]"
|
||||
isFirstElementAnswered ? "h-[100%]" : "h-[200%]"
|
||||
} w-0.5 items-center justify-center`}
|
||||
style={{
|
||||
background:
|
||||
"repeating-linear-gradient(rgb(148, 163, 184), rgb(148, 163, 184) 5px, transparent 5px, transparent 8px)", // adjust the values to fit your design
|
||||
"repeating-linear-gradient(rgb(148, 163, 184), rgb(148, 163, 184) 5px, transparent 5px, transparent 8px)",
|
||||
}}>
|
||||
<CheckCircle2Icon className="p-0.25 absolute top-0 w-[1.5rem] min-w-[1.5rem] rounded-full bg-white text-slate-400" />
|
||||
</div>
|
||||
@@ -52,9 +52,9 @@ export const ElementSkip = ({
|
||||
className="flex w-0.5 items-center justify-center"
|
||||
style={{
|
||||
background:
|
||||
"repeating-linear-gradient(to bottom, rgb(148 163 184), rgb(148 163 184) 8px, transparent 5px, transparent 15px)", // adjust the values to fit your design
|
||||
"repeating-linear-gradient(to bottom, rgb(148 163 184), rgb(148 163 184) 8px, transparent 5px, transparent 15px)",
|
||||
}}>
|
||||
{skippedQuestions.length > 1 && (
|
||||
{skippedElements.length > 1 && (
|
||||
<TooltipProvider delayDuration={50}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -70,13 +70,13 @@ export const ElementSkip = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-6 flex flex-col">
|
||||
{skippedQuestions?.map((questionId) => {
|
||||
{skippedElements?.map((questionId) => {
|
||||
return (
|
||||
<p className="my-2" key={questionId}>
|
||||
{getTextContent(
|
||||
parseRecallInfo(
|
||||
getLocalizedValue(
|
||||
questions.find((question) => question.id === questionId)?.headline ?? {
|
||||
elements.find((question) => question.id === questionId)?.headline ?? {
|
||||
default: "",
|
||||
},
|
||||
"default"
|
||||
@@ -96,7 +96,7 @@ export const ElementSkip = ({
|
||||
className="flex w-0.5 flex-grow items-start justify-center"
|
||||
style={{
|
||||
background:
|
||||
"repeating-linear-gradient(to bottom, rgb(148 163 184), rgb(148 163 184) 2px, transparent 2px, transparent 10px)", // adjust the 2px to change dot size and 10px to change space between dots
|
||||
"repeating-linear-gradient(to bottom, rgb(148 163 184), rgb(148 163 184) 2px, transparent 2px, transparent 10px)",
|
||||
}}>
|
||||
<div className="flex">
|
||||
<XCircleIcon className="min-h-[1.5rem] min-w-[1.5rem] rounded-full bg-white text-slate-400" />
|
||||
@@ -108,14 +108,14 @@ export const ElementSkip = ({
|
||||
className="mb-2 w-fit rounded-lg bg-slate-100 px-2 font-medium text-slate-700">
|
||||
{t("environments.surveys.responses.survey_closed")}
|
||||
</p>
|
||||
{skippedQuestions &&
|
||||
skippedQuestions.map((questionId) => {
|
||||
{skippedElements &&
|
||||
skippedElements.map((questionId) => {
|
||||
return (
|
||||
<p className="my-2" key={questionId}>
|
||||
{getTextContent(
|
||||
parseRecallInfo(
|
||||
getLocalizedValue(
|
||||
questions.find((question) => question.id === questionId)?.headline ?? {
|
||||
elements.find((question) => question.id === questionId)?.headline ?? {
|
||||
default: "",
|
||||
},
|
||||
"default"
|
||||
|
||||
+8
-8
@@ -27,8 +27,8 @@ export const SingleResponseCardBody = ({
|
||||
response,
|
||||
skippedQuestions,
|
||||
}: SingleResponseCardBodyProps) => {
|
||||
const questions = getElementsFromBlocks(survey.blocks);
|
||||
const isFirstQuestionAnswered = questions[0] ? !!response.data[questions[0].id] : false;
|
||||
const elements = getElementsFromBlocks(survey.blocks);
|
||||
const isFirstElementAnswered = elements[0] ? !!response.data[elements[0].id] : false;
|
||||
const { t } = useTranslation();
|
||||
const formatTextWithSlashes = (text: string) => {
|
||||
// Updated regex to match content between #/ and \#
|
||||
@@ -55,10 +55,10 @@ export const SingleResponseCardBody = ({
|
||||
<div className="p-6">
|
||||
{survey.welcomeCard.enabled && (
|
||||
<ElementSkip
|
||||
skippedQuestions={[]}
|
||||
questions={questions}
|
||||
skippedElements={[]}
|
||||
elements={elements}
|
||||
status={"welcomeCard"}
|
||||
isFirstQuestionAnswered={isFirstQuestionAnswered}
|
||||
isFirstElementAnswered={isFirstElementAnswered}
|
||||
responseData={response.data}
|
||||
/>
|
||||
)}
|
||||
@@ -66,7 +66,7 @@ export const SingleResponseCardBody = ({
|
||||
{survey.isVerifyEmailEnabled && response.data["verifiedEmail"] && (
|
||||
<VerifiedEmail responseData={response.data} />
|
||||
)}
|
||||
{questions.map((question) => {
|
||||
{elements.map((question) => {
|
||||
const skipped = skippedQuestions.find((skippedQuestionElement) =>
|
||||
skippedQuestionElement.includes(question.id)
|
||||
);
|
||||
@@ -104,8 +104,8 @@ export const SingleResponseCardBody = ({
|
||||
</div>
|
||||
) : (
|
||||
<ElementSkip
|
||||
skippedQuestions={skipped}
|
||||
questions={questions}
|
||||
skippedElements={skipped}
|
||||
elements={elements}
|
||||
responseData={response.data}
|
||||
status={
|
||||
response.finished ||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Container } from "@react-email/components";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
interface QuestionHeaderProps {
|
||||
interface ElementHeaderProps {
|
||||
headline: string;
|
||||
subheader?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function QuestionHeader({ headline, subheader, className }: QuestionHeaderProps): React.JSX.Element {
|
||||
export function ElementHeader({ headline, subheader, className }: ElementHeaderProps): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Container className={cn("text-question-color m-0 block text-base font-semibold leading-6", className)}>
|
||||
|
||||
@@ -24,7 +24,7 @@ import { isLight, mixColor } from "@/lib/utils/colors";
|
||||
import { parseRecallInfo } from "@/lib/utils/recall";
|
||||
import { RatingSmiley } from "@/modules/analysis/components/RatingSmiley";
|
||||
import { getNPSOptionColor, getRatingNumberOptionColor } from "../lib/utils";
|
||||
import { QuestionHeader } from "./email-element-header";
|
||||
import { ElementHeader } from "./email-element-header";
|
||||
|
||||
interface PreviewEmailTemplateProps {
|
||||
survey: TSurvey;
|
||||
@@ -92,7 +92,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.OpenText:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Section className="border-input-border-color rounded-custom mt-4 block h-20 w-full border border-solid bg-slate-50" />
|
||||
<EmailFooter />
|
||||
</EmailTemplateWrapper>
|
||||
@@ -100,7 +100,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.Consent:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Container className="border-input-border-color bg-input-color rounded-custom m-0 mt-4 block w-full max-w-none border border-solid p-4 font-medium text-slate-800">
|
||||
<Text className="text-question-color m-0 inline-block">
|
||||
{getLocalizedValue(firstQuestion.label, defaultLanguageCode)}
|
||||
@@ -130,7 +130,7 @@ export async function PreviewEmailTemplate({
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<Section className="w-full justify-center">
|
||||
<QuestionHeader headline={headline} subheader={subheader} />
|
||||
<ElementHeader headline={headline} subheader={subheader} />
|
||||
<Container className="mx-0 mt-4 w-full items-center justify-center">
|
||||
<Section
|
||||
className={cn("w-full overflow-hidden", {
|
||||
@@ -179,7 +179,7 @@ export async function PreviewEmailTemplate({
|
||||
const ctaElement = firstQuestion as TSurveyCTAElement;
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
{ctaElement.buttonExternal && ctaElement.ctaButtonLabel && ctaElement.buttonUrl && (
|
||||
<Container className="mx-0 mt-4 flex max-w-none items-center justify-end">
|
||||
<EmailButton
|
||||
@@ -200,7 +200,7 @@ export async function PreviewEmailTemplate({
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<Section className="w-full">
|
||||
<QuestionHeader headline={headline} subheader={subheader} />
|
||||
<ElementHeader headline={headline} subheader={subheader} />
|
||||
<Container className="mx-0 mt-4 w-full items-center justify-center">
|
||||
<Section className="w-full overflow-hidden">
|
||||
<Row>
|
||||
@@ -252,7 +252,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.MultipleChoiceMulti:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Container className="mx-0 max-w-none">
|
||||
{firstQuestion.choices.map((choice) => (
|
||||
<Section
|
||||
@@ -268,7 +268,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.Ranking:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Container className="mx-0 max-w-none">
|
||||
{firstQuestion.choices.map((choice) => (
|
||||
<Section
|
||||
@@ -284,7 +284,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.MultipleChoiceSingle:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Container className="mx-0 max-w-none">
|
||||
{firstQuestion.choices.map((choice) => (
|
||||
<Link
|
||||
@@ -301,7 +301,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.PictureSelection:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Section className="mx-0 mt-4">
|
||||
{firstQuestion.choices.map((choice) =>
|
||||
firstQuestion.allowMulti ? (
|
||||
@@ -328,7 +328,7 @@ export async function PreviewEmailTemplate({
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<Container>
|
||||
<QuestionHeader headline={headline} subheader={subheader} />
|
||||
<ElementHeader headline={headline} subheader={subheader} />
|
||||
<EmailButton
|
||||
className={cn(
|
||||
"bg-brand-color rounded-custom mx-auto block w-max cursor-pointer appearance-none px-6 py-3 text-sm font-medium",
|
||||
@@ -343,7 +343,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.Date:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Section className="border-input-border-color bg-input-color rounded-custom mt-4 flex h-12 w-full items-center justify-center border border-solid">
|
||||
<CalendarDaysIcon className="text-question-color inline h-4 w-4" />
|
||||
<Text className="text-question-color inline text-sm font-medium">
|
||||
@@ -356,7 +356,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.Matrix:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Container className="mx-0">
|
||||
<Section className="w-full table-auto">
|
||||
<Row>
|
||||
@@ -398,7 +398,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.ContactInfo:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
{["First Name", "Last Name", "Email", "Phone", "Company"].map((label) => (
|
||||
<Section
|
||||
className="border-input-border-color bg-input-color rounded-custom mt-4 block h-10 w-full border border-solid py-2 pl-2 text-slate-400"
|
||||
@@ -413,7 +413,7 @@ export async function PreviewEmailTemplate({
|
||||
case TSurveyElementTypeEnum.FileUpload:
|
||||
return (
|
||||
<EmailTemplateWrapper styling={styling} surveyUrl={url}>
|
||||
<QuestionHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<ElementHeader headline={headline} subheader={subheader} className="mr-8" />
|
||||
<Section className="border-input-border-color rounded-custom mt-4 flex h-24 w-full items-center justify-center border border-dashed bg-slate-50">
|
||||
<Container className="mx-auto flex items-center text-center">
|
||||
<UploadIcon className="mt-6 inline h-5 w-5 text-slate-400" />
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function ResponseFinishedEmail({
|
||||
environmentId,
|
||||
organization,
|
||||
}: ResponseFinishedEmailProps): Promise<React.JSX.Element> {
|
||||
const questions = getElementResponseMapping(survey, response);
|
||||
const elements = getElementResponseMapping(survey, response);
|
||||
const t = await getTranslate();
|
||||
|
||||
return (
|
||||
@@ -41,13 +41,13 @@ export async function ResponseFinishedEmail({
|
||||
})}
|
||||
</Text>
|
||||
<Hr />
|
||||
{questions.map((question) => {
|
||||
if (!question.response) return;
|
||||
{elements.map((e) => {
|
||||
if (!e.response) return;
|
||||
return (
|
||||
<Row key={question.element}>
|
||||
<Row key={e.element}>
|
||||
<Column className="w-full font-medium">
|
||||
<Text className="mb-2 text-sm">{question.element}</Text>
|
||||
{renderEmailResponseValue(question.response, question.type, t)}
|
||||
<Text className="mb-2 text-sm">{e.element}</Text>
|
||||
{renderEmailResponseValue(e.response, e.type, t)}
|
||||
</Column>
|
||||
</Row>
|
||||
);
|
||||
|
||||
@@ -205,12 +205,12 @@ export const ElementsView = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!invalidElements) return;
|
||||
let updatedInvalidElements: string[] = invalidElements;
|
||||
let updatedInvalidElements: string[] = { ...invalidElements };
|
||||
|
||||
// Check welcome card
|
||||
if (localSurvey.welcomeCard.enabled && !isWelcomeCardValid(localSurvey.welcomeCard, surveyLanguages)) {
|
||||
if (!updatedInvalidElements.includes("start")) {
|
||||
updatedInvalidElements.push("start");
|
||||
updatedInvalidElements = [...updatedInvalidElements, "start"];
|
||||
}
|
||||
} else {
|
||||
updatedInvalidElements = updatedInvalidElements.filter((elementId) => elementId !== "start");
|
||||
@@ -220,7 +220,7 @@ export const ElementsView = ({
|
||||
localSurvey.endings.forEach((ending) => {
|
||||
if (!isEndingCardValid(ending, surveyLanguages)) {
|
||||
if (!updatedInvalidElements.includes(ending.id)) {
|
||||
updatedInvalidElements.push(ending.id);
|
||||
updatedInvalidElements = [...updatedInvalidElements, ending.id];
|
||||
}
|
||||
} else {
|
||||
updatedInvalidElements = updatedInvalidElements.filter((elementId) => elementId !== ending.id);
|
||||
|
||||
@@ -142,7 +142,7 @@ export const MultipleChoiceElementForm = ({
|
||||
|
||||
if (choiceToDelete !== "other" && choiceToDelete !== "none") {
|
||||
const idx = findOptionUsedInLogic(localSurvey, element.id, choiceToDelete);
|
||||
if (elementIdx !== -1) {
|
||||
if (idx !== -1) {
|
||||
toast.error(
|
||||
t("environments.surveys.edit.option_used_in_logic_error", {
|
||||
questionIndex: idx + 1,
|
||||
|
||||
@@ -90,7 +90,7 @@ export const RankingElementForm = ({
|
||||
};
|
||||
|
||||
const deleteChoice = (choiceIdx: number) => {
|
||||
const newChoices = !element.choices ? [] : element.choices.filter((_, idx) => idx !== choiceIdx);
|
||||
const newChoices = element.choices.filter((_, idx) => idx !== choiceIdx);
|
||||
const choiceValue = element.choices[choiceIdx].label[selectedLanguageCode];
|
||||
|
||||
if (isInvalidValue === choiceValue) {
|
||||
|
||||
@@ -11,14 +11,19 @@ import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
|
||||
interface UpdatElementIdProps {
|
||||
interface UpdateElementIdProps {
|
||||
localSurvey: TSurvey;
|
||||
element: TSurveyElement;
|
||||
elementIdx: number;
|
||||
updateElement: (elementIdx: number, updatedAttributes: any) => void;
|
||||
}
|
||||
|
||||
export const UpdateElementId = ({ localSurvey, element, elementIdx, updateElement }: UpdatElementIdProps) => {
|
||||
export const UpdateElementId = ({
|
||||
localSurvey,
|
||||
element,
|
||||
elementIdx,
|
||||
updateElement,
|
||||
}: UpdateElementIdProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [currentValue, setCurrentValue] = useState(element.id);
|
||||
const [prevValue, setPrevValue] = useState(element.id);
|
||||
@@ -37,7 +42,7 @@ export const UpdateElementId = ({ localSurvey, element, elementIdx, updateElemen
|
||||
const endingCardIds = localSurvey.endings.map((e) => e.id);
|
||||
const hiddenFieldIds = localSurvey.hiddenFields.fieldIds ?? [];
|
||||
|
||||
const validateIdError = validateId("Element", currentValue, elementIds, endingCardIds, hiddenFieldIds);
|
||||
const validateIdError = validateId("Question", currentValue, elementIds, endingCardIds, hiddenFieldIds);
|
||||
|
||||
if (validateIdError) {
|
||||
setIsInputInvalid(true);
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function FollowUpEmail(props: FollowUpEmailProps): Promise<React.JS
|
||||
const { properties } = props.followUp.action;
|
||||
const { body } = properties;
|
||||
|
||||
const questions = props.attachResponseData ? getElementResponseMapping(props.survey, props.response) : [];
|
||||
const elements = props.attachResponseData ? getElementResponseMapping(props.survey, props.response) : [];
|
||||
const t = await getTranslate();
|
||||
// If the logo is not set, we are not using white labeling
|
||||
const isDefaultLogo = !props.logoUrl || props.logoUrl === fbLogoUrl;
|
||||
@@ -70,15 +70,15 @@ export async function FollowUpEmail(props: FollowUpEmailProps): Promise<React.JS
|
||||
}}
|
||||
/>
|
||||
|
||||
{questions.length > 0 ? <Hr /> : null}
|
||||
{elements.length > 0 ? <Hr /> : null}
|
||||
|
||||
{questions.map((question) => {
|
||||
if (!question.response) return;
|
||||
{elements.map((e) => {
|
||||
if (!e.response) return;
|
||||
return (
|
||||
<Row key={question.element}>
|
||||
<Row key={e.element}>
|
||||
<Column className="w-full font-medium">
|
||||
<Text className="mb-2 text-sm">{question.element}</Text>
|
||||
{renderEmailResponseValue(question.response, question.type, t, true)}
|
||||
<Text className="mb-2 text-sm">{e.element}</Text>
|
||||
{renderEmailResponseValue(e.response, e.type, t, true)}
|
||||
</Column>
|
||||
</Row>
|
||||
);
|
||||
|
||||
@@ -78,7 +78,7 @@ interface AddFollowUpModalProps {
|
||||
}
|
||||
|
||||
type EmailSendToOption = {
|
||||
type: "openTextQuestion" | "contactInfoQuestion" | "hiddenField" | "user";
|
||||
type: "openTextElement" | "contactInfoElement" | "hiddenField" | "user";
|
||||
label: string;
|
||||
id: string;
|
||||
};
|
||||
@@ -97,21 +97,20 @@ export const FollowUpModal = ({
|
||||
locale,
|
||||
}: AddFollowUpModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const QUESTIONS_ICON_MAP = getElementIconMap(t);
|
||||
const ELEMENTS_ICON_MAP = getElementIconMap(t);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
|
||||
const emailSendToOptions: EmailSendToOption[] = useMemo(() => {
|
||||
// Derive questions from blocks
|
||||
const questions = getElementsFromBlocks(localSurvey.blocks);
|
||||
const elements = getElementsFromBlocks(localSurvey.blocks);
|
||||
|
||||
const openTextAndContactQuestions = questions.filter((question) => {
|
||||
if (question.type === TSurveyElementTypeEnum.ContactInfo) {
|
||||
return question.email.show;
|
||||
const openTextAndContactElements = elements.filter((element) => {
|
||||
if (element.type === TSurveyElementTypeEnum.ContactInfo) {
|
||||
return element.email.show;
|
||||
}
|
||||
|
||||
if (question.type === TSurveyElementTypeEnum.OpenText) {
|
||||
if (question.inputType === "email") {
|
||||
if (element.type === TSurveyElementTypeEnum.OpenText) {
|
||||
if (element.inputType === "email") {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,15 +141,15 @@ export const FollowUpModal = ({
|
||||
: [...updatedTeamMemberDetails, { email: userEmail, name: "Yourself" }];
|
||||
|
||||
return [
|
||||
...openTextAndContactQuestions.map((question) => ({
|
||||
...openTextAndContactElements.map((element) => ({
|
||||
label: getTextContent(
|
||||
recallToHeadline(question.headline, localSurvey, false, selectedLanguageCode)[selectedLanguageCode]
|
||||
recallToHeadline(element.headline, localSurvey, false, selectedLanguageCode)[selectedLanguageCode]
|
||||
),
|
||||
id: question.id,
|
||||
id: element.id,
|
||||
type:
|
||||
question.type === TSurveyElementTypeEnum.OpenText
|
||||
? "openTextQuestion"
|
||||
: ("contactInfoQuestion" as EmailSendToOption["type"]),
|
||||
element.type === TSurveyElementTypeEnum.OpenText
|
||||
? "openTextElement"
|
||||
: ("contactInfoElement" as EmailSendToOption["type"]),
|
||||
})),
|
||||
|
||||
...hiddenFields.fieldIds.map((fieldId: string) => ({
|
||||
@@ -164,7 +163,7 @@ export const FollowUpModal = ({
|
||||
id: member.email,
|
||||
type: "user" as EmailSendToOption["type"],
|
||||
})),
|
||||
];
|
||||
] satisfies EmailSendToOption[];
|
||||
}, [localSurvey, selectedLanguageCode, teamMemberDetails, userEmail]);
|
||||
|
||||
const form = useForm<TCreateSurveyFollowUpForm>({
|
||||
@@ -373,8 +372,8 @@ export const FollowUpModal = ({
|
||||
setOpen(open);
|
||||
};
|
||||
|
||||
const emailSendToQuestionOptions = emailSendToOptions.filter(
|
||||
(option) => option.type === "openTextQuestion" || option.type === "contactInfoQuestion"
|
||||
const emailSendToElementOptions = emailSendToOptions.filter(
|
||||
(option) => option.type === "openTextElement" || option.type === "contactInfoElement"
|
||||
);
|
||||
const emailSendToHiddenFieldOptions = emailSendToOptions.filter((option) => option.type === "hiddenField");
|
||||
const userSendToEmailOptions = emailSendToOptions.filter((option) => option.type === "user");
|
||||
@@ -395,7 +394,7 @@ export const FollowUpModal = ({
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="h-4 w-4">
|
||||
{QUESTIONS_ICON_MAP[option.type === "openTextQuestion" ? "openText" : "contactInfo"]}
|
||||
{ELEMENTS_ICON_MAP[option.type === "openTextElement" ? "openText" : "contactInfo"]}
|
||||
</div>
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap">{option.label}</span>
|
||||
</div>
|
||||
@@ -649,13 +648,15 @@ export const FollowUpModal = ({
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
{emailSendToQuestionOptions.length > 0 ? (
|
||||
{emailSendToElementOptions.length > 0 ? (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center space-x-2 p-2">
|
||||
<p className="text-sm text-slate-500">Questions</p>
|
||||
<p className="text-sm text-slate-500">
|
||||
{t("common.questions")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{emailSendToQuestionOptions.map((option) =>
|
||||
{emailSendToElementOptions.map((option) =>
|
||||
renderSelectItem(option)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@ export const TemplateContainerWithPreview = ({
|
||||
const { t } = useTranslation();
|
||||
const initialTemplate = customSurveyTemplate(t);
|
||||
const [activeTemplate, setActiveTemplate] = useState<TTemplate>(initialTemplate);
|
||||
const [activeQuestionId, setActiveQuestionId] = useState<string>(
|
||||
const [activeElementId, setActiveElementId] = useState<string>(
|
||||
initialTemplate.preset.blocks[0]?.elements[0]?.id || ""
|
||||
);
|
||||
const [templateSearch, setTemplateSearch] = useState<string | null>(null);
|
||||
@@ -58,7 +58,7 @@ export const TemplateContainerWithPreview = ({
|
||||
userId={userId}
|
||||
templateSearch={templateSearch ?? ""}
|
||||
onTemplateClick={(template) => {
|
||||
setActiveQuestionId(template.preset.blocks[0]?.elements[0]?.id || "");
|
||||
setActiveElementId(template.preset.blocks[0]?.elements[0]?.id || "");
|
||||
setActiveTemplate(template);
|
||||
}}
|
||||
/>
|
||||
@@ -67,7 +67,7 @@ export const TemplateContainerWithPreview = ({
|
||||
{activeTemplate && (
|
||||
<PreviewSurvey
|
||||
survey={{ ...getMinimalSurvey(t), ...activeTemplate.preset }}
|
||||
elementId={activeQuestionId}
|
||||
elementId={activeElementId}
|
||||
project={project}
|
||||
environment={environment}
|
||||
languageCode={"default"}
|
||||
|
||||
@@ -120,7 +120,7 @@ export function RatingElement({
|
||||
key={number}
|
||||
onMouseOver={() => {
|
||||
setHoveredNumber(number);
|
||||
}}
|
||||
}} // NOSONAR
|
||||
onMouseLeave={() => {
|
||||
setHoveredNumber(0);
|
||||
}}
|
||||
|
||||
@@ -300,23 +300,17 @@ const findJumpToQuestionActions = (actions: TSurveyLogicAction[]): TActionJumpTo
|
||||
|
||||
// function to validate hidden field or question id or element id
|
||||
export const validateId = (
|
||||
type: "Hidden field" | "Question" | "Element",
|
||||
type: "Hidden field" | "Question", // TODO: Change this to "Element" when we're ready to change the UI
|
||||
field: string,
|
||||
existingQuestionIds: string[],
|
||||
existingElementIds: string[],
|
||||
existingEndingCardIds: string[],
|
||||
existingHiddenFieldIds: string[],
|
||||
existingElementIds?: string[]
|
||||
existingHiddenFieldIds: string[]
|
||||
): string | null => {
|
||||
if (field.trim() === "") {
|
||||
return `Please enter a ${type} Id.`;
|
||||
}
|
||||
|
||||
const combinedIds = [
|
||||
...existingQuestionIds,
|
||||
...existingHiddenFieldIds,
|
||||
...existingEndingCardIds,
|
||||
...(existingElementIds ?? []),
|
||||
];
|
||||
const combinedIds = [...existingElementIds, ...existingHiddenFieldIds, ...existingEndingCardIds];
|
||||
|
||||
if (combinedIds.findIndex((id) => id.toLowerCase() === field.toLowerCase()) !== -1) {
|
||||
return `${type} ID already exists in questions, hidden fields, or elements.`;
|
||||
|
||||
Reference in New Issue
Block a user