mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-27 20:22:33 +08:00
[offers][feat] delete comment (#464)
* [offers][feat] delete comment * [offers][style] arrange loading spinner in profile
This commit is contained in:
@ -1,7 +1,10 @@
|
|||||||
import { signIn, useSession } from 'next-auth/react';
|
import { signIn, useSession } from 'next-auth/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ChatBubbleBottomCenterIcon } from '@heroicons/react/24/outline';
|
import {
|
||||||
import { Button, HorizontalDivider, TextArea } from '@tih/ui';
|
ChatBubbleBottomCenterIcon,
|
||||||
|
TrashIcon,
|
||||||
|
} from '@heroicons/react/24/outline';
|
||||||
|
import { Button, Dialog, HorizontalDivider, TextArea, useToast } from '@tih/ui';
|
||||||
|
|
||||||
import { timeSinceNow } from '~/utils/offers/time';
|
import { timeSinceNow } from '~/utils/offers/time';
|
||||||
|
|
||||||
@ -25,12 +28,15 @@ export default function CommentCard({
|
|||||||
handleExpanded,
|
handleExpanded,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
profileId,
|
profileId,
|
||||||
token = '',
|
|
||||||
replyLength = 0,
|
replyLength = 0,
|
||||||
|
token = '',
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
const [isReplying, setIsReplying] = useState(false);
|
const [isReplying, setIsReplying] = useState(false);
|
||||||
const [currentReply, setCurrentReply] = useState<string>('');
|
const [currentReply, setCurrentReply] = useState<string>('');
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
const { showToast } = useToast();
|
||||||
|
const deletable: boolean = token.length > 0 || user?.id === session?.user?.id;
|
||||||
|
|
||||||
const trpcContext = trpc.useContext();
|
const trpcContext = trpc.useContext();
|
||||||
const createCommentMutation = trpc.useMutation(['offers.comments.create'], {
|
const createCommentMutation = trpc.useMutation(['offers.comments.create'], {
|
||||||
@ -91,6 +97,33 @@ export default function CommentCard({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteCommentMutation = trpc.useMutation(['offers.comments.delete'], {
|
||||||
|
onSuccess() {
|
||||||
|
trpcContext.invalidateQueries([
|
||||||
|
'offers.comments.getComments',
|
||||||
|
{ profileId },
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
function handleDelete() {
|
||||||
|
deleteCommentMutation.mutate(
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
profileId,
|
||||||
|
token,
|
||||||
|
userId: session?.user?.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onError: () => {
|
||||||
|
showToast({ title: `Server Error`, variant: 'failure' });
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
showToast({ title: `Deleted comment`, variant: 'success' });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex pl-2">
|
<div className="flex pl-2">
|
||||||
@ -122,6 +155,47 @@ export default function CommentCard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{deletable && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
disabled={deleteCommentMutation.isLoading}
|
||||||
|
icon={TrashIcon}
|
||||||
|
isLabelHidden={true}
|
||||||
|
isLoading={deleteCommentMutation.isLoading}
|
||||||
|
label="Delete"
|
||||||
|
size="sm"
|
||||||
|
variant="tertiary"
|
||||||
|
onClick={() => setIsDialogOpen(true)}
|
||||||
|
/>
|
||||||
|
{isDialogOpen && (
|
||||||
|
<Dialog
|
||||||
|
isShown={isDialogOpen}
|
||||||
|
primaryButton={
|
||||||
|
<Button
|
||||||
|
display="block"
|
||||||
|
label="Delete"
|
||||||
|
variant="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
handleDelete();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
secondaryButton={
|
||||||
|
<Button
|
||||||
|
display="block"
|
||||||
|
label="Cancel"
|
||||||
|
variant="tertiary"
|
||||||
|
onClick={() => setIsDialogOpen(false)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
title="Are you sure you want to delete this comment?"
|
||||||
|
onClose={() => setIsDialogOpen(false)}>
|
||||||
|
<div>You cannot undo this operation.</div>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!disableReply && isReplying && (
|
{!disableReply && isReplying && (
|
||||||
<div className="mt-2 mr-2">
|
<div className="mt-2 mr-2">
|
||||||
@ -137,7 +211,9 @@ export default function CommentCard({
|
|||||||
<div className="w-fit">
|
<div className="w-fit">
|
||||||
<Button
|
<Button
|
||||||
disabled={
|
disabled={
|
||||||
!currentReply.length || createCommentMutation.isLoading
|
!currentReply.length ||
|
||||||
|
createCommentMutation.isLoading ||
|
||||||
|
deleteCommentMutation.isLoading
|
||||||
}
|
}
|
||||||
display="block"
|
display="block"
|
||||||
isLabelHidden={false}
|
isLabelHidden={false}
|
||||||
|
@ -35,6 +35,7 @@ export default function ExpandableCommentCard({
|
|||||||
comment={reply}
|
comment={reply}
|
||||||
disableReply={true}
|
disableReply={true}
|
||||||
profileId={profileId}
|
profileId={profileId}
|
||||||
|
token={token}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import Error from 'next/error';
|
import Error from 'next/error';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { Spinner, useToast } from '@tih/ui';
|
||||||
|
|
||||||
import { ProfileDetailTab } from '~/components/offers/constants';
|
import { ProfileDetailTab } from '~/components/offers/constants';
|
||||||
import ProfileComments from '~/components/offers/profile/ProfileComments';
|
import ProfileComments from '~/components/offers/profile/ProfileComments';
|
||||||
@ -14,7 +15,6 @@ import { HOME_URL } from '~/components/offers/types';
|
|||||||
import type { JobTitleType } from '~/components/shared/JobTitles';
|
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||||
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||||
|
|
||||||
import { useToast } from '~/../../../packages/ui/dist';
|
|
||||||
import { convertMoneyToString } from '~/utils/offers/currency';
|
import { convertMoneyToString } from '~/utils/offers/currency';
|
||||||
import { getProfilePath } from '~/utils/offers/link';
|
import { getProfilePath } from '~/utils/offers/link';
|
||||||
import { formatDate } from '~/utils/offers/time';
|
import { formatDate } from '~/utils/offers/time';
|
||||||
@ -180,7 +180,15 @@ export default function OfferProfile() {
|
|||||||
<Error statusCode={404} title="Requested profile does not exist" />
|
<Error statusCode={404} title="Requested profile does not exist" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!getProfileQuery.isError && (
|
{getProfileQuery.isLoading && (
|
||||||
|
<div className="flex h-screen w-screen">
|
||||||
|
<div className="m-auto mx-auto w-screen justify-center">
|
||||||
|
<Spinner display="block" size="lg" />
|
||||||
|
<div className="text-center">Loading...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!getProfileQuery.isLoading && !getProfileQuery.isError && (
|
||||||
<div className="mb-4 flex flex h-screen w-screen items-center justify-center divide-x">
|
<div className="mb-4 flex flex h-screen w-screen items-center justify-center divide-x">
|
||||||
<div className="h-full w-2/3 divide-y">
|
<div className="h-full w-2/3 divide-y">
|
||||||
<ProfileHeader
|
<ProfileHeader
|
||||||
|
Reference in New Issue
Block a user