Skip to content

Commit 452f891

Browse files
add: edit collection name
1 parent 0f9a8f2 commit 452f891

File tree

4 files changed

+204
-10
lines changed

4 files changed

+204
-10
lines changed

src/app/api/collection/[id]/route.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,6 @@ export async function POST(
9898
id: params.id,
9999
madeById: user.id,
100100
},
101-
include: {
102-
memes: true,
103-
},
104101
});
105102

106103
if (!collection) {
@@ -111,6 +108,7 @@ export async function POST(
111108
}
112109

113110
const { collectionId, memeId } = await req.json();
111+
114112
if (!collectionId || !memeId) {
115113
return NextResponse.json(
116114
{ message: "No collection id or meme id" },
@@ -143,7 +141,7 @@ export async function POST(
143141

144142
return NextResponse.json(
145143
{ message: "Saved meme to collection successfully" },
146-
{ status: 200 },
144+
{ status: 201 },
147145
);
148146
} catch (err: any) {
149147
console.error(err);
@@ -153,3 +151,62 @@ export async function POST(
153151
);
154152
}
155153
}
154+
155+
export async function PATCH(
156+
req: NextRequest,
157+
{ params }: { params: { id: string } },
158+
) {
159+
try {
160+
const user = await currentUser();
161+
if (!user) {
162+
return NextResponse.json(
163+
{ message: "User not authenticated" },
164+
{ status: 401 },
165+
);
166+
}
167+
168+
if (!params.id) {
169+
return NextResponse.json(
170+
{ message: "No collection id" },
171+
{ status: 400 },
172+
);
173+
}
174+
175+
const collection = await prisma.collection.findUnique({
176+
where: {
177+
id: params.id,
178+
madeById: user.id,
179+
},
180+
});
181+
182+
if (!collection) {
183+
return NextResponse.json(
184+
{ message: "Collection not found" },
185+
{ status: 404 },
186+
);
187+
}
188+
189+
const { collectionName } = await req.json();
190+
191+
await prisma.collection.update({
192+
where: {
193+
id: params.id,
194+
madeById: user.id,
195+
},
196+
data: {
197+
name: collectionName,
198+
},
199+
});
200+
201+
return NextResponse.json(
202+
{ message: "Edited collection successfully" },
203+
{ status: 200 },
204+
);
205+
} catch (err) {
206+
console.error(err);
207+
return NextResponse.json(
208+
{ message: "Internal Server error" },
209+
{ status: 500 },
210+
);
211+
}
212+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {
2+
Dialog,
3+
DialogContent,
4+
DialogDescription,
5+
DialogFooter,
6+
DialogHeader,
7+
DialogTitle,
8+
DialogTrigger,
9+
} from "@/components/ui/dialog";
10+
import { Edit, FolderPlusIcon, Loader2 } from "lucide-react";
11+
import { Input } from "@/components/ui/input";
12+
import { z } from "zod";
13+
import { zodResolver } from "@hookform/resolvers/zod";
14+
import { useForm } from "react-hook-form";
15+
import {
16+
Form,
17+
FormControl,
18+
FormField,
19+
FormItem,
20+
FormMessage,
21+
} from "@/components/ui/form";
22+
import { useToast } from "@/components/ui/use-toast";
23+
import { useEffect, useState } from "react";
24+
import { useDashboardCtx } from "@/context/DashboardContext";
25+
import { Button } from "@/components/ui/button";
26+
27+
interface EditCollectionProps {
28+
collectionId: string;
29+
name: string;
30+
}
31+
32+
const collectionFormSchema = z.object({
33+
name: z.string().min(3, {
34+
message: "Name must be at least 3 characters",
35+
}),
36+
});
37+
38+
export function EditCollection({ name, collectionId }: EditCollectionProps) {
39+
const { toast } = useToast();
40+
const { refreshCollectionWithMemes } = useDashboardCtx();
41+
const [loading, setLoading] = useState<boolean>(false);
42+
const [open, setOpen] = useState<boolean>(false);
43+
const collectionForm = useForm<z.infer<typeof collectionFormSchema>>({
44+
resolver: zodResolver(collectionFormSchema),
45+
defaultValues: {
46+
name,
47+
},
48+
});
49+
50+
async function onSubmit(values: z.infer<typeof collectionFormSchema>) {
51+
setLoading(true);
52+
try {
53+
const req = await fetch(`/api/collection/${collectionId}`, {
54+
method: "PATCH",
55+
body: JSON.stringify({
56+
collectionName: values.name,
57+
}),
58+
});
59+
console.log(req.status)
60+
if (req.status === 200) {
61+
await refreshCollectionWithMemes();
62+
return toast({
63+
title: "Edit collection!",
64+
description: `${name} was renamed to ${values.name} successfully.`,
65+
});
66+
}
67+
} catch (err: any) {
68+
console.error(err);
69+
return toast({
70+
title: "Uh oh! Couldn't create collection",
71+
description: err.message,
72+
variant: "destructive",
73+
});
74+
} finally {
75+
setOpen(false);
76+
setLoading(false);
77+
collectionForm.reset();
78+
}
79+
}
80+
81+
return (
82+
<Dialog open={open} onOpenChange={setOpen}>
83+
<DialogTrigger asChild>
84+
<Button variant="ghost">
85+
<Edit className="h-5 w-5" />
86+
</Button>
87+
</DialogTrigger>
88+
<DialogContent className="sm:max-w-[425px]">
89+
<DialogHeader>
90+
<DialogTitle>Edit collection</DialogTitle>
91+
<DialogDescription>
92+
Edit this collection&apos;s name
93+
</DialogDescription>
94+
</DialogHeader>
95+
<Form {...collectionForm}>
96+
<form onSubmit={collectionForm.handleSubmit(onSubmit)}>
97+
<FormField
98+
control={collectionForm.control}
99+
name="name"
100+
render={({ field }) => (
101+
<FormItem className="w-full flex flex-col items-center justify-center">
102+
<FormControl>
103+
<Input
104+
className="col-span-3"
105+
placeholder="Twitter"
106+
{...field}
107+
/>
108+
</FormControl>
109+
<FormMessage />
110+
</FormItem>
111+
)}
112+
/>
113+
<DialogFooter className="mt-6">
114+
<Button type="submit" disabled={loading}>
115+
{!loading ? (
116+
"Edit"
117+
) : (
118+
<span className="flex items-center">
119+
<Loader2 className="mr-2 animate-spin" /> Editing
120+
</span>
121+
)}
122+
</Button>
123+
</DialogFooter>
124+
</form>
125+
</Form>
126+
</DialogContent>
127+
</Dialog>
128+
);
129+
}

src/app/dashboard/collections/[id]/page.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,23 @@ import React, { useEffect } from "react";
33
import { useLoadingCtx } from "@/context/LoadingContext";
44
import Loader from "@/components/loader";
55
import Link from "next/link";
6-
import { ChevronLeft, Edit } from "lucide-react";
6+
import { ChevronLeft } from "lucide-react";
77
import Image from "next/image";
88
import MoreInfo from "./_components/more-info";
99
import { useDashboardCtx } from "@/context/DashboardContext";
1010
import { DeleteCollection } from "./_components/delete-collection";
11+
import { EditCollection } from "./_components/edit-collection";
1112

1213
export default function CollectionPage({ params }: { params: { id: string } }) {
13-
const { loading, setLoading } = useLoadingCtx();
14+
const { loading } = useLoadingCtx();
1415
const { collectionWithMemes, getCollectionById } = useDashboardCtx();
1516

1617
useEffect(() => {
17-
getCollectionById(params.id);
18+
async function getCollection() {
19+
await getCollectionById(params.id);
20+
}
21+
22+
getCollection();
1823
}, []);
1924

2025
return (
@@ -44,7 +49,10 @@ export default function CollectionPage({ params }: { params: { id: string } }) {
4449
<h1 className="text-2xl font-bold text-gray-800 dark:text-white">
4550
{collectionWithMemes.name}
4651
</h1>
47-
<Edit />
52+
<EditCollection
53+
collectionId={collectionWithMemes.id}
54+
name={collectionWithMemes.name}
55+
/>
4856
<DeleteCollection collectionId={collectionWithMemes.id} />
4957
</div>
5058
<Link

src/context/DashboardContext.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ type DashboardContextType = {
1717
collections: Collection[];
1818
refreshCollections: () => Promise<void>;
1919
collectionWithMemes: CollectionWithMemes | undefined;
20-
getCollectionById: (id: string) => void;
21-
refreshCollectionWithMemes: () => void;
20+
getCollectionById: (id: string) => Promise<void>;
21+
refreshCollectionWithMemes: () => Promise<void>;
2222
};
2323

2424
const DashboardContext = createContext<DashboardContextType | undefined>(

0 commit comments

Comments
 (0)