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!"]
},
@pavansbhat
Copy link

pavansbhat commented Apr 27, 2024

Hi did anyone got this error?? and if you have resolved it, please pass the solutions.

I have done everything. but again and again got this error..

Nav.jsx:15 [next-auth][error][CLIENT_FETCH_ERROR] https://next-auth.js.org/errors#client_fetch_error Unexpected token '<', "<!DOCTYPE "... is not valid JSON {error: {…}, url: '/api/auth/providers', message: Unexpected token '<', "<!DOCTYPE "... is not valid JSON} logger.js:92 POST http://localhost:3000/api/auth/_log 404 (Not Found) Nav.jsx:15 POST http://localhost:3000/api/auth/_log 404 (Not Found) logger.js:92 POST http://localhost:3000/api/auth/_log 404 (Not Found) Nav.jsx:15 POST http://localhost:3000/api/auth/_log 404 (Not Found)

app-index.js:35 [next-auth][error][CLIENT_FETCH_ERROR] https://next-auth.js.org/errors#client_fetch_error Unexpected token '<', "<!DOCTYPE "... is not valid JSON {error: {…}, url: '/api/auth/session', message: Unexpected token '<', "<!DOCTYPE "... is not valid JSON}

You are probably rendering it wrong in layout.jsx file inside RootLayout.

Try this:

import React from "react";
import "@styles/globals.css";
import Nav from "@components/Nav";
import Provider from "@components/Provider";

export const metadata = {
  title: "Promptopia",
  description: "Discover & Share prompts",
};

const RootLayout = ({ children }) => {
  return (
    <html lang="en">
      <body>
        <Provider>
          <div className="main">
            <div className="gradient" />
          </div>
          <main className="app">
            <Nav />
            {children}
          </main>
        </Provider>
      </body>
    </html>
  );
};

export default RootLayout;

@pavansbhat
Copy link

http://localhost:3000/api/auth/error?error=AccessDenied image

error ⚠ Invalid next.config.mjs options detected: ⚠ Unrecognized key(s) in object: 'appDir' at "experimental" ⚠ See more info here: https://nextjs.org/docs/messages/invalid-next-config ✓ Ready in 2.4s

/** @type {import('next').NextConfig} */ const nextConfig = { experimental: { appDir: true, serverComponentsExternalPackages: ["mongoose"], }, images: { domains: ['lh3.googleusercontent.com'], }, webpack(config) { config.experiments = { ...config.experiments, topLevelAwait: true, }; return config; } };

export default nextConfig;

Keep the next.config.mjs as it is.
Create a new file with the name next.config.js in your root directory and add the following code in this file.

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
    serverComponentsExternalPackages: ["mongoose"],
  },
  images: {
    domains: ["lh3.googleusercontent.com"],
  },
  webpack(config) {
    config.experiments = {
      ...config.experiments,
      topLevelAwait: true,
    };
    return config;
  },
};

module.exports = nextConfig;

@pavansbhat
Copy link

Guys i'm stuck with this error.anybody have a solution to this ⨯ Error: [next-auth]: useSession must be wrapped in a at Nav (./components/Nav.jsx:19:90) session

So if you watch the video, you need to add the following code inside Provider.jsx file inside your component folder.

"use client";
import React from "react";
import { SessionProvider } from "next-auth/react";

const Provider = ({ children, session }) => {
  return <SessionProvider session={session}>{children}</SessionProvider>;
};

export default Provider;

and after that inside layout.jsx file inside your create-prompt folder you need to wrap everything inside the body tag with the Provider component as follows.

import React from "react";
import "@styles/globals.css";
import Nav from "@components/Nav";
import Provider from "@components/Provider";

export const metadata = {
  title: "Promptopia",
  description: "Discover & Share prompts",
};

const RootLayout = ({ children }) => {
  return (
    <html lang="en">
      <body>
        <Provider>
          <div className="main">
            <div className="gradient" />
          </div>
          <main className="app">
            <Nav />
            {children}
          </main>
        </Provider>
      </body>
    </html>
  );
};

export default RootLayout;

Hope this solves your issue.

@Thai-Huynh0510
Copy link

anyone got 500 error while deleting? my edit works fine I have checked my code few times but everything seems fine
image

@kevinhangkh
Copy link

I am getting the Access denied error when trying to log in. This happens after putting the session() and signIn() functions in the callbacks.
image
Please help me figure this out.

you find the error? i have de same issue and i am very lost

  1. Make sure the password match in .env and MONGO DB. I noticed sometimes the password does not update if you changed it.
  2. Make sure the < > are removed in the env file for the MONGODB_URI
  3. Make sure you add your email as an authorize tester in the Google Cloud project
  4. Make sure there are no trailing spaces in the IDs

In case all of these don't work, try replacing your route.js with the one from the repo. I was missing some returns in the signIn function.

@ethanmorton
Copy link

@Thai-Huynh0510 I believe I had a similar issue. It turns out that in the DELETE method implemented in /api/prompt/[id]/route.js, the findByIdAndRemove() function has been replaced by findByIdAndDelete(). Changing that solved the issue for me.

@Acheke004
Copy link

where is next.config in gist

@sllkexe
Copy link

sllkexe commented May 20, 2024

I'm still getting an access denied error despite trying all of the solutions and copying the route.js from the repo

@Thai-Huynh0510
Copy link

@ethanmorton it works thanks. I tried to switch both of them in the local host didn't work, but it works when I deployed it

@Ephraim67
Copy link

Hello everyone, I have been having issues with the jsconfig.json file and global css
jsconfig.json
{
"compilerOptions": {
"paths": {
"@": ["./"]
}
}
}

global.css
jsconfig.json
{
"compilerOptions": {
"paths": {
"@": ["./"]
}
}
}

Is there anything I am doing wrongly?

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