Skip to content

Commit 5d6b354

Browse files
committed
feat(resource): 🚸 added toast acknowledgement to notify user
1 parent 695f062 commit 5d6b354

File tree

7 files changed

+51
-24
lines changed

7 files changed

+51
-24
lines changed

app/(main)/projects/[id]/settings/danger-zone/page.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,10 @@ const ProjectSettingsDangerZonePage = ({
2525
);
2626
const router = useRouter();
2727

28-
const handleDeleteProject = () => {
29-
deleteProject({ id })
30-
.then(() => {
31-
toast.success("Project deleted successfully");
32-
router.push(DASHBOARD_ROUTE);
33-
})
34-
.catch(() => {
35-
toast.error("Failed to delete project");
36-
});
37-
};
28+
const handleDeleteProject = () =>
29+
deleteProject({ id }).then(() => {
30+
router.push(DASHBOARD_ROUTE);
31+
});
3832

3933
return (
4034
<div>
@@ -44,6 +38,7 @@ const ProjectSettingsDangerZonePage = ({
4438
onConfirm={handleDeleteProject}
4539
header="Delete project"
4640
disabled={isPending}
41+
toastMessage="Project deleted successfully"
4742
>
4843
<Button variant="destructive" disabled={isPending}>
4944
Delete Project

components/modals/confirm-modal.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import {
1010
} from "@/components/ui/alert-dialog";
1111
import { ReactNode } from "react";
1212
import { buttonVariants } from "../ui/button";
13+
import { toast } from "sonner";
1314

1415
type ConfirmModalProps = {
1516
children: ReactNode;
1617
disabled?: boolean;
17-
onConfirm: () => void;
18+
onConfirm: () => Promise<any>;
1819
header: string;
1920
description?: string;
21+
toastMessage?: string;
2022
};
2123

2224
const ConfirmModal = ({
@@ -25,9 +27,12 @@ const ConfirmModal = ({
2527
disabled,
2628
header,
2729
onConfirm,
30+
toastMessage = "Action completed successfully.",
2831
}: ConfirmModalProps) => {
2932
const handleConfirm = () => {
30-
onConfirm();
33+
onConfirm()
34+
.then(() => toast.success(toastMessage))
35+
.catch(() => toast.error("Failed to perform action. Please try again."));
3136
};
3237

3338
return (

components/resources/file-card.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const FileCard = ({
6363
header={`Delete file : ${title}`}
6464
onConfirm={() => deleteFile({ _id })}
6565
disabled={isDeleting}
66+
toastMessage="File deleted successfully"
6667
>
6768
<Button size="icon" variant="ghost">
6869
<Trash className="h-5 w-5" />

components/resources/link-card.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { Id } from "@/convex/_generated/dataModel";
44
import useApiMutation from "@/lib/hooks/use-api-mutation";
55
import { useLinkModal } from "@/lib/store/use-link-modal";
66
import { Edit, LinkIcon, Trash } from "lucide-react";
7+
import ConfirmModal from "@/components/modals/confirm-modal";
78
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
8-
import ConfirmModal from "../modals/confirm-modal";
99

1010
type LinkCardProps = {
1111
resource: {
@@ -45,7 +45,7 @@ const LinkCard = ({
4545
className="font-medium hover:underline hover:text-gray-900 dark:hover:text-gray-50"
4646
href={url}
4747
>
48-
{title}
48+
{title.length > 10 ? `${title.slice(0, 10)}...` : title}
4949
</a>
5050
</div>
5151
<div>
@@ -60,6 +60,8 @@ const LinkCard = ({
6060
<ConfirmModal
6161
onConfirm={() => deleteLink({ _id })}
6262
header={`Delete link:${title}`}
63+
disabled={isPending}
64+
toastMessage="Link deleted successfully"
6365
>
6466
<Button size="icon" variant="ghost" disabled={isPending}>
6567
<Trash className="h-5 w-5" />

components/resources/resource-list.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ const ResourceList = () => {
2020
const files = useQuery(api.resources.files.list, {
2121
projectId: param.id as Id<"projects">,
2222
});
23+
2324
return (
2425
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
2526
<Card className="col-span-3 overflow-hidden">
2627
<CardHeader className="bg-gray-100 dark:bg-gray-800 flex-row flex items-center justify-between py-3">
2728
<h2 className="text-lg font-semibold">Files</h2>
28-
<UploadFile />
29+
<UploadFile fileCount={files?.length} />
2930
</CardHeader>
3031
<CardContent className="p-6 grid gap-6">
3132
{files && files.length > 0 ? (

components/resources/upload-file.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@ import { useUploadFiles } from "@xixixao/uploadstuff/react";
55
import { Upload } from "lucide-react";
66
import { useParams } from "next/navigation";
77
import { Button } from "../ui/button";
8+
import { MAX_FILE_COUNT, MAX_FILE_SIZE } from "@/lib/constants";
9+
import { toast } from "sonner";
810

9-
const UploadFile = () => {
11+
type UploadFileProps = {
12+
fileCount?: number;
13+
};
14+
15+
const UploadFile = ({ fileCount = 0 }: UploadFileProps) => {
1016
const params = useParams();
1117

1218
const { mutate: generateUploadUrl, isPending } = useApiMutation(
@@ -23,18 +29,32 @@ const UploadFile = () => {
2329
fileInput.accept = "*/*";
2430

2531
fileInput.addEventListener("change", async (e) => {
32+
if (fileCount >= MAX_FILE_COUNT) {
33+
toast.error(`A project can have a maximum of ${MAX_FILE_COUNT} files.`);
34+
return;
35+
}
36+
2637
const file = (e.target as HTMLInputElement).files?.[0];
27-
if (!file) {
38+
if (!file) return;
39+
40+
if (file.size > MAX_FILE_SIZE) {
41+
toast.error("File size should be less than 5MB");
2842
return;
2943
}
3044

31-
const [res] = await startUpload([file]);
32-
createFileResource({
33-
title: res.name,
34-
storageId: (res.response as { storageId: Id<"_storage"> }).storageId,
35-
projectId: params.id as Id<"projects">,
36-
type: res.type,
37-
});
45+
try {
46+
const [res] = await startUpload([file]);
47+
createFileResource({
48+
title: res.name,
49+
storageId: (res.response as { storageId: Id<"_storage"> }).storageId,
50+
projectId: params.id as Id<"projects">,
51+
type: res.type,
52+
});
53+
54+
toast.success("File uploaded successfully.");
55+
} catch (e) {
56+
toast.error("Failed to upload file. Please try again.");
57+
}
3858
});
3959

4060
fileInput.click();

lib/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import { TaskPriority, TaskStatus, TaskType } from "./types";
1414

1515
export const DASHBOARD_ROUTE = "/dashboard";
1616

17+
export const MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB
18+
export const MAX_FILE_COUNT = 10;
19+
1720
export const UNASSIGNED_USER = {
1821
label: "Unassigned",
1922
value: process.env.UNASSIGNED_USER_ID as Id<"users">,

0 commit comments

Comments
 (0)