mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 12:43:12 +08:00
[offers][feat] Add toast (#417)
* [offers][feat] Add toasts * [offers][fix] Disable empty comments
This commit is contained in:
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user