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

* [offers][feat] Add toasts

* [offers][fix] Disable empty comments
pull/419/head
Ai Ling 2 years ago committed by GitHub
parent c0f92584ef
commit c3d2b4d325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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}

@ -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}

@ -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}

@ -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',
});
}, },
}); });

@ -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));
} }

Loading…
Cancel
Save