[offers][feat] Add toast (#417)

* [offers][feat] Add toasts

* [offers][fix] Disable empty comments
This commit is contained in:
Ai Ling
2022-10-23 18:51:12 +08:00
committed by GitHub
parent c0f92584ef
commit c3d2b4d325
5 changed files with 65 additions and 31 deletions

View File

@ -1,9 +1,9 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useState } from 'react'; // Import { useState } from 'react';
import { setTimeout } from 'timers'; // import { setTimeout } from 'timers';
import { CheckIcon, DocumentDuplicateIcon } from '@heroicons/react/20/solid'; import { DocumentDuplicateIcon } from '@heroicons/react/20/solid';
import { BookmarkSquareIcon, EyeIcon } from '@heroicons/react/24/outline'; import { EyeIcon } from '@heroicons/react/24/outline';
import { Button, TextInput } from '@tih/ui'; import { Button, TextInput, useToast } from '@tih/ui';
import { import {
copyProfileLink, copyProfileLink,
@ -20,18 +20,18 @@ export default function OffersProfileSave({
profileId, profileId,
token, token,
}: OfferProfileSaveProps) { }: OfferProfileSaveProps) {
const [linkCopied, setLinkCopied] = useState(false); const { showToast } = useToast();
const [isSaving, setSaving] = useState(false); // Const [isSaving, setSaving] = useState(false);
const [isSaved, setSaved] = useState(false); // const [isSaved, setSaved] = useState(false);
const router = useRouter(); const router = useRouter();
const saveProfile = () => { // Const saveProfile = () => {
setSaving(true); // setSaving(true);
setTimeout(() => { // setTimeout(() => {
setSaving(false); // setSaving(false);
setSaved(true); // setSaved(true);
}, 5); // }, 5);
}; // };
return ( return (
<div className="flex w-full justify-center"> <div className="flex w-full justify-center">
@ -44,7 +44,7 @@ export default function OffersProfileSave({
To keep you offer profile strictly anonymous, only people who have the To keep you offer profile strictly anonymous, only people who have the
link below can edit it. link below can edit it.
</p> </p>
<div className="mb-5 grid grid-cols-12 gap-4"> <div className="mb-20 grid grid-cols-12 gap-4">
<div className="col-span-11"> <div className="col-span-11">
<TextInput <TextInput
disabled={true} disabled={true}
@ -59,17 +59,15 @@ export default function OffersProfileSave({
label="Copy" label="Copy"
variant="primary" variant="primary"
onClick={() => { onClick={() => {
copyProfileLink(profileId, token), setLinkCopied(true); copyProfileLink(profileId, token);
showToast({
title: `Profile edit link copied to clipboard!`,
variant: 'success',
});
}} }}
/> />
</div> </div>
<div className="mb-20"> {/* <p className="mb-5 text-gray-900">
{linkCopied && (
<p className="text-purple-700">Link copied to clipboard!</p>
)}
</div>
<p className="mb-5 text-gray-900">
If you do not want to keep the edit link, you can opt to save this If you do not want to keep the edit link, you can opt to save this
profile under your user account. It will still only be editable by profile under your user account. It will still only be editable by
you. you.
@ -83,7 +81,7 @@ export default function OffersProfileSave({
variant="primary" variant="primary"
onClick={saveProfile} onClick={saveProfile}
/> />
</div> </div> */}
<div> <div>
<Button <Button
icon={EyeIcon} icon={EyeIcon}

View File

@ -1,7 +1,13 @@
import { signIn, useSession } from 'next-auth/react'; import { signIn, useSession } from 'next-auth/react';
import { useState } from 'react'; import { useState } from 'react';
import { ClipboardDocumentIcon, ShareIcon } from '@heroicons/react/24/outline'; import { ClipboardDocumentIcon, ShareIcon } from '@heroicons/react/24/outline';
import { Button, HorizontalDivider, Spinner, TextArea } from '@tih/ui'; import {
Button,
HorizontalDivider,
Spinner,
TextArea,
useToast,
} from '@tih/ui';
import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard'; import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard';
@ -30,6 +36,7 @@ export default function ProfileComments({
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const [currentReply, setCurrentReply] = useState<string>(''); const [currentReply, setCurrentReply] = useState<string>('');
const [replies, setReplies] = useState<Array<Reply>>(); const [replies, setReplies] = useState<Array<Reply>>();
const { showToast } = useToast();
const commentsQuery = trpc.useQuery( const commentsQuery = trpc.useQuery(
['offers.comments.getComments', { profileId }], ['offers.comments.getComments', { profileId }],
@ -51,6 +58,10 @@ export default function ProfileComments({
}); });
function handleComment(message: string) { function handleComment(message: string) {
if (!currentReply.length) {
return;
}
if (isEditable) { if (isEditable) {
// If it is with edit permission, send comment to API with username = null // If it is with edit permission, send comment to API with username = null
createCommentMutation.mutate( createCommentMutation.mutate(
@ -104,7 +115,13 @@ export default function ProfileComments({
label="Copy profile edit link" label="Copy profile edit link"
size="sm" size="sm"
variant="secondary" variant="secondary"
onClick={() => copyProfileLink(profileId, token)} onClick={() => {
copyProfileLink(profileId, token);
showToast({
title: `Profile edit link copied to clipboard!`,
variant: 'success',
});
}}
/> />
)} )}
<Button <Button
@ -115,7 +132,13 @@ export default function ProfileComments({
label="Copy public link" label="Copy public link"
size="sm" size="sm"
variant="secondary" variant="secondary"
onClick={() => copyProfileLink(profileId)} onClick={() => {
copyProfileLink(profileId);
showToast({
title: `Public profile link copied to clipboard!`,
variant: 'success',
});
}}
/> />
</div> </div>
<h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2> <h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2>
@ -131,7 +154,7 @@ export default function ProfileComments({
<div className="mt-2 flex w-full justify-end"> <div className="mt-2 flex w-full justify-end">
<div className="w-fit"> <div className="w-fit">
<Button <Button
disabled={commentsQuery.isLoading} disabled={commentsQuery.isLoading || !currentReply.length}
display="block" display="block"
isLabelHidden={false} isLabelHidden={false}
isLoading={createCommentMutation.isLoading} isLoading={createCommentMutation.isLoading}

View File

@ -43,6 +43,10 @@ export default function CommentCard({
}); });
function handleReply() { function handleReply() {
if (!currentReply.length) {
return;
}
if (token && token.length > 0) { if (token && token.length > 0) {
// If it is with edit permission, send comment to API with username = null // If it is with edit permission, send comment to API with username = null
createCommentMutation.mutate( createCommentMutation.mutate(
@ -132,6 +136,7 @@ export default function CommentCard({
<div className="mt-2 flex w-full justify-end"> <div className="mt-2 flex w-full justify-end">
<div className="w-fit"> <div className="w-fit">
<Button <Button
disabled={!currentReply.length}
display="block" display="block"
isLabelHidden={false} isLabelHidden={false}
isLoading={createCommentMutation.isLoading} isLoading={createCommentMutation.isLoading}

View File

@ -11,6 +11,7 @@ import type {
OfferDisplayData, OfferDisplayData,
} from '~/components/offers/types'; } from '~/components/offers/types';
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';
@ -19,6 +20,7 @@ import { trpc } from '~/utils/trpc';
import type { Profile, ProfileAnalysis, ProfileOffer } from '~/types/offers'; import type { Profile, ProfileAnalysis, ProfileOffer } from '~/types/offers';
export default function OfferProfile() { export default function OfferProfile() {
const { showToast } = useToast();
const ErrorPage = ( const ErrorPage = (
<Error statusCode={404} title="Requested profile does not exist." /> <Error statusCode={404} title="Requested profile does not exist." />
); );
@ -131,11 +133,18 @@ export default function OfferProfile() {
const trpcContext = trpc.useContext(); const trpcContext = trpc.useContext();
const deleteMutation = trpc.useMutation(['offers.profile.delete'], { const deleteMutation = trpc.useMutation(['offers.profile.delete'], {
onError: () => { onError: () => {
alert('Error deleting profile'); // TODO: replace with toast showToast({
title: `Error deleting offers profile.`,
variant: 'failure',
});
}, },
onSuccess: () => { onSuccess: () => {
trpcContext.invalidateQueries(['offers.profile.listOne']); trpcContext.invalidateQueries(['offers.profile.listOne']);
router.push('/offers'); router.push('/offers');
showToast({
title: `Offers profile successfully deleted!`,
variant: 'success',
});
}, },
}); });

View File

@ -3,7 +3,6 @@ export function getProfileLink(profileId: string, token?: string) {
} }
export function copyProfileLink(profileId: string, token?: string) { export function copyProfileLink(profileId: string, token?: string) {
// TODO: Add notification
navigator.clipboard.writeText(getProfileLink(profileId, token)); navigator.clipboard.writeText(getProfileLink(profileId, token));
} }