Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Week 10 assignment complete solution using TS, Cookie based auth, SSR for listing courses #7

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
68 changes: 50 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,58 @@
## Migrate the course selling app to next.js
# Course Selling App

Until now we've built a basic course selling application
- It uses React for the frontend
- It uses MongoDB as the database
- It uses Node.js as the Backend server
- It is written in JS
Welcome to the Course Selling App! This Next.js project offers a platform for users to explore, edit, and add online courses. Below, you'll find an overview of the technologies used and the main features of the app.

Link to it - https://github.com/100xDevs-hkirat/week-10
## Technologies Used

The assignment of this week is to move the application over to NextJS
- **Next.js**: A React framework for building server-rendered React applications.
- **React**: A JavaScript library for building user interfaces.
- **Material-UI (MUI)**: A popular React UI framework for designing visually appealing web applications.
- **Recoil**: A state management library for managing complex application state in a simple way.
- **Middleware**: Custom middleware for handling authentication and authorization.
- **JWT (JSON Web Tokens)**: Cookie-based authentication using JWT for secure user sessions.
- **MongoDB**: A NoSQL database for storing and managing course and user data.
- **TypeScript**: A statically typed superset of JavaScript for improved development efficiency and code quality.
- **Server-Side Rendering (SSR)**: Providing initial page load performance and SEO benefits.
## Features

You can bootstrap a nextjs application by calling -
```
npx create-next-app@latest
```
### User Authentication

If you have followed the video from Week 10.2, please select these configs to use the Page router (vs the new NextJS 13 app router)
- **Sign Up and Sign In**: Users can register and log in to their accounts securely.
- **JWT-Based Auth**: Cookie-based JWT authentication for maintaining user sessions.
- **Admin Access**: Admins have special privileges to manage courses.

![Image](https://github.com/100xDevs-hkirat/Week-10-Assignment/blob/master/photo.jpg?raw=true, "image")
### Course Management

- **Add New Courses**: Admins can add new courses with details like title, description, imageLink and price.
- **Edit Courses**: Admins have the ability to edit course details and make updates.
- **Course Listings**: Users can view a list of available courses with their descriptions without authentication.

The goal is to -
- Move the frontend over to NextJS
- Move the backend over to NextJS API
- Convert the complete project to Typescript (and not use JS)
### Server-Side Rendering

- **SSR Enabled**: Utilize server-side rendering for improved page load performance and SEO benefits.
- **Public Course Listing**: Allow users to see course listings without requiring sign-up.

## Getting Started

To run this project locally, follow these steps:

1. Clone this repository.
2. Install the required dependencies using `npm install` or `yarn install`.
3. Set up your MongoDB database and configure the necessary credentials.
4. Create a `.env.local` file and provide the required environment variables (e.g., JWT secret, MongoDB connection).
5. Start the development server using `npm run dev` or `yarn dev`.
![](https://github.com/codergirl2023/Course-Selling-Web-app/blob/master/ezgif.com-video-to-gif%20(1).gif)
![](https://github.com/codergirl2023/Course-Selling-Web-app/blob/master/ezgif.com-video-to-gif%20(2).gif)
![](https://github.com/codergirl2023/Course-Selling-Web-app/blob/master/ezgif.com-video-to-gif%20(3).gif)

## Contributing

Contributions to enhance this project are welcome! Feel free to open issues and pull requests for bug fixes, new features, or improvements.

## License

This project is licensed under the [MIT License](LICENSE).

---

Feel free to tailor this template to match your project's details accurately. Make sure to replace the placeholder text with the correct information about
97 changes: 97 additions & 0 deletions components/Appbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Typography } from "@mui/material";
import Button from "@mui/material/Button";
import { useRouter } from "next/router";
import { isUserLoading } from "../store/selectors/isUserLoading";
import { useSetRecoilState, useRecoilValue } from "recoil";
import { userState } from "../store/atoms/user";
import { userEmailState } from "../store/selectors/userEmail"
import axios from "axios";

export default function Appbar({ }) {
const router = useRouter()
const userLoading = useRecoilValue(isUserLoading);
const userEmail = useRecoilValue(userEmailState);
const setUser = useSetRecoilState(userState);
if (userLoading) {
return <></>
}
if (userEmail) {
return <div style={{
display: "flex",
justifyContent: "space-between",
padding: 4,
zIndex: 1
}}>
<div style={{ marginLeft: 10, cursor: "pointer" }} onClick={() => {
router.push("/")
}}>
<Typography variant={"h6"}>Coursera</Typography>
</div>

<div style={{ display: "flex" }}>
<div style={{ marginRight: 10, display: "flex" }}>
<div style={{ marginRight: 10 }}>
<Button
onClick={() => {
router.push("/addcourse")
}}
>Add course</Button>
</div>

<div style={{ marginRight: 10 }}>
<Button
onClick={() => {
router.push("/courses")
}}
>Courses</Button>
</div>

<Button
variant={"contained"}
onClick={() => {
axios.post('/api/admin/logout');
setUser({
isLoading:false,
userEmail:null
})
router.push('/signin');
}
}
>Logout</Button>
</div>
</div>
</div>
} else {
return <div style={{
display: "flex",
justifyContent: "space-between",
padding: 4,
zIndex: 1
}}>
<div style={{ marginLeft: 10, cursor: "pointer" }} onClick={() => {
router.push("/")
}}>
<Typography variant={"h6"}>Coursera</Typography>
</div>

<div style={{ display: "flex" }}>
<div style={{ marginRight: 10 }}>
<Button
variant={"contained"}
onClick={() => {
router.push("/signup")
}}
>Signup</Button>
</div>
<div>
<Button
variant={"contained"}
onClick={() => {
router.push("/signin")
}}
>Signin</Button>
</div>
</div>
</div>
}
}
39 changes: 39 additions & 0 deletions components/InitUser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
useSetRecoilState
} from 'recoil';
import axios from "axios";
import { userState } from "../store/atoms/user";
import {useEffect} from "react";

export default function InitUser() {
const setUser = useSetRecoilState(userState);
const init = async() => {
try {
const response = await axios.get('/api/admin/me', {

})
if (response.data.username) {
setUser({
isLoading: false,
userEmail: response.data.username
})
} else {
setUser({
isLoading: false,
userEmail: ""
})
}
} catch (e) {
setUser({
isLoading: false,
userEmail: ""
})
}
};

useEffect(() => {
init();
}, []);

return <></>
}
53 changes: 53 additions & 0 deletions components/Landing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {Grid, Typography} from "@mui/material";
import Button from "@mui/material/Button";
import {useRouter} from "next/router";
import {useRecoilValue} from "recoil";
import { userEmailState } from "../store/selectors/userEmail"
import {isUserLoading} from "../store/selectors/isUserLoading";
import Image from "next/image";

export default function Landing() {
const router = useRouter()
const userEmail = useRecoilValue(userEmailState);
const userLoading = useRecoilValue(isUserLoading);
return <div>
<Grid container style={{padding: "5vw"}}>
<Grid item xs={12} md={6} lg={6}>
<div style={{marginTop: 100}}>
<Typography variant={"h2"}>
Coursera Admin
</Typography>
<Typography variant={"h5"}>
A place to learn, earn and grow
</Typography>
{!userLoading && !userEmail && <div style={{display: "flex", marginTop: 20}}>
<div style={{marginRight: 10}}>
<Button
size={"large"}
variant={"contained"}
onClick={() => {
router.push("/signup")
}}
>Signup</Button>
</div>
<div>
<Button
size={"large"}
variant={"contained"}
onClick={() => {
router.push("/signin")
}}
>Signin</Button>
</div>
</div>}
</div>
<div>
</div>
</Grid>
<Grid item xs={12} md={6} lg={6} style={{marginTop: 20}}>
<Image src={"/class.jpeg"} alt="classteacher" width={790}
height={400}/>
</Grid>
</Grid>
</div>
}
9 changes: 9 additions & 0 deletions components/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {CircularProgress} from "@mui/material";

export const Loading = () => {
return <div style={{display: "flex", justifyContent: "center", flexDirection: "column", alignItems: "center", width: "100%", height: "100vh"}}>
<div style={{display: "flex", justifyContent: "center"}}>
<CircularProgress />
</div>
</div>
}
Binary file added ezgif.com-video-to-gif (1).gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ezgif.com-video-to-gif (2).gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ezgif.com-video-to-gif (3).gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
env: {
// @see https://github.com/facebookexperimental/Recoil/issues/2135#issuecomment-1362197710
RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED: "false",
}
};

module.exports = nextConfig;
Loading