Skip to content

Instantly share code, notes, and snippets.

@adrianhajdin
Created May 5, 2023 13:13
Show Gist options
  • Save adrianhajdin/6df61c6cd5ed052dce814a765bff9032 to your computer and use it in GitHub Desktop.
Save adrianhajdin/6df61c6cd5ed052dce814a765bff9032 to your computer and use it in GitHub Desktop.
Next.js 13 Full Course 2023 | Build and Deploy a Full Stack App Using the Official React Framework
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
/*
Note: The styles for this gradient grid background is heavily inspired by the creator of this amazing site (https://dub.sh) – all credits go to them!
*/
.main {
width: 100vw;
min-height: 100vh;
position: fixed;
display: flex;
justify-content: center;
padding: 120px 24px 160px 24px;
pointer-events: none;
}
.main:before {
background: radial-gradient(circle, rgba(2, 0, 36, 0) 0, #fafafa 100%);
position: absolute;
content: "";
z-index: 2;
width: 100%;
height: 100%;
top: 0;
}
.main:after {
content: "";
background-image: url("/assets/images/grid.svg");
z-index: 1;
position: absolute;
width: 100%;
height: 100%;
top: 0;
opacity: 0.4;
filter: invert(1);
}
.gradient {
height: fit-content;
z-index: 3;
width: 100%;
max-width: 640px;
background-image: radial-gradient(
at 27% 37%,
hsla(215, 98%, 61%, 1) 0px,
transparent 0%
),
radial-gradient(at 97% 21%, hsla(125, 98%, 72%, 1) 0px, transparent 50%),
radial-gradient(at 52% 99%, hsla(354, 98%, 61%, 1) 0px, transparent 50%),
radial-gradient(at 10% 29%, hsla(256, 96%, 67%, 1) 0px, transparent 50%),
radial-gradient(at 97% 96%, hsla(38, 60%, 74%, 1) 0px, transparent 50%),
radial-gradient(at 33% 50%, hsla(222, 67%, 73%, 1) 0px, transparent 50%),
radial-gradient(at 79% 53%, hsla(343, 68%, 79%, 1) 0px, transparent 50%);
position: absolute;
content: "";
width: 100%;
height: 100%;
filter: blur(100px) saturate(150%);
top: 80px;
opacity: 0.15;
}
@media screen and (max-width: 640px) {
.main {
padding: 0;
}
}
/* Tailwind Styles */
.app {
@apply relative z-10 flex justify-center items-center flex-col max-w-7xl mx-auto sm:px-16 px-6;
}
.black_btn {
@apply rounded-full border border-black bg-black py-1.5 px-5 text-white transition-all hover:bg-white hover:text-black text-center text-sm font-inter flex items-center justify-center;
}
.outline_btn {
@apply rounded-full border border-black bg-transparent py-1.5 px-5 text-black transition-all hover:bg-black hover:text-white text-center text-sm font-inter flex items-center justify-center;
}
.head_text {
@apply mt-5 text-5xl font-extrabold leading-[1.15] text-black sm:text-6xl;
}
.orange_gradient {
@apply bg-gradient-to-r from-amber-500 via-orange-600 to-yellow-500 bg-clip-text text-transparent;
}
.green_gradient {
@apply bg-gradient-to-r from-green-400 to-green-500 bg-clip-text text-transparent;
}
.blue_gradient {
@apply bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent;
}
.desc {
@apply mt-5 text-lg text-gray-600 sm:text-xl max-w-2xl;
}
.search_input {
@apply block w-full rounded-md border border-gray-200 bg-white py-2.5 font-satoshi pl-5 pr-12 text-sm shadow-lg font-medium focus:border-black focus:outline-none focus:ring-0;
}
.copy_btn {
@apply w-7 h-7 rounded-full bg-white/10 shadow-[inset_10px_-50px_94px_0_rgb(199,199,199,0.2)] backdrop-blur flex justify-center items-center cursor-pointer;
}
.glassmorphism {
@apply rounded-xl border border-gray-200 bg-white/20 shadow-[inset_10px_-50px_94px_0_rgb(199,199,199,0.2)] backdrop-blur p-5;
}
.prompt_layout {
@apply space-y-6 py-8 sm:columns-2 sm:gap-6 xl:columns-3;
}
/* Feed Component */
.feed {
@apply mt-16 mx-auto w-full max-w-xl flex justify-center items-center flex-col gap-2;
}
/* Form Component */
.form_textarea {
@apply w-full flex rounded-lg h-[200px] mt-2 p-3 text-sm text-gray-500 outline-0;
}
.form_input {
@apply w-full flex rounded-lg mt-2 p-3 text-sm text-gray-500 outline-0;
}
/* Nav Component */
.logo_text {
@apply max-sm:hidden font-satoshi font-semibold text-lg text-black tracking-wide;
}
.dropdown {
@apply absolute right-0 top-full mt-3 w-full p-5 rounded-lg bg-white min-w-[210px] flex flex-col gap-2 justify-end items-end;
}
.dropdown_link {
@apply text-sm font-inter text-gray-700 hover:text-gray-500 font-medium;
}
/* PromptCard Component */
.prompt_card {
@apply flex-1 break-inside-avoid rounded-lg border border-gray-300 bg-white/20 bg-clip-padding p-6 pb-4 backdrop-blur-lg backdrop-filter md:w-[360px] w-full h-fit;
}
.flex-center {
@apply flex justify-center items-center;
}
.flex-start {
@apply flex justify-start items-start;
}
.flex-end {
@apply flex justify-end items-center;
}
.flex-between {
@apply flex justify-between items-center;
}
{
"compilerOptions": {
"paths": {
"@*": ["./*"]
}
}
}
import Prompt from "@models/prompt";
import { connectToDB } from "@utils/database";
export const GET = async (request, { params }) => {
try {
await connectToDB()
const prompt = await Prompt.findById(params.id).populate("creator")
if (!prompt) return new Response("Prompt Not Found", { status: 404 });
return new Response(JSON.stringify(prompt), { status: 200 })
} catch (error) {
return new Response("Internal Server Error", { status: 500 });
}
}
export const PATCH = async (request, { params }) => {
const { prompt, tag } = await request.json();
try {
await connectToDB();
// Find the existing prompt by ID
const existingPrompt = await Prompt.findById(params.id);
if (!existingPrompt) {
return new Response("Prompt not found", { status: 404 });
}
// Update the prompt with new data
existingPrompt.prompt = prompt;
existingPrompt.tag = tag;
await existingPrompt.save();
return new Response("Successfully updated the Prompts", { status: 200 });
} catch (error) {
return new Response("Error Updating Prompt", { status: 500 });
}
};
export const DELETE = async (request, { params }) => {
try {
await connectToDB();
// Find the prompt by ID and remove it
await Prompt.findByIdAndRemove(params.id);
return new Response("Prompt deleted successfully", { status: 200 });
} catch (error) {
return new Response("Error deleting prompt", { status: 500 });
}
};
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
fontFamily: {
satoshi: ['Satoshi', 'sans-serif'],
inter: ['Inter', 'sans-serif'],
},
colors: {
'primary-orange': '#FF5722',
}
},
},
plugins: [],
}
username: {
type: String,
required: [true, 'Username is required!'],
match: [/^(?=.{8,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$/, "Username invalid, it should contain 8-20 alphanumeric letters and be unique!"]
},
@brotherSI
Copy link

Hii all, when i am deploying my project on vercel it is giving me error like below... Export encountered errors on following paths: /update-prompt/page: /update-prompt

please help me with that Screenshot 2024-07-02 183946

Hi @Harshitn24 I am stuck with the same error as well I did everything I could to wrap the component that uses the useSearchParams() in suspense boundary and I haven't solved it as well. Lol maybe if we shout! together someone might hear us and help

error-suspense

@Sidharth1327
Copy link

Hii all, when i am deploying my project on vercel it is giving me error like below... Export encountered errors on following paths: /update-prompt/page: /update-prompt

please help me with that Screenshot 2024-07-02 183946

harshith, I am also facing the same issue, if any updates,please let me know

@krizto8
Copy link

krizto8 commented Jul 6, 2024

Hii all, when i am deploying my project on vercel it is giving me error like below... Export encountered errors on following paths: /update-prompt/page: /update-prompt

please help me with that Screenshot 2024-07-02 183946

just go to the next.config.mjs and add the following line in the experimental attribute=> missingSuspenseWithCSRBailout: false

@Y3ASIN
Copy link

Y3ASIN commented Jul 17, 2024

Hii all, when i am deploying my project on vercel it is giving me error like below... Export encountered errors on following paths: /update-prompt/page: /update-prompt
please help me with that Screenshot 2024-07-02 183946

Hi @Harshitn24 I am stuck with the same error as well I did everything I could to wrap the component that uses the useSearchParams() in suspense boundary and I haven't solved it as well. Lol maybe if we shout! together someone might hear us and help

error-suspense


The solution to the problem is to warp the (EditPrompt or UpdatePrompt) component with the react 18 Suspense wrapper.

update-prompt/page.jsx

// page.jsx

import { Suspense } from "react";
import dynamic from "next/dynamic";

const EditPrompt = dynamic(() => import("@components/EditPrompt"), {
  suspense: true,
});

const Page = () => {
  return (
    <Suspense>
      <EditPrompt />
    </Suspense>
  );
};

export default Page;

components/EditPormpt

// EditPrompt.jsx

"use client";

import { useState, useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";

import Form from "@components/Form";

const EditPrompt = () => {
  const router = useRouter();
  const searchParams = useSearchParams();
  const promptId = searchParams.get("id");

  const [submitting, setSubmitting] = useState(false);
  const [post, setPost] = useState({
    prompt: "",
    tag: "",
  });

  useEffect(() => {
    const getPromptDetails = async () => {
      const response = await fetch(`/api/prompt/${promptId}`);
      const data = await response.json();

      setPost({
        prompt: data.prompt,
        tag: data.tag,
      });
    };

    if (promptId) getPromptDetails();
  }, [promptId]);

  const updatePrompt = async (e) => {
    e.preventDefault();
    setSubmitting(true);

    if (!promptId) return alert("Prompt Id not found!");

    try {
      const response = await fetch(`/api/prompt/${promptId}`, {
        method: "PATCH",
        body: JSON.stringify({
          prompt: post.prompt,
          tag: post.tag,
        }),
      });

      if (response.ok) {
        router.push("/");
      }
    } catch (error) {
      console.log(error);
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Form
      type="Edit"
      post={post}
      setPost={setPost}
      submitting={submitting}
      handleSubmit={updatePrompt}
    />
  );
};

export default EditPrompt;

Follow: How I solved
I moved the EditPrompt component to the components folder and imported it from there.

@Y3ASIN
Copy link

Y3ASIN commented Jul 17, 2024

"use client";
import Link from "next/link";
import Image from "next/image";
import { useState, useEffect } from `"react";` <- typo here
import { useState, useEffect } from "react";
import { signIn, signOut, useSession, getProviders } from "next-auth/react";

const Nav = () => {
  const {data:session} = useSession();
  const [providers, setProviders] = useState(null);
  const [toggle,setToggle] = useState(false);
  useEffect(() => {
    const setUpProviders = async () => {
      const response = await getProviders();
      // alert(response);
      console.log(response);
      setProviders(response);
      console.log(providers);
    };
    setUpProviders();
  }, []);
  return (
    <div className="w-full flex-between m-2 ">
      <Link href="/">
        <Image
          src="/assets/images/logo.svg"
          alt="logo"
          width={30}
          height={30}
        />
      </Link>
      {/* {alert(providers)} */}
      {/* desktop view */}
      <div className="sm:flex hidden">
        {session?.user ? (
          <div className="flex gap-2">
            <Link href="/create-prompt" className="black_btn">
              Create Post
            </Link>
            <button type="button" onClick={signOut} className="outline_btn">
              {" "}
              Sign Out
            </button>
            <Link href="profile">
              <Image
                src="/assets/images/logo.svg"
                width={37}
                height={37}
                alt="profile"
              ></Image>
            </Link>
          </div>
        ) : (
          <>
            {providers &&
              Object.values(providers).map((provider) => {
                <button
                  type="button"
                  key={provider.name}
                  onClick={() => signIn(provider.id)}
                  className="black_btn" <- was missing
                >
                  Sign In
                </button>;
              })}
          </>
        )}
      </div >
      {/* mobile view */}
      <div className="sm:hidden relative flex">
        {session?.user ? (<div>
              <Image
                src="/assets/images/logo.svg"
                width={37}
                height={37}
                alt="profile"
                onClick={()=>setToggle((prev)=>!prev)}
              />
              {toggle && (
                <div className="dropdown">
                  <Link
                    href="/profile"
                    className="dropdown_link"
                    onClick={() => setToggle(false)}
                  >
                    Profile
                  </Link>
                  <Link
                    href="/create-prompt"
                    className="dropdown_link"
                    onClick={() => setToggle(false)}
                  >
                    Create Post
                  </Link>
                  <button
                    type="button"
                    onClick={() => {
                      setToggle(false);
                      signOut();
                    }}
                    className="mt-5 w-full black_btn"
                  >
                    Sign Out
                  </button>
                </div>
              )}
              </div>
        ) : (
          <>
            {providers &&
              Object.values(providers).map((provider) => {
                <button
                  type="button"
                  key={provider.name}
                  onClick={() => signIn(provider.id)}
                  className="black_btn" <- was missing
                >
                  Sign In
                </button>;
              })}
          </>
        )}
      </div>
    </div>
  );
};

export default Nav;

Sign in button not showing, pls help.


@Rishik-Seth

Here Typo

import { useState, useEffect } from `"react";` <- typo here
import { useState, useEffect } from "react";

In providers state conditional rendering missing className='black_btn'

{providers &&
   Object.values(providers).map((provider) => {
      <button
         type="button"
         key={provider.name}
         onClick={() => signIn(provider.id)}
         className="black_btn" <- was missing
        >
        Sign In
       </button>;
})}

My code is working fine. You can look into codes.

You can also look here: Showing Sign-in button

@Y3ASIN
Copy link

Y3ASIN commented Jul 17, 2024

Hii all, when i am deploying my project on vercel it is giving me error like below... Export encountered errors on following paths: /update-prompt/page: /update-prompt
please help me with that Screenshot 2024-07-02 183946

just go to the next.config.mjs and add the following line in the experimental attribute=> missingSuspenseWithCSRBailout: false


@krizto8

In next.js documentation, it is not ideal as they say. Next js documentation

@drshshzaman
Copy link

Screenshot 2024-07-17 151832

i successfully went through the access denied error but when i try to login it says to try with a different account. Can anyone please help. Its been 3hours and i am working on this error

@Y3ASIN
Copy link

Y3ASIN commented Jul 17, 2024

Screenshot 2024-07-17 151832

i successfully went through the access denied error but when i try to login it says to try with a different account. Can anyone please help. Its been 3hours and i am working on this error


@drshshzaman

Check your 'console.cloud' on Google OAuth 2.0, and check for typos. It happened in my case and I was able to solve it.

@brotherSI
Copy link

brotherSI commented Jul 17, 2024 via email

@drshshzaman
Copy link

Screenshot 2024-07-17 151832
i successfully went through the access denied error but when i try to login it says to try with a different account. Can anyone please help. Its been 3hours and i am working on this error

@drshshzaman
Check your 'console.cloud' on Google OAuth 2.0, and check for typos. It happened in my case and I was able to solve it.

I have checked everything triple times but still it doesnt work. i cant even find its fix in the documentation. please help

@Y3ASIN
Copy link

Y3ASIN commented Jul 17, 2024

Screenshot 2024-07-17 151832
i successfully went through the access denied error but when i try to login it says to try with a different account. Can anyone please help. Its been 3hours and i am working on this error

@drshshzaman
Check your 'console.cloud' on Google OAuth 2.0, and check for typos. It happened in my case and I was able to solve it.

I have checked everything triple times but still it doesnt work. i cant even find its fix in the documentation. please help


@drshshzaman

Check your api/auth/[...nextauth] route.js
Next Auth route

@drshshzaman
Copy link

Screenshot 2024-07-17 151832
i successfully went through the access denied error but when i try to login it says to try with a different account. Can anyone please help. Its been 3hours and i am working on this error

@drshshzaman
Check your 'console.cloud' on Google OAuth 2.0, and check for typos. It happened in my case and I was able to solve it.

I have checked everything triple times but still it doesnt work. i cant even find its fix in the documentation. please help

@drshshzaman
Check your api/auth/[...nextauth] route.js Next Auth route

I have literally copy pasted from the repo and checked three times everything. i cant get it whats wrong.
as

i have added this line in the end
export { handler as GET, handler as POST };

please tell me the fix. its been two days and i am stuck

@LauraCopetti
Copy link

Hello everyone. I´ve been having this issue and don´t know how to fix it.
imagem_2024-07-18_211952807
Would appreciate some help.

@Boat718
Copy link

Boat718 commented Jul 20, 2024

Hello everyone. I´ve been having this issue and don´t know how to fix it. imagem_2024-07-18_211952807 Would appreciate some help.

I think you have to use "use client" at the top of your importing
example:
'use client' import { useSession } from 'next-auth/react' import { useRouter } from 'next/navigation' import {useState, useEffect} from 'react'

@satyaki07
Copy link

satyaki07 commented Jul 24, 2024

After moving the async functions in route.js inside callbacks object, I am getting timed out error
Screenshot 2024-07-24 at 2 17 52 PM
MongooseError: Operation users.findOne() buffering timed out after 10000ms
at Timeout. (/Users/satbose/promptopia/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js:185:23)
at listOnTimeout (node:internal/timers:573:17)
at process.processTimers (node:internal/timers:514:7)
GET /api/auth/callback/google?state=LtbhGTi_II6RGfOs0fHyFkTPDeU6EJIDAR_yfCfjk_w&code=4%2F0AcvDMrA8ZXoWghvqrxPqWThKv0pwJ3WRSTKrVZa4xeuDXANUZUZoI-nlUeJs-87lqP0LGg&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&prompt=none 302 in 10846ms
GET /api/auth/error?error=AccessDenied 403 in 18ms
MongooseError: Operation users.findOne() buffering timed out after 10000ms
at Timeout. (/Users/satbose/promptopia/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js:185:23)
at listOnTimeout (node:internal/timers:573:17)
at process.processTimers (node:internal/timers:514:7)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment