mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-22 02:55:04 -05:00
80ef504bef
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com> Co-authored-by: Johannes <johannes@formbricks.com>
222 lines
8.4 KiB
TypeScript
222 lines
8.4 KiB
TypeScript
import { LanguagesIcon, TrashIcon } from "lucide-react";
|
|
import Link from "next/link";
|
|
import { ReactNode } from "react";
|
|
import { getLanguageLabel } from "@formbricks/lib/i18n/utils";
|
|
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
|
import { timeSince } from "@formbricks/lib/time";
|
|
import { TEnvironment } from "@formbricks/types/environment";
|
|
import { TResponse } from "@formbricks/types/responses";
|
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
|
import { TUser } from "@formbricks/types/user";
|
|
import { PersonAvatar } from "../../Avatars";
|
|
import { SurveyStatusIndicator } from "../../SurveyStatusIndicator";
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../Tooltip";
|
|
import { isSubmissionTimeMoreThan5Minutes } from "../util";
|
|
|
|
interface TooltipRendererProps {
|
|
shouldRender: boolean;
|
|
tooltipContent: ReactNode;
|
|
children: ReactNode;
|
|
}
|
|
|
|
interface SingleResponseCardHeaderProps {
|
|
pageType: "people" | "response";
|
|
response: TResponse;
|
|
survey: TSurvey;
|
|
environment: TEnvironment;
|
|
user?: TUser;
|
|
isViewer: boolean;
|
|
setDeleteDialogOpen: (deleteDialogOpen: boolean) => void;
|
|
}
|
|
|
|
export const SingleResponseCardHeader = ({
|
|
pageType,
|
|
response,
|
|
survey,
|
|
environment,
|
|
user,
|
|
isViewer,
|
|
setDeleteDialogOpen,
|
|
}: SingleResponseCardHeaderProps) => {
|
|
const displayIdentifier = response.person
|
|
? getPersonIdentifier(response.person, response.personAttributes)
|
|
: null;
|
|
const environmentId = survey.environmentId;
|
|
const canResponseBeDeleted = response.finished
|
|
? true
|
|
: isSubmissionTimeMoreThan5Minutes(response.updatedAt);
|
|
|
|
const TooltipRenderer = ({ children, shouldRender, tooltipContent }: TooltipRendererProps) => {
|
|
return shouldRender ? (
|
|
<TooltipProvider delayDuration={0}>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
<TooltipContent avoidCollisions align="start" side="bottom" className="max-w-[75vw]">
|
|
{tooltipContent}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
) : (
|
|
<>{children}</>
|
|
);
|
|
};
|
|
|
|
const renderTooltip = Boolean(
|
|
(response.personAttributes && Object.keys(response.personAttributes).length > 0) ||
|
|
(response.meta.userAgent && Object.keys(response.meta.userAgent).length > 0)
|
|
);
|
|
|
|
const tooltipContent = (
|
|
<>
|
|
{response.singleUseId && (
|
|
<div>
|
|
<p className="py-1 font-bold text-slate-700">SingleUse ID:</p>
|
|
<span>{response.singleUseId}</span>
|
|
</div>
|
|
)}
|
|
{response.personAttributes && Object.keys(response.personAttributes).length > 0 && (
|
|
<div>
|
|
<p className="py-1 font-bold text-slate-700">Person attributes:</p>
|
|
{Object.keys(response.personAttributes).map((key) => (
|
|
<p
|
|
key={key}
|
|
className="truncate"
|
|
title={`${key}: ${response.personAttributes && response.personAttributes[key]}`}>
|
|
{key}:{" "}
|
|
<span className="font-bold">{response.personAttributes && response.personAttributes[key]}</span>
|
|
</p>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{response.meta.userAgent && Object.keys(response.meta.userAgent).length > 0 && (
|
|
<div className="text-slate-600">
|
|
{response.personAttributes && Object.keys(response.personAttributes).length > 0 && (
|
|
<hr className="my-2 border-slate-200" />
|
|
)}
|
|
<p className="py-1 font-bold text-slate-700">Device info:</p>
|
|
{response.meta.userAgent?.browser && (
|
|
<p className="truncate" title={`Browser: ${response.meta.userAgent.browser}`}>
|
|
Browser: {response.meta.userAgent.browser}
|
|
</p>
|
|
)}
|
|
{response.meta.userAgent?.os && (
|
|
<p className="truncate" title={`OS: ${response.meta.userAgent.os}`}>
|
|
OS: {response.meta.userAgent.os}
|
|
</p>
|
|
)}
|
|
{response.meta.userAgent && (
|
|
<p
|
|
className="truncate"
|
|
title={`Device: ${response.meta.userAgent.device ? response.meta.userAgent.device : "PC / Generic device"}`}>
|
|
Device:{" "}
|
|
{response.meta.userAgent.device ? response.meta.userAgent.device : "PC / Generic device"}
|
|
</p>
|
|
)}
|
|
{response.meta.url && (
|
|
<p className="truncate" title={`URL: ${response.meta.url}`}>
|
|
URL: {response.meta.url}
|
|
</p>
|
|
)}
|
|
{response.meta.action && (
|
|
<p className="truncate" title={`Action: ${response.meta.action}`}>
|
|
Action: {response.meta.action}
|
|
</p>
|
|
)}
|
|
{response.meta.source && (
|
|
<p className="truncate" title={`Source: ${response.meta.source}`}>
|
|
Source: {response.meta.source}
|
|
</p>
|
|
)}
|
|
{response.meta.country && (
|
|
<p className="truncate" title={`Country: ${response.meta.country}`}>
|
|
Country: {response.meta.country}
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
const deleteSubmissionToolTip = <>This response is in progress.</>;
|
|
|
|
return (
|
|
<div className="space-y-2 border-b border-slate-200 px-6 pb-4 pt-4">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center justify-center space-x-4">
|
|
{pageType === "response" && (
|
|
<TooltipRenderer shouldRender={renderTooltip} tooltipContent={tooltipContent}>
|
|
<div className="group">
|
|
{response.person?.id ? (
|
|
user ? (
|
|
<Link
|
|
className="flex items-center"
|
|
href={`/environments/${environmentId}/people/${response.person.id}`}>
|
|
<PersonAvatar personId={response.person.id} />
|
|
<h3 className="ph-no-capture ml-4 pb-1 font-semibold text-slate-600 hover:underline">
|
|
{displayIdentifier}
|
|
</h3>
|
|
</Link>
|
|
) : (
|
|
<div className="flex items-center">
|
|
<PersonAvatar personId={response.person.id} />
|
|
<h3 className="ph-no-capture ml-4 pb-1 font-semibold text-slate-600">
|
|
{displayIdentifier}
|
|
</h3>
|
|
</div>
|
|
)
|
|
) : (
|
|
<div className="flex items-center">
|
|
<PersonAvatar personId="anonymous" />
|
|
<h3 className="ml-4 pb-1 font-semibold text-slate-600">Anonymous</h3>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</TooltipRenderer>
|
|
)}
|
|
|
|
{pageType === "people" && (
|
|
<div className="flex items-center justify-center space-x-2 rounded-full bg-slate-100 p-1 px-2 text-sm text-slate-600">
|
|
{(survey.type === "link" ||
|
|
environment.appSetupCompleted ||
|
|
environment.websiteSetupCompleted) && <SurveyStatusIndicator status={survey.status} />}
|
|
<Link
|
|
className="hover:underline"
|
|
href={`/environments/${environmentId}/surveys/${survey.id}/summary`}>
|
|
{survey.name}
|
|
</Link>
|
|
</div>
|
|
)}
|
|
{response.language && response.language !== "default" && (
|
|
<div className="flex space-x-2 rounded-full bg-slate-700 px-2 py-1 text-xs text-white">
|
|
<div>{getLanguageLabel(response.language)}</div>
|
|
<LanguagesIcon className="h-4 w-4" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-4 text-sm">
|
|
<time className="text-slate-500" dateTime={timeSince(response.createdAt.toISOString())}>
|
|
{timeSince(response.createdAt.toISOString())}
|
|
</time>
|
|
{user && !isViewer && (
|
|
<TooltipRenderer shouldRender={!canResponseBeDeleted} tooltipContent={deleteSubmissionToolTip}>
|
|
<TrashIcon
|
|
onClick={() => {
|
|
if (canResponseBeDeleted) {
|
|
setDeleteDialogOpen(true);
|
|
}
|
|
}}
|
|
className={`h-4 w-4 ${
|
|
canResponseBeDeleted
|
|
? "cursor-pointer text-slate-500 hover:text-red-700"
|
|
: "cursor-not-allowed text-slate-400"
|
|
} `}
|
|
/>
|
|
</TooltipRenderer>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|