Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 0489921

Browse files
authored
File Uploader (#8)
2 parents 90f2df9 + 8b8df42 commit 0489921

File tree

7 files changed

+192
-5
lines changed

7 files changed

+192
-5
lines changed

app/api/uploadthing/core.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { createUploadthing, type FileRouter } from "uploadthing/next";
2+
import { UploadThingError } from "uploadthing/server";
3+
4+
const f = createUploadthing();
5+
6+
const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function
7+
8+
// FileRouter for your app, can contain multiple FileRoutes
9+
export const ourFileRouter = {
10+
// Define as many FileRoutes as you like, each with a unique routeSlug
11+
imageUploader: f({ image: { maxFileSize: "4MB" } })
12+
// Set permissions and file types for this FileRoute
13+
.middleware(async ({ req }) => {
14+
// This code runs on your server before upload
15+
const user = await auth(req);
16+
17+
// If you throw, the user will not be able to upload
18+
if (!user) throw new UploadThingError("Unauthorized");
19+
20+
// Whatever is returned here is accessible in onUploadComplete as `metadata`
21+
return { userId: user.id };
22+
})
23+
.onUploadComplete(async ({ metadata, file }) => {
24+
// This code RUNS ON YOUR SERVER after upload
25+
console.log("Upload complete for userId:", metadata.userId);
26+
27+
console.log("file url", file.url);
28+
29+
// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
30+
return { uploadedBy: metadata.userId };
31+
}),
32+
} satisfies FileRouter;
33+
34+
export type OurFileRouter = typeof ourFileRouter;

app/api/uploadthing/route.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createNextRouteHandler } from "uploadthing/next";
2+
3+
import { ourFileRouter } from "./core";
4+
5+
// Export routes for Next App Router
6+
export const { GET, POST } = createNextRouteHandler({
7+
router: ourFileRouter,
8+
});

components/shared/EventForm.tsx

+12-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { useRouter } from "next/navigation";
3030
import { createEvent, updateEvent } from "@/lib/actions/event.actions";
3131
import { IEvent } from "@/lib/database/models/event.model";
3232
import Dropdown from "./Dropdown";
33+
import { FileUploader } from "./FileUploader";
3334

3435
type EventFormProps = {
3536
userId: string;
@@ -39,6 +40,8 @@ type EventFormProps = {
3940
};
4041

4142
const EventForm = ({ userId, type, event, eventId }: EventFormProps) => {
43+
const [files, setFiles] = useState<File[]>([]);
44+
4245
const initialValues =
4346
event && type === "Update"
4447
? {
@@ -116,16 +119,22 @@ const EventForm = ({ userId, type, event, eventId }: EventFormProps) => {
116119
</FormItem>
117120
)}
118121
/>
119-
{/* <FormField
122+
<FormField
120123
control={form.control}
121124
name="imageUrl"
122125
render={({ field }) => (
123126
<FormItem className="w-full">
124-
<FormControl className="h-72"></FormControl>
127+
<FormControl className="h-72">
128+
<FileUploader
129+
onFieldChange={field.onChange}
130+
imageUrl={field.value}
131+
setFiles={setFiles}
132+
/>
133+
</FormControl>
125134
<FormMessage />
126135
</FormItem>
127136
)}
128-
/> */}
137+
/>
129138
</div>
130139

131140
<div className="flex flex-col gap-5 md:flex-grow">

components/shared/FileUploader.tsx

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* eslint-disable react-hooks/exhaustive-deps */
2+
"use client";
3+
4+
import { useCallback, Dispatch, SetStateAction } from "react";
5+
// import type { FileWithPath } from "@uploadthing/react";
6+
import { useDropzone } from "@uploadthing/react/hooks";
7+
import { generateClientDropzoneAccept } from "uploadthing/client";
8+
9+
import { Button } from "@/components/ui/button";
10+
import { convertFileToUrl } from "@/lib/utils";
11+
import Image from "next/image";
12+
13+
type FileUploaderProps = {
14+
onFieldChange: (url: string) => void;
15+
imageUrl: string;
16+
setFiles: Dispatch<SetStateAction<File[]>>;
17+
};
18+
19+
export function FileUploader({
20+
imageUrl,
21+
onFieldChange,
22+
setFiles,
23+
}: FileUploaderProps) {
24+
const onDrop = useCallback((acceptedFiles: File[]) => {
25+
setFiles(acceptedFiles);
26+
onFieldChange(convertFileToUrl(acceptedFiles[0]));
27+
}, []);
28+
29+
const { getRootProps, getInputProps } = useDropzone({
30+
onDrop,
31+
accept: "image/*" ? generateClientDropzoneAccept(["image/*"]) : undefined,
32+
});
33+
34+
return (
35+
<div
36+
{...getRootProps()}
37+
className="flex-center bg-dark-3 flex h-72 cursor-pointer flex-col overflow-hidden rounded-xl bg-grey-50"
38+
>
39+
<input {...getInputProps()} className="cursor-pointer" />
40+
41+
{imageUrl ? (
42+
<div className="flex h-full w-full flex-1 justify-center">
43+
<Image
44+
src={imageUrl}
45+
alt="image"
46+
width={250}
47+
height={250}
48+
className="w-full object-cover object-center"
49+
/>
50+
</div>
51+
) : (
52+
<div className="flex-center flex-col py-5 text-grey-500">
53+
<Image
54+
src="/assets/icons/upload.svg"
55+
width={77}
56+
height={77}
57+
alt="file upload"
58+
/>
59+
<h3 className="mb-2 mt-2">Drag photo here</h3>
60+
<p className="p-medium-12 mb-4">SVG, PNG, JPG</p>
61+
<Button type="button" className="rounded-full">
62+
Select from computer
63+
</Button>
64+
</div>
65+
)}
66+
</div>
67+
);
68+
}

lib/uploadthing.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { generateReactHelpers } from "@uploadthing/react/hooks";
2+
3+
import type { OurFileRouter } from "@/app/api/uploadthing/core";
4+
5+
export const { useUploadThing, uploadFiles } =
6+
generateReactHelpers<OurFileRouter>();

package-lock.json

+62-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@radix-ui/react-separator": "^1.1.0",
2020
"@radix-ui/react-slot": "^1.1.0",
2121
"@stripe/stripe-js": "^2.2.1",
22+
"@uploadthing/react": "^6.0.3",
2223
"class-variance-authority": "^0.7.0",
2324
"clsx": "^2.1.1",
2425
"lucide-react": "^0.399.0",
@@ -33,7 +34,7 @@
3334
"svix": "^1.24.0",
3435
"tailwind-merge": "^2.3.0",
3536
"tailwindcss-animate": "^1.0.7",
36-
"uploadthing": "^6.13.2",
37+
"uploadthing": "^6.0.4",
3738
"zod": "^3.23.8"
3839
},
3940
"devDependencies": {

0 commit comments

Comments
 (0)