casper's Profile Image

Full Stack Web/Mobile Developer

Jan, 10, 2025

Next Js Tutorial #6 - Ecommerce App - Admin Create Product Page & Api Route

We'll create functionality for create product and make a admin only protection creating an admin layout page.

Next Js Tutorial #6 - Ecommerce App - Admin Create Product Page & Api Route Image

You can also watch the YouTube video:

Next JS Tutorial #6 | Ecommerce App - Admin Create Product Page & API Route

We want to make every page admin only in the app/auth/admin folder so we'll move our is admin check from auth/admin/page.js to auth/admin/layout.js

Instead of checking the user is admin or not in every page we'll make every page admin protected this way.

Edit page.js in app/auth/admin folder:

"use client";
import AreaChartComponent from "@/components/Admin/AreaChartComponent";
import BarChartComponent from "@/components/Admin/BarChartComponent";
import PieChartComponent from "@/components/Admin/PieChartComponent";

const AdminPage = () => {
  return (
    <div className="container mx-auto py-8 px-4 lg:py-16">
      <h1 className="mb-4 text-3xl font-bold text-gray-900 dark:text-white">
        Dashboard
      </h1>

      <div className="grid grid-cols-3 gap-4">
        <AreaChartComponent />

        <BarChartComponent />

        <PieChartComponent />
      </div>
    </div>
  );
};

export default AdminPage;

Create layout.js in app/auth/admin folder:

"use client";

import { useCurrentUser } from "@/contexts/CurrentUserContext";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export default function AdminLayout({ children }) {
  const { currentUser } = useCurrentUser();

  const router = useRouter();

  useEffect(() => {
    if (!currentUser || currentUser.role !== "ADMIN") {
      router.push("/");
    }
  }, [currentUser]);

  return children;
}

Create route.js in app/api/products folder:

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

const prisma = new PrismaClient();

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

  const { title, description, price, image } = body;

  if (!title || !description || !price || !image) {
    return NextResponse.json(
      { error: "You must fill all the required fields!" },
      { 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 }
        );
      }

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

      const product = await prisma.product.create({
        data: {
          title,
          description,
          image,
          price: parseFloat(price),
        },
      });

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

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

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

Create page.js in app/auth/admin/create-product folder:

import CreateProduct from "@/components/Admin/CreateProduct";

export default function Home() {
  return <CreateProduct />;
}

Create CreateProduct.jsx in components/Admin folder:

"use client";

import { useState } from "react";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import { Label } from "../ui/label";

const CreateProduct = () => {
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");
  const [image, setImage] = useState("");
  const [price, setPrice] = useState("");
  const [errorMsg, setErrorMsg] = useState("");

  const handleSubmit = async () => {
    try {
      const response = await fetch("/api/products", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          title,
          description,
          price,
          image,
        }),
      });

      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);
    }
  };

  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">
          Create Product
        </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="title"
                className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
              >
                Title
              </Label>

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

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

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

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

              <Input
                type="number"
                name="price"
                id="price"
                value={price}
                onChange={(event) => setPrice(event.target.value)}
                className="input"
                placeholder="Price"
                required
              />
            </div>

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

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

          <Button onClick={handleSubmit} className="btn mt-6">
            Create
          </Button>
        </div>
      </div>
    </section>
  );
};

export default CreateProduct;

That's it for this tutorial, we created functionality for create product and made a admin only protection creating a admin layout page.

Next tutorial we'll listing products in our home page and make api route for it.

0
0

Comments (0)