mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-07 22:31:18 -06:00
feat: adds feedback component
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { UserIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import formbricks from "@formbricks/js/app";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TAttributeClass } from "@formbricks/types/attribute-classes";
|
||||
@@ -28,6 +29,8 @@ interface OpenTextSummaryProps {
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
isAiEnabled: boolean;
|
||||
productId: string;
|
||||
productName: string;
|
||||
}
|
||||
|
||||
export const OpenTextSummary = ({
|
||||
@@ -36,6 +39,8 @@ export const OpenTextSummary = ({
|
||||
survey,
|
||||
attributeClasses,
|
||||
isAiEnabled,
|
||||
productId,
|
||||
productName,
|
||||
}: OpenTextSummaryProps) => {
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
const [activeTab, setActiveTab] = useState<"insights" | "responses">(
|
||||
@@ -65,6 +70,22 @@ export const OpenTextSummary = ({
|
||||
},
|
||||
];
|
||||
|
||||
const handleFeedback = (feedback: "positive" | "negative") => {
|
||||
formbricks.track("Insight Feedback", {
|
||||
hiddenFields: {
|
||||
feedbackSentiment: feedback,
|
||||
productId,
|
||||
productName,
|
||||
surveyId: survey.id,
|
||||
surveyName: survey.name,
|
||||
insightId: currentInsight?.id,
|
||||
insightCategory: currentInsight?.category,
|
||||
questionId: questionSummary.question.id,
|
||||
environmentId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader
|
||||
@@ -78,6 +99,7 @@ export const OpenTextSummary = ({
|
||||
insight={currentInsight}
|
||||
surveyId={survey.id}
|
||||
questionId={questionSummary.question.id}
|
||||
handleFeedback={handleFeedback}
|
||||
/>
|
||||
{isAiEnabled && (
|
||||
<div className="flex items-center justify-between pr-4">
|
||||
|
||||
@@ -40,6 +40,7 @@ interface SummaryListProps {
|
||||
totalResponseCount: number;
|
||||
attributeClasses: TAttributeClass[];
|
||||
isAiEnabled: boolean;
|
||||
productName: string;
|
||||
}
|
||||
|
||||
export const SummaryList = ({
|
||||
@@ -50,6 +51,7 @@ export const SummaryList = ({
|
||||
totalResponseCount,
|
||||
attributeClasses,
|
||||
isAiEnabled,
|
||||
productName,
|
||||
}: SummaryListProps) => {
|
||||
const { setSelectedFilter, selectedFilter } = useResponseFilter();
|
||||
const widgetSetupCompleted =
|
||||
@@ -134,6 +136,8 @@ export const SummaryList = ({
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
isAiEnabled={isAiEnabled}
|
||||
productId={environment.productId}
|
||||
productName={productName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ interface SummaryPageProps {
|
||||
totalResponseCount: number;
|
||||
attributeClasses: TAttributeClass[];
|
||||
isAiEnabled: boolean;
|
||||
productName: string;
|
||||
}
|
||||
|
||||
export const SummaryPage = ({
|
||||
@@ -58,6 +59,7 @@ export const SummaryPage = ({
|
||||
totalResponseCount,
|
||||
attributeClasses,
|
||||
isAiEnabled,
|
||||
productName,
|
||||
}: SummaryPageProps) => {
|
||||
const params = useParams();
|
||||
const sharingKey = params.sharingKey as string;
|
||||
@@ -177,6 +179,7 @@ export const SummaryPage = ({
|
||||
totalResponseCount={totalResponseCount}
|
||||
attributeClasses={attributeClasses}
|
||||
isAiEnabled={isAiEnabled}
|
||||
productName={productName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -100,6 +100,7 @@ const Page = async ({ params }) => {
|
||||
totalResponseCount={totalResponseCount}
|
||||
attributeClasses={attributeClasses}
|
||||
isAiEnabled={isAiEnabled}
|
||||
productName={product.name}
|
||||
/>
|
||||
</PageContentWrapper>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ThumbsDownIcon, ThumbsUpIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
|
||||
@@ -8,7 +9,7 @@ import { TDocument } from "@formbricks/types/documents";
|
||||
import { TInsight } from "@formbricks/types/insights";
|
||||
import { Badge } from "../Badge";
|
||||
import { Card, CardContent, CardFooter } from "../Card";
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "../Sheet";
|
||||
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "../Sheet";
|
||||
import { getDocumentsByInsightIdSurveyIdQuestionIdAction } from "./actions";
|
||||
|
||||
interface InsightSheetProps {
|
||||
@@ -17,9 +18,17 @@ interface InsightSheetProps {
|
||||
insight: TInsight | null;
|
||||
surveyId: string;
|
||||
questionId: string;
|
||||
handleFeedback: (feedback: "positive" | "negative") => void;
|
||||
}
|
||||
|
||||
export const InsightSheet = ({ isOpen, setIsOpen, insight, surveyId, questionId }: InsightSheetProps) => {
|
||||
export const InsightSheet = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
insight,
|
||||
surveyId,
|
||||
questionId,
|
||||
handleFeedback,
|
||||
}: InsightSheetProps) => {
|
||||
const [documents, setDocuments] = useState<TDocument[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -49,22 +58,31 @@ export const InsightSheet = ({ isOpen, setIsOpen, insight, surveyId, questionId
|
||||
if (!insight) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleFeedbackClick = (feedback: "positive" | "negative") => {
|
||||
setIsOpen(false);
|
||||
handleFeedback(feedback);
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={isOpen} onOpenChange={(v) => setIsOpen(v)}>
|
||||
<SheetContent className="w-[400rem] bg-white lg:max-w-lg xl:max-w-2xl">
|
||||
<SheetHeader>
|
||||
<SheetTitle>
|
||||
<span className="mr-3">{insight.title}</span>
|
||||
{insight.category === "complaint" ? (
|
||||
<Badge text="Complaint" type="error" size="tiny" />
|
||||
) : insight.category === "featureRequest" ? (
|
||||
<Badge text="Request" type="warning" size="tiny" />
|
||||
) : insight.category === "praise" ? (
|
||||
<Badge text="Praise" type="success" size="tiny" />
|
||||
) : null}
|
||||
</SheetTitle>
|
||||
<SheetDescription>{insight.description}</SheetDescription>
|
||||
<div className="flex flex-col space-y-2 pt-4">
|
||||
<>
|
||||
<Sheet open={isOpen} onOpenChange={(v) => setIsOpen(v)}>
|
||||
<SheetContent className="flex h-full w-[400rem] flex-col bg-white lg:max-w-lg xl:max-w-2xl">
|
||||
<SheetHeader>
|
||||
<SheetTitle>
|
||||
<span className="mr-3">{insight.title}</span>
|
||||
{insight.category === "complaint" ? (
|
||||
<Badge text="Complaint" type="error" size="tiny" />
|
||||
) : insight.category === "featureRequest" ? (
|
||||
<Badge text="Request" type="warning" size="tiny" />
|
||||
) : insight.category === "praise" ? (
|
||||
<Badge text="Praise" type="success" size="tiny" />
|
||||
) : null}
|
||||
</SheetTitle>
|
||||
<SheetDescription>{insight.description}</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="flex flex-1 flex-col space-y-2 overflow-auto pt-4">
|
||||
{documents.map((document) => (
|
||||
<Card>
|
||||
<CardContent className="p-4 text-sm">
|
||||
@@ -86,8 +104,22 @@ export const InsightSheet = ({ isOpen, setIsOpen, insight, surveyId, questionId
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</SheetHeader>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
<SheetFooter>
|
||||
<div className="flex items-center gap-2">
|
||||
<p>Did you find this insight helpful?</p>
|
||||
<ThumbsUpIcon
|
||||
className="upvote h-5 w-5 cursor-pointer"
|
||||
onClick={() => handleFeedbackClick("positive")}
|
||||
/>
|
||||
<ThumbsDownIcon
|
||||
className="downvote h-5 w-5 cursor-pointer"
|
||||
onClick={() => handleFeedbackClick("negative")}
|
||||
/>
|
||||
</div>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user