casper's Profile Image

Full Stack Web/Mobile Developer

Jan, 21, 2025

Next Js Tutorial #12 - Ecommerce App - Edit Profile Page & Api Route

We created edit profile page and api route.

Next Js Tutorial #12 - Ecommerce App - Edit Profile Page & Api Route Image

You can also watch the YouTube video:

Next Js Tutorial #12 | Ecommerce App - Edit Profile Page & Api Route

Edit schema.prisma in prisma folder:

// We added fullName and address field to our User model

model User {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  email     String   @unique
  password  String
  fullName  String?
  address   String?
  role      UserRole @default(USER)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  orders    Order[]
}

Push the new schema to our database:

npx prisma db push

Create route.js in app/api/auth/edit-profile folder:

import { NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";
import { cookies } from "next/headers";
import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";

const prisma = new PrismaClient();

export async function PUT(request) {
  const body = await request.json();

  const { email, password, fullName, address } = body;

  if (!email) {
    return NextResponse.json(
      { error: "You must enter valid email address!" },
      { status: 200 }
    );
  }

  try {
    const tokenCookie = await cookies();
    const getToken = tokenCookie.get("token");

    if (getToken) {
      const token = jwt.verify(getToken.value, "appSecret");

      const userId = token.id;

      if (!userId) {
        return NextResponse.json(
          { error: "Unauthorized request!" },
          { status: 200 }
        );
      }

      const user = await prisma.user.findUnique({ where: { id: userId } });

      if (!user) {
        return NextResponse.json(
          { error: "Unauthorized request!" },
          { status: 200 }
        );
      }

      let updatedUser;

      if (password) {
        const hash = await bcrypt.hash(password, 10);

        updatedUser = await prisma.user.update({
          data: {
            email,
            password: hash,
            fullName,
            address,
          },
          where: {
            id: user.id,
          },
        });
      } else {
        updatedUser = await prisma.user.update({
          data: {
            email,
            fullName,
            address,
          },
          where: {
            id: user.id,
          },
        });
      }

      return NextResponse.json(updatedUser, { status: 201 });
    }

    return NextResponse.json(
      { error: "Unauthorized request!" },
      { status: 200 }
    );
  } catch (error) {
    console.log(error.message);

    return NextResponse.json(
      { error: "Failed to update user" },
      { status: 500 }
    );
  } finally {
    await prisma.$disconnect();
  }
}

Create page.js in app/auth/edit-profile folder:

"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useCurrentUser } from "@/contexts/CurrentUserContext";
import { Loader2 } from "lucide-react";

const EditProfilePage = () => {
  const { currentUser } = useCurrentUser();

  const [email, setEmail] = useState(currentUser?.email || "");
  const [password, setPassword] = useState("");
  const [fullName, setFullName] = useState(currentUser?.fullName || "");
  const [address, setAddress] = useState(currentUser?.address || "");
  const [errorMsg, setErrorMsg] = useState("");
  const [loading, setLoading] = useState(false);

  const handleSubmit = async () => {
    try {
      setLoading(true);

      const response = await fetch("/api/auth/edit-profile", {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          email,
          password,
          fullName,
          address,
        }),
      });
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const data = await response.json();
      if (data.error) {
        setErrorMsg(data.error);
        setTimeout(() => {
          setErrorMsg("");
        }, 4000);
      } else {
        window.location.reload();
      }
      return data;
    } catch (error) {
      console.error("There was a problem with the fetch operation:", error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <section>
      <div className="py-8 px-4 mx-auto max-w-2xl lg:py-16">
        <h1 className="mb-4 text-2xl font-bold text-gray-900 dark:text-white">
          Edit Profile
        </h1>

        {errorMsg ? (
          <div
            className="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50"
            role="alert"
          >
            <span className="font-medium">Error:</span>
            {errorMsg}
          </div>
        ) : undefined}

        <div>
          <div className="grid gap-4 sm:grid-cols-2 sm:gap-6">
            <div className="sm:col-span-2">
              <Label
                htmlFor="email"
                className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
              >
                Email
              </Label>

              <Input
                type="email"
                name="email"
                id="email"
                value={email}
                onChange={(event) => setEmail(event.target.value)}
                className="input"
                placeholder="Email"
                required
              />
            </div>

            <div className="sm:col-span-2">
              <Label
                htmlFor="password"
                className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
              >
                Password
              </Label>

              <Input
                type="password"
                name="password"
                id="password"
                value={password}
                onChange={(event) => setPassword(event.target.value)}
                className="input"
                placeholder="Password"
                required
              />

              <p className="text-xs text-gray-500 mt-1">
                You can leave empty if you don't want to change your password.
              </p>
            </div>

            <div className="sm:col-span-2">
              <Label
                htmlFor="fullName"
                className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
              >
                Full Name
              </Label>

              <Input
                type="text"
                name="fullName"
                id="fullName"
                value={fullName}
                onChange={(event) => setFullName(event.target.value)}
                className="input"
                placeholder="Full Name"
                required
              />
            </div>

            <div className="sm:col-span-2">
              <Label
                htmlFor="address"
                className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
              >
                Address
              </Label>

              <Input
                type="text"
                name="address"
                id="address"
                value={address}
                onChange={(event) => setAddress(event.target.value)}
                className="input"
                placeholder="Address"
                required
              />
            </div>
          </div>

          <Button
            disabled={loading}
            onClick={handleSubmit}
            className="btn mt-6"
          >
            {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : "Edit"}
          </Button>
        </div>
      </div>
    </section>
  );
};

export default EditProfilePage;

That's it for this tutorial, we created edit profile page and api route.

Next tutorial we'll create bookmarks page and api route.

0
0

Comments (0)