Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/components/entities/rows/RowLogComment.tsx
T

134 lines
5.7 KiB
TypeScript

import { useLocation, useSubmit, useNavigation } from "react-router";
import clsx from "clsx";
import { useTranslation } from "react-i18next";
import ApiKeyBadge from "~/components/core/users/ApiKeyBadge";
import UserAvatarBadge from "~/components/core/users/UserAvatarBadge";
import UserBadge from "~/components/core/users/UserBadge";
import ChatAltIcon from "~/components/ui/icons/ChatAltIcon";
import ThumbsUpEmptyIcon from "~/components/ui/icons/ThumbsUpEmptyIcon";
import ThumbsUpFilledIcon from "~/components/ui/icons/ThumbsUpFilledIcon";
import TrashEmptyIcon from "~/components/ui/icons/TrashEmptyIcon";
import { useAppOrAdminData } from "~/utils/data/useAppOrAdminData";
import { LogWithDetails } from "~/utils/db/logs.db.server";
import { appendCSRFToken } from "~/utils/csrf";
import { renderSanitizedMarkdown } from "~/utils/html-sanitizer";
import { useLocaleFormatters } from "~/locale/format";
interface Props {
item: LogWithDetails;
}
export default function RowLogComment({ item }: Props) {
const { t } = useTranslation();
const formatters = useLocaleFormatters();
const submit = useSubmit();
const { user } = useAppOrAdminData();
const navigation = useNavigation();
const location = useLocation();
const isAdding = navigation.state === "submitting" && navigation.formData?.get("action") === "comment-reaction";
const isDeleting = navigation.state === "submitting" && navigation.formData?.get("action") === "comment-delete";
function onReacted(reaction: string) {
const form = new FormData();
appendCSRFToken(form);
form.set("action", "comment-reaction");
form.set("comment-id", item.comment?.id ?? "");
form.set("reaction", reaction);
submit(form, {
method: "post",
action: location.pathname + location.search,
});
}
function onDeleted() {
const form = new FormData();
appendCSRFToken(form);
form.set("action", "comment-delete");
form.set("comment-id", item.comment?.id ?? "");
submit(form, {
method: "post",
action: location.pathname + location.search,
});
}
function getReactions(reaction: string) {
return item.comment?.reactions.filter((f) => f.reaction === reaction) ?? [];
}
function hasMyReaction(reaction: string) {
return item.comment?.reactions.find((f) => f.createdByUserId === user?.id && f.reaction === reaction);
}
return (
<>
{item.comment && (
<>
<div className="relative px-1">
<UserAvatarBadge className="h-9 w-9" avatar={item.user?.avatar} />
<span className="bg-secondary absolute -bottom-0.5 -right-1 rounded-tl px-0.5 py-px">
<ChatAltIcon className="text-muted-foreground h-4 w-4" aria-hidden="true" />
</span>
</div>
<div className="group min-w-0 flex-1 space-y-0.5">
<div>
<div className="text-sm">
<div className="text-foreground font-medium">
{item.user && <UserBadge item={item.user} withEmail={false} />}
{item.apiKey && <ApiKeyBadge item={item.apiKey} />}
</div>
</div>
</div>
<div className="text-muted-foreground text-sm font-normal">
{item.comment.isDeleted ? (
<p className="text-muted-foreground italic">{t("shared.commentDeleted")}</p>
) : (
<div className="prose border-border rounded-md border border-dashed p-2 text-sm">
<div dangerouslySetInnerHTML={{ __html: renderSanitizedMarkdown(item.comment.value) }} />
</div>
)}
</div>
<div className="text-muted-foreground flex items-center justify-between space-x-1 font-medium">
<time dateTime={new Date(item.createdAt).toISOString()} title={formatters.dateTime(item.createdAt, { timeStyle: "medium" })}>
{formatters.relativeDate(item.createdAt)}
</time>
<div className="group flex items-center space-x-2">
{!item.comment.isDeleted && item.comment.createdByUserId === user?.id && (
<button
type="button"
disabled={isDeleting}
onClick={() => onDeleted()}
className="text-muted-foreground hover:text-muted-foreground invisible p-0.5 focus:outline-hidden group-hover:visible"
>
<div className="flex items-center space-x-1">
<TrashEmptyIcon className="h-4 w-4" />
</div>
</button>
)}
<button
type="button"
disabled={isAdding}
onClick={() => onReacted("like")}
className={clsx(
"text-muted-foreground hover:text-muted-foreground p-0.5 focus:outline-hidden",
getReactions("like").length === 0 && "invisible group-hover:visible"
)}
>
<div
className="flex items-center space-x-1"
title={
getReactions("like")
?.map((f) => `${f.createdByUser?.firstName} ${f.createdByUser?.lastName}`)
.join(", ") ?? ""
}
>
{(getReactions("like")?.length ?? 0) > 0 && <div className="">{getReactions("like")?.length ?? 0}</div>}
{hasMyReaction("like") ? <ThumbsUpFilledIcon className="h-4 w-4" /> : <ThumbsUpEmptyIcon className="h-4 w-4" />}
</div>
</button>
</div>
</div>
</div>
</>
)}
</>
);
}