diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/.eslintrc.json b/Next-JS-Projects/Advanced/Breaking-News-App/.eslintrc.json new file mode 100644 index 00000000..bffb357a --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/README.md b/Next-JS-Projects/Advanced/Breaking-News-App/README.md new file mode 100644 index 00000000..7bd86c58 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/README.md @@ -0,0 +1,72 @@ +

đŸ’Ĩ Breaking News đŸ’Ĩ

+ + + +

Tech Stack Used 🎮

+ + +
+ + ![HTML5](https://img.shields.io/badge/html5-%23E34F26.svg?style=for-the-badge&logo=html5&logoColor=white) + ![CSS3](https://img.shields.io/badge/css3-%231572B6.svg?style=for-the-badge&logo=css3&logoColor=white) + ![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E) + ![Nextjs](https://img.shields.io/badge/Nextjs-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) +
+ + +![Line](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/114330097/4b78510f-a941-45f8-a9d5-80ed0705e847) + + + +## :zap: Description 📃 + +
+ +

+ A news app is a mobile or web application designed to provide users with up-to-date news and information from various sources. The app aggregates news articles, videos, and other content, presenting it in an organized and easily accessible format. +

+
+ + + + + +## :zap: How to run it? 🕹ī¸ + + +Steps to run this website in your local machine is as follows : +1. Fork this repository +2. Save the code on your local machine you can also clone the repository +3. Open terminal +4. Run following commands in the terminal : +5. npm install +6. npm run dev +7. Browse the website by hitting `localhost:3000` on a web browser. +8. Make sure you have installed node js before entering these code in the terminal + + + + +## :zap: Screenshots 📸 + + + + + +![Line](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/114330097/4b78510f-a941-45f8-a9d5-80ed0705e847) + + + +

Developed By Maheshwari Love đŸ‘Ļ

+

+ + + + + + +

+ +

Happy Coding 🧑‍đŸ’ģ

+ +

Show some  â¤ī¸  by  đŸŒŸ  this repository!

diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/assets/images/newsarticle_placeholder.png b/Next-JS-Projects/Advanced/Breaking-News-App/assets/images/newsarticle_placeholder.png new file mode 100644 index 00000000..013e0e5d Binary files /dev/null and b/Next-JS-Projects/Advanced/Breaking-News-App/assets/images/newsarticle_placeholder.png differ diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/components/NavBar.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/components/NavBar.tsx new file mode 100644 index 00000000..62f3738d --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/components/NavBar.tsx @@ -0,0 +1,47 @@ +import Link from "next/link"; +import { Navbar, Container, Nav, NavDropdown } from "react-bootstrap"; + +const NavBar = () => { + return ( + + + + + + + + + ); +}; + +export default NavBar; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/components/NewsArticleEntry.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/components/NewsArticleEntry.tsx new file mode 100644 index 00000000..33339bbc --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/components/NewsArticleEntry.tsx @@ -0,0 +1,37 @@ +import placeholderImage from "@/assets/images/newsarticle_placeholder.jpg"; +import { Card } from "react-bootstrap"; +import Image from "next/image"; +import styles from "@/styles/NewsArticleEntry.module.css"; + +type NewsArticleEntryProps = { + article: INewsArticle; +}; +const NewsArticleEntry = ({ + article: { title, description, author, url, urlToImage }, +}: NewsArticleEntryProps) => { + const validImageUrl = + urlToImage?.startsWith("http://") || urlToImage?.startsWith("https://") + ? urlToImage + : undefined; + + return ( + + + {/* */} + News article image + + {title} + {description} + + + + ); +}; + +export default NewsArticleEntry; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/components/NewsArticlesGrid.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/components/NewsArticlesGrid.tsx new file mode 100644 index 00000000..85d58db4 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/components/NewsArticlesGrid.tsx @@ -0,0 +1,19 @@ +import { Col, Row } from "react-bootstrap"; +import NewsArticleEntry from "./NewsArticleEntry"; + +type NewsArticlesGridProps = { + articles: INewsArticle[]; +}; +const NewsArticlesGrid = ({ articles }: NewsArticlesGridProps) => { + return ( + + {articles.map((article) => ( + + + + ))} + + ); +}; + +export default NewsArticlesGrid; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/models/NewsArticles.d.ts b/Next-JS-Projects/Advanced/Breaking-News-App/models/NewsArticles.d.ts new file mode 100644 index 00000000..648e9a3a --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/models/NewsArticles.d.ts @@ -0,0 +1,15 @@ +interface INewsArticlesResponse { + status: string, + totalResults: number, + articles: INewsArticle[] +} + +interface INewsArticle { + author: string, + title: string, + description: string, + url: string, + urlToImage?: string, + publishedAt: string, + content: string, +} \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/next.config.js b/Next-JS-Projects/Advanced/Breaking-News-App/next.config.js new file mode 100644 index 00000000..d68bbaad --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/next.config.js @@ -0,0 +1,19 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + images: { + domains: ["www.si.com", "fashionista.com"], + remotePatterns: [ + { + protocol: "https", + hostname: '**' + }, + { + protocol: "http", + hostname: '**' + }, + ] + } +} + +module.exports = nextConfig diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/package.json b/Next-JS-Projects/Advanced/Breaking-News-App/package.json new file mode 100644 index 00000000..34c32d2a --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/package.json @@ -0,0 +1,25 @@ +{ + "name": "news-website", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@types/node": "18.15.11", + "@types/react": "18.0.33", + "@types/react-dom": "18.0.11", + "bootstrap": "^5.2.3", + "eslint": "8.37.0", + "eslint-config-next": "13.3.0", + "next": "13.3.0", + "nextjs-progressbar": "^0.0.16", + "react": "18.2.0", + "react-bootstrap": "^2.7.2", + "react-dom": "18.2.0", + "typescript": "5.0.3" + } +} diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/404.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/404.tsx new file mode 100644 index 00000000..270dbeb6 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/404.tsx @@ -0,0 +1,10 @@ +const NotFoundPage = () => { + return ( +
+

Not Found

+

Looks like this page is not exists

+
+ ); +}; + +export default NotFoundPage; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/500.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/500.tsx new file mode 100644 index 00000000..5ff52573 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/500.tsx @@ -0,0 +1,13 @@ +const ErrorPage = () => { + return ( +
+

Error đŸ˜ĩ

+

+ Looks like something went wrong. Please refresh the page or contact + support. +

+
+ ); +}; + +export default ErrorPage; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/_app.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/_app.tsx new file mode 100644 index 00000000..9c4e7f60 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/_app.tsx @@ -0,0 +1,34 @@ +import "bootstrap/dist/css/bootstrap.min.css"; +import "@/styles/globals.css"; +import styles from "@/styles/app.module.css"; +import Head from "next/head"; +import NextNProgress from "nextjs-progressbar"; +import type { AppProps } from "next/app"; +import { Inter } from "next/font/google"; +import { Container } from "react-bootstrap"; +import NavBar from "@/components/NavBar"; +const inter = Inter({ subsets: ["latin"] }); + +export default function App({ Component, pageProps }: AppProps) { + return ( + <> +
+ + Nextjs News App + + + + + + + + + +
+ + ); +} diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/_document.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/_document.tsx new file mode 100644 index 00000000..54e8bf3e --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/_document.tsx @@ -0,0 +1,13 @@ +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + +
+ + + + ) +} diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/api/search-news.ts b/Next-JS-Projects/Advanced/Breaking-News-App/pages/api/search-news.ts new file mode 100644 index 00000000..2a16da68 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/api/search-news.ts @@ -0,0 +1,24 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from 'next' + +type ErrRes = { + error: any +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const searchQuery = req.query.q?.toString(); + + if (!searchQuery) { + return res.status(400).json({ error: "Please provide a search query" }) + } + try { + const response = await fetch(`https://newsapi.org/v2/everything?q=${searchQuery}&apiKey=${process.env.NEWS_API_KEY}`) + const newsResponse: INewsArticlesResponse = await response.json() + res.status(200).json(newsResponse.articles) + } catch (error) { + res.status(400).json({ error: error }) + } +} diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/categories/[category].tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/categories/[category].tsx new file mode 100644 index 00000000..732e6e0c --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/categories/[category].tsx @@ -0,0 +1,69 @@ +import Head from "next/head"; +import { useRouter } from "next/router"; +import NewsArticlesGrid from "@/components/NewsArticlesGrid"; +import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from "next"; +import { Alert } from "react-bootstrap"; + +type CategoryNewsPageProps = { + articles: INewsArticle[]; +}; +const CategoryNewsPage = ({ articles }: CategoryNewsPageProps) => { + const router = useRouter(); + const categoryName = router.query.category?.toString(); + const title = `Category: ${categoryName}`; + return ( + <> + + {`${title} - NextJS News App`} + +
+

{title}

+ + This page is uses getStaticProps for very high page + loading speed and incremental static regeneration to + show data not older then 5 minutes. + + +
+ + ); +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const categorySlugs = [ + "business", + "entertainment", + "general", + "health", + "science", + "sports", + "technology", + ]; + + const paths = categorySlugs.map((slug) => ({ + params: { category: slug }, + })); + return { + paths, + fallback: false, + }; +}; + +export const getStaticProps: GetStaticProps = async ({ + params, +}: GetStaticPropsContext) => { + const category = params?.category?.toString(); + const response = await fetch( + `https://newsapi.org/v2/top-headlines?country=us&category=${category}&apiKey=${process.env.NEWS_API_KEY}` + ); + const newsResponse: INewsArticlesResponse = await response.json(); + return { + props: { articles: newsResponse.articles }, + //? Incremantal static regeneration. + //? It means revalidate page content every (5 * 60 = 5 minutes). + revalidate: 5 * 60, + }; + // Let error go to 500 page +}; + +export default CategoryNewsPage; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/index.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/index.tsx new file mode 100644 index 00000000..7a0a41c8 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/index.tsx @@ -0,0 +1,52 @@ +import NewsArticlesGrid from "@/components/NewsArticlesGrid"; +import { GetServerSideProps } from "next"; +import Head from "next/head"; +import { Alert } from "react-bootstrap"; + +type BreakingNewsPageProps = { + newsArticles?: INewsArticle[]; +}; +export default function BreakingNewsPage({ + newsArticles, +}: BreakingNewsPageProps) { + return ( + <> + + Breaking news - Nextjs News App + +
+

Breaking News

+ + + This page uses getServerSideProps to fetch data + server-side on every request. +
+ This allows to search engines to crawl the page content and{" "} + improves SEO. +
+ {newsArticles !== undefined ? ( + <> + + + ) : ( +

Error

+ )} +
+ + ); +} + +export const getServerSideProps: GetServerSideProps< + BreakingNewsPageProps +> = async () => { + const response = await fetch( + `https://newsapi.org/v2/top-headlines?country=us&apiKey=${process.env.NEWS_API_KEY}` + ); + const newsResponse: INewsArticlesResponse = await response.json(); + return { + props: { + newsArticles: newsResponse.articles, + }, + }; + // Let error go to 500 page +}; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/pages/search.tsx b/Next-JS-Projects/Advanced/Breaking-News-App/pages/search.tsx new file mode 100644 index 00000000..bc0630a3 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/pages/search.tsx @@ -0,0 +1,80 @@ +import NewsArticlesGrid from "@/components/NewsArticlesGrid"; +import Head from "next/head"; +import { FormEvent, useState } from "react"; +import { Alert, Button, Form, Spinner } from "react-bootstrap"; + +const SearchPage = () => { + const [searchQuery, setSearchQuery] = useState(); + const [searchResults, setSearchResults] = useState( + null + ); + const [searchResultsLoading, setSearchResultsLoading] = useState(false); + const [searchResultsLoadingIsError, setSearchResultsLoadingIsError] = + useState(false); + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + if (searchQuery) { + try { + setSearchResults(null); + setSearchResultsLoadingIsError(false); + setSearchResultsLoading(true); + const response = await fetch(`/api/search-news?q=${searchQuery}`); + const articles: INewsArticle[] = await response.json(); + setSearchResults(articles); + } catch (error) { + console.error(error); + setSearchResultsLoadingIsError(true); + } finally { + setSearchResultsLoading(false); + } + } + }; + + return ( + <> + + Search News | NextJS News App + +
+

Search News

+ + This page uses client-side data fetching to show + fresh data for every search. +
+ Requests are handled by our backend via API routes. +
+
+ + Search query + setSearchQuery(e.target.value)} + /> + + +
+ +
+ {searchResultsLoading && } + {searchResultsLoadingIsError && ( +

Something went wrong please try again!

+ )} + {searchResults?.length === 0 && ( +

Nothing found. Try a differet query

+ )} + {searchResults && } +
+
+ + ); +}; + +export default SearchPage; diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/public/favicon.ico b/Next-JS-Projects/Advanced/Breaking-News-App/public/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/Next-JS-Projects/Advanced/Breaking-News-App/public/favicon.ico differ diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/public/next.svg b/Next-JS-Projects/Advanced/Breaking-News-App/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/public/vercel.svg b/Next-JS-Projects/Advanced/Breaking-News-App/public/vercel.svg new file mode 100644 index 00000000..d2f84222 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/screenshot.webp b/Next-JS-Projects/Advanced/Breaking-News-App/screenshot.webp new file mode 100644 index 00000000..01eb10c3 Binary files /dev/null and b/Next-JS-Projects/Advanced/Breaking-News-App/screenshot.webp differ diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/styles/NewsArticleEntry.module.css b/Next-JS-Projects/Advanced/Breaking-News-App/styles/NewsArticleEntry.module.css new file mode 100644 index 00000000..0763164b --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/styles/NewsArticleEntry.module.css @@ -0,0 +1,4 @@ +.image { + object-fit: cover; + height: 100%; +} \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/styles/app.module.css b/Next-JS-Projects/Advanced/Breaking-News-App/styles/app.module.css new file mode 100644 index 00000000..7ba3db9c --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/styles/app.module.css @@ -0,0 +1,3 @@ +.pageContainer { + margin: 2rem auto; +} \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/styles/globals.css b/Next-JS-Projects/Advanced/Breaking-News-App/styles/globals.css new file mode 100644 index 00000000..d071795b --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/styles/globals.css @@ -0,0 +1,33 @@ +:root { + --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', + 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', + 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; + + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + max-width: 100vw; + min-height: 100vh; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient(to bottom, + transparent, + rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); +} + +a { + color: inherit; + text-decoration: none; +} \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Breaking-News-App/tsconfig.json b/Next-JS-Projects/Advanced/Breaking-News-App/tsconfig.json new file mode 100644 index 00000000..5950e225 --- /dev/null +++ b/Next-JS-Projects/Advanced/Breaking-News-App/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "models" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/Next-JS-Projects/README.md b/Next-JS-Projects/README.md index 588ca56d..95e86213 100644 --- a/Next-JS-Projects/README.md +++ b/Next-JS-Projects/README.md @@ -27,8 +27,7 @@ |------|--------------|----------| | 1 | [Weather-Website](./Basic/Weather-Website) | ![Basic](https://img.shields.io/badge/Basic-00FF00?style=for-the-badge) | | 2 | [Quiz-App](./Intermediate/Quiz-App) | ![Intermediate](https://img.shields.io/badge/Intermediate-FFD700?style=for-the-badge) | - - +| 3 | [Breaking-News-App](./Advanced/Breaking-News-App/) | ![Advanced](https://img.shields.io/badge/Advanced-FF0000?style=for-the-badge) |