From 6ac952f23ac308b5c7738abbaffc8f006a3ac77a Mon Sep 17 00:00:00 2001 From: fevziatanoglu Date: Sat, 16 Nov 2024 21:50:21 +0300 Subject: [PATCH] User Actions --- components/forms/AccountProfile.tsx | 58 ++++++++++++++++++----------- libs/actions/user.actions.ts | 39 ++++++++++++++++--- libs/models/user.model.ts | 17 +++++++++ libs/mongoose.ts | 3 ++ libs/validations/userValidation.ts | 17 +++++++++ package-lock.json | 12 +++++- package.json | 3 +- 7 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 libs/validations/userValidation.ts diff --git a/components/forms/AccountProfile.tsx b/components/forms/AccountProfile.tsx index 9e37d75..755be54 100644 --- a/components/forms/AccountProfile.tsx +++ b/components/forms/AccountProfile.tsx @@ -1,6 +1,8 @@ "use client"; -import { FormEvent, useEffect, useRef, useState } from "react"; +import { updateUser } from "@/libs/actions/user.actions"; +import { usePathname, useRouter } from "next/navigation"; +import { FormEvent, useState } from "react"; interface Props { user: { @@ -15,37 +17,49 @@ interface Props { } export default function AccountProfile({ user, btnTitle }: Props) { + // ADD VALIDATION const [imgUrl, setImgUrl] = useState(user.image); - const imgUrlRef = useRef(null); + const pathname = usePathname(); + const router = useRouter(); + // SUBMIT + async function handleSubmit(event: FormEvent) { + event.preventDefault(); + const data = new FormData(event.currentTarget); + await updateUser( + user.id, + data.get("username") as string, + data.get("name") as string, + data.get("bio") as string, + // data.get("image") as string, + (imgUrl || "/profile.svg") as string, + pathname + ) + .then((res) => { + console.log(res); + if (pathname == "/profile/edit") { + router.back(); + } else { + router.push("/"); + } + }) + .catch((err) => { + console.log(err); + }); + } function handleImgChange(event: React.ChangeEvent) { const file = event.target.files?.[0]; if (file) { const reader = new FileReader(); - - // When the file is loaded, set the image URL to its data URL reader.onload = () => { setImgUrl(reader.result as string); }; - - reader.readAsDataURL(file); // Read the file as a data URL + reader.readAsDataURL(file); } else { - setImgUrl(null); // Clear the preview if no file is selected + setImgUrl(null); } } - function deleteSelectedImg() { - setImgUrl(null); - } - - function handleSubmit(event: FormEvent) { - event.preventDefault(); - // const data = new FormData(event.currentTarget); - const data = new FormData(event.currentTarget); - - console.log(Object.fromEntries(data.entries()), imgUrl, data.get("image")); - } - return (
Account Settings @@ -66,7 +80,7 @@ export default function AccountProfile({ user, btnTitle }: Props) { > Choose a profile picture - {imgUrl && ( + {/* {imgUrl && ( - )} + )} */} @@ -134,6 +149,7 @@ export default function AccountProfile({ user, btnTitle }: Props) {
diff --git a/libs/actions/user.actions.ts b/libs/actions/user.actions.ts index 4e6ecac..fa15d88 100644 --- a/libs/actions/user.actions.ts +++ b/libs/actions/user.actions.ts @@ -1,9 +1,38 @@ -"use server" +"use server"; -import connectToDB from "../mongoose" +import { revalidatePath } from "next/cache"; +import User from "../models/user.model"; +import connectToDB from "../mongoose"; +export async function updateUser( + userId: string, + username: string , + name: string, + bio: string, + image: string, + path: string +): Promise { + connectToDB(); -export async function updateUser(): Promise { - connectToDB(); + try { + await User.findOneAndUpdate( + { id: userId }, + { + username: username.toLowerCase(), + name, + bio, + image, + onboarded: true, + }, + // upsert => update if exist , create if not exist + { upsert: true } + ); -} \ No newline at end of file + // update cached data at profile/edit path (without waiting revalidation period) + if (path === "profile/edit") { + revalidatePath(path); + } + } catch (error: any) { + throw new Error(error.message); + } +} diff --git a/libs/models/user.model.ts b/libs/models/user.model.ts index e69de29..a094842 100644 --- a/libs/models/user.model.ts +++ b/libs/models/user.model.ts @@ -0,0 +1,17 @@ +import mongoose from "mongoose"; + + +const userSchema = new mongoose.Schema({ + id: { type:String , require: true}, + username : { type:String , require: true}, + name : { type:String }, + bio : { type:String }, + image : { type:String }, + posts: [{ type: mongoose.Schema.Types.ObjectId, ref: "Post" }], + onboarded : { type:Boolean , default: false} + +}); + +// from database || create new model +const User = mongoose.models.User || mongoose.model("User" , userSchema); +export default User; diff --git a/libs/mongoose.ts b/libs/mongoose.ts index 3bf31df..5c80d7b 100644 --- a/libs/mongoose.ts +++ b/libs/mongoose.ts @@ -5,8 +5,11 @@ let isConnectedDB = false; export default async function connectToDB() { if (!process.env.MONGODB_URL) return console.log("DB URL NOT FOUND"); if (isConnectedDB) return console.log("Already connected to MongoDB"); + console.log(process.env.MONGODB_URL) try { await mongoose.connect(process.env.MONGODB_URL); + console.log("Connected to MongoDB"); + } catch (err) { console.log(err); } diff --git a/libs/validations/userValidation.ts b/libs/validations/userValidation.ts new file mode 100644 index 0000000..12134ef --- /dev/null +++ b/libs/validations/userValidation.ts @@ -0,0 +1,17 @@ +import * as z from "zod"; + +export const UserValidation = z.object({ + image: z.string().url().nonempty(), + name: z + .string() + .min(3, { message: "Minimum 3 characters." }) + .max(30, { message: "Maximum 30 caracters." }), + username: z + .string() + .min(3, { message: "Minimum 3 characters." }) + .max(30, { message: "Maximum 30 caracters." }), + bio: z + .string() + .min(3, { message: "Minimum 3 characters." }) + .max(1000, { message: "Maximum 1000 caracters." }), +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0d07563..a163a8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "mongoose": "^8.8.1", "next": "15.0.3", "react": "19.0.0-rc-66855b96-20241106", - "react-dom": "19.0.0-rc-66855b96-20241106" + "react-dom": "19.0.0-rc-66855b96-20241106", + "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20", @@ -2891,6 +2892,15 @@ "engines": { "node": ">= 14" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index d4eef97..bb0ef8c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "mongoose": "^8.8.1", "next": "15.0.3", "react": "19.0.0-rc-66855b96-20241106", - "react-dom": "19.0.0-rc-66855b96-20241106" + "react-dom": "19.0.0-rc-66855b96-20241106", + "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20",