├── .eslintrc.json
├── .gitignore
├── .nvmrc
├── .prettierrc
├── README.md
├── babel.config.js
├── components
├── CsrComponent
│ └── CsrComponent.js
├── DateComponent
│ └── DateComponent.js
├── ImageDisplay
│ └── ImageDisplay.js
├── InputComponent
│ ├── InputComponent.js
│ ├── InputComponent.test.js
│ └── __snapshots__
│ │ └── InputComponent.test.js.snap
├── Modal
│ ├── Modal.js
│ └── ModalStyled.js
├── Navbar
│ └── Navbar.js
├── RegistrationForm
│ └── RegistrationForm.js
└── TailwindButton
│ └── TailwindButton.js
├── jest.config.js
├── jest.setup.js
├── next-seo.config.js
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── 404.js
├── [slug].js
├── _app.js
├── _error.js
├── csr-page.js
├── gallery.js
├── index.js
├── posts
│ └── [category]
│ │ └── [postId].js
└── ssr-page.js
├── postcss.config.js
├── public
├── favicon.ico
└── vercel.svg
├── styles
├── Home.module.css
└── global.css
├── tailwind.config.js
└── utils
├── axiosInstance.js
└── fetcher.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "standard",
8 | "plugin:react/recommended"
9 | ],
10 | "parserOptions": {
11 | "ecmaFeatures": {
12 | "jsx": true
13 | },
14 | "ecmaVersion": "latest",
15 | "sourceType": "module"
16 | },
17 | "rules": {
18 | "react/react-in-jsx-scope": "off" // For Next.js since React import is not needed in files with JSX
19 | },
20 | "settings": {
21 | "react": {
22 | "version": "detect" // Automatically detect the React version
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 |
21 | # debug
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # local env files
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "singleQuote": true,
5 | "trailingComma": "es5",
6 | "semi": false
7 | }
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Project Dependencies
2 |
3 | This NextJS project uses a number of open source libraries and dependencies. Below is a list of these libraries and a brief description of their use in the project.
4 |
5 | ## Dependencies
6 |
7 | - **autoprefixer**: PostCSS plugin to parse CSS and add vendor prefixes to CSS rules using values from Can I Use. It is used in conjunction with Tailwind CSS for broader browser compatibility.
8 | - **axios**: Promise based HTTP client for the browser and Node.js. Used for making HTTP requests to external services and APIs.
9 | - **date-fns**: Modern JavaScript date utility library. Used for manipulating and formatting dates.
10 | - **formik**: Small library that helps with handling form states and validations in React.
11 | - **next**: The React framework for production. Used for server-side rendering and generating static websites.
12 | - **next-seo**: A plugin that makes managing your SEO easier in Next.js projects.
13 | - **postcss**: A tool for transforming CSS with JavaScript. Used in this project for working with Tailwind CSS.
14 | - **react**: A JavaScript library for building user interfaces. Core library for building the UI of this project.
15 | - **react-dom**: Serves as the entry point to the DOM and server renderers for React. It is intended to be paired with the generic React package.
16 | - **styled-components**: A library for styling React applications using tagged template literals.
17 | - **swr**: React Hooks library for remote data fetching. Simplifies data fetching and caching.
18 | - **tailwindcss**: A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.
19 | - **yup**: JavaScript schema builder for value parsing and validation. Integrates well with Formik for managing forms.
20 |
21 | ## Development Dependencies
22 |
23 | - **@babel/preset-env, @babel/preset-react**: These Babel presets are used for compiling ES6+ and React JSX into compatible JavaScript code for older browsers.
24 | - **@testing-library/jest-dom, @testing-library/react**: Utilities for testing React components in a more user-centric way.
25 | - **babel-jest**: Integrates Babel with Jest for testing.
26 | - **eslint, eslint-config-prettier, eslint-config-standard, eslint-plugin-import, eslint-plugin-n, eslint-plugin-prettier, eslint-plugin-promise**: These are used for maintaining code quality and consistent coding style.
27 | - **jest, jest-environment-jsdom**: JavaScript testing framework used for testing JavaScript code.
28 | - **prettier**: An opinionated code formatter used for maintaining consistent style across the codebase.
29 |
30 | This project also specifies Node.js version `>=18` as per the `engines` field in the `package.json`.
31 |
32 | For detailed information about each library or tool, you can visit their respective documentation or GitHub repository.
33 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@babel/preset-env',
4 | '@babel/preset-react',
5 | // If you are using TypeScript, also include '@babel/preset-typescript'
6 | ],
7 | };
8 |
--------------------------------------------------------------------------------
/components/CsrComponent/CsrComponent.js:
--------------------------------------------------------------------------------
1 | import useSWR, { mutate } from 'swr'
2 | import fetcher from '../../utils/fetcher'
3 | import TailwindButton from '../TailwindButton/TailwindButton'
4 | import React from 'react';
5 |
6 | const CsrComponent = () => {
7 | const { data, error } = useSWR('todos/5', fetcher)
8 |
9 | if (error) return
Failed to load data
10 | if (!data) return Loading...
11 |
12 | const submitPost = () => {
13 | fetch('https://jsonplaceholder.typicode.com/posts', {
14 | method: 'POST',
15 | body: JSON.stringify({
16 | title: 'foo',
17 | body: 'bar',
18 | userId: 1
19 | }),
20 | headers: {
21 | 'Content-type': 'application/json; charset=UTF-8'
22 | }
23 | })
24 | .then((response) => response.json())
25 | .then((json) => {
26 | console.log(json)
27 | // After the POST request, revalidate and refetch GET data
28 | mutate('https://jsonplaceholder.typicode.com/posts')
29 | })
30 | .catch((error) => {
31 | console.error('Error in POST request:', error)
32 | })
33 | }
34 |
35 | return (
36 |
37 |
Data Loaded
38 |
{JSON.stringify(data, null, 2)}
39 |
Submit
40 |
41 | )
42 | }
43 |
44 | export default CsrComponent
45 |
--------------------------------------------------------------------------------
/components/DateComponent/DateComponent.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import {
3 | format,
4 | addDays,
5 | isBefore,
6 | addMonths,
7 | subYears,
8 | addYears,
9 | differenceInDays,
10 | } from 'date-fns'
11 | import TailwindButton from '../TailwindButton/TailwindButton'
12 |
13 | const DateComponent = ({ initialDate = new Date() }) => {
14 | const [date, setDate] = useState(initialDate)
15 | const [output, setOutput] = useState('')
16 |
17 | const addDaysToDate = (numberOfDays) => {
18 | const newDate = addDays(date, numberOfDays)
19 | setDate(newDate)
20 | setOutput(
21 | `Date after adding ${numberOfDays} day(s): ${format(
22 | newDate,
23 | 'dd/MM/yyyy',
24 | )}`,
25 | )
26 | }
27 |
28 | const compareDates = () => {
29 | const comparisonDate = new Date()
30 | const isEarlier = isBefore(date, comparisonDate)
31 | setOutput(
32 | `The selected date is ${
33 | isEarlier ? 'earlier' : 'not earlier'
34 | } than today.`,
35 | )
36 | }
37 |
38 | const addMonthsToDate = () => {
39 | const newDate = addMonths(date, 1)
40 | setDate(newDate)
41 | setOutput(`Date after adding 1 month: ${format(newDate, 'dd/MM/yyyy')}`)
42 | }
43 |
44 | const subtractYearFromDate = () => {
45 | const newDate = subYears(date, 1)
46 | setDate(newDate)
47 | setOutput(`Date after subtracting 1 year: ${format(newDate, 'dd/MM/yyyy')}`)
48 | }
49 |
50 | const addYearFromDate = () => {
51 | const newDate = addYears(date, 1)
52 | setDate(newDate)
53 | setOutput(`Date after adding 1 year: ${format(newDate, 'dd/MM/yyyy')}`)
54 | }
55 |
56 | const calculateDifference = () => {
57 | const daysDifference = differenceInDays(new Date(), date)
58 | setOutput(`Difference from today: ${daysDifference} day(s)`)
59 | }
60 |
61 | return (
62 |
63 |
64 | Selected Date: {format(date, 'dd/MM/yyyy')}
65 |
66 |
67 |
68 | addDaysToDate(1)}>Add 1 Day
69 | addDaysToDate(7)}>Add 7 Days
70 | Is Earlier Than Today
71 | Add 1 Month
72 | Subtract 1 Year
73 | Add 1 Year
74 | Days from Today
75 |
76 |
77 |
78 | {output}
79 |
80 |
81 | )
82 | }
83 |
84 | export default DateComponent
85 |
--------------------------------------------------------------------------------
/components/ImageDisplay/ImageDisplay.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Image from 'next/image';
3 |
4 | const ImageDisplay = ({ imageUrls }) => {
5 | return (
6 |
7 | {imageUrls.map((url, index) => (
8 |
9 |
18 |
19 | ))}
20 |
21 | );
22 | };
23 |
24 | export default ImageDisplay;
25 |
--------------------------------------------------------------------------------
/components/InputComponent/InputComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const InputComponent = ({
4 | label,
5 | id,
6 | name,
7 | type,
8 | value,
9 | onChange,
10 | onBlur,
11 | error,
12 | }) => {
13 | return (
14 |
15 |
16 | {label}
17 |
18 |
27 | {error &&
{error}
}
28 |
29 | )
30 | }
31 |
32 | export default InputComponent;
33 |
--------------------------------------------------------------------------------
/components/InputComponent/InputComponent.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render, screen, fireEvent } from '@testing-library/react'
3 | import '@testing-library/jest-dom'
4 | import InputComponent from './InputComponent' // Adjust the path as necessary
5 |
6 | describe('InputComponent', () => {
7 | // Snapshot Test
8 | it('renders correctly', () => {
9 | const { asFragment } = render(
10 | ,
11 | )
12 | expect(asFragment()).toMatchSnapshot()
13 | })
14 |
15 | // Functional Test - Rendering and Props
16 | it('displays the correct label and input type', () => {
17 | render(
18 | ,
19 | )
20 | expect(screen.getByLabelText('Test Label')).toBeInTheDocument()
21 | expect(screen.getByLabelText('Test Label')).toHaveAttribute('type', 'text')
22 | })
23 |
24 | // Functional Test - onChange Event
25 | it('calls onChange event when input changes', () => {
26 | const handleChange = jest.fn()
27 | render(
28 | ,
35 | )
36 | fireEvent.change(screen.getByLabelText('Test Label'), {
37 | target: { value: 'new value' },
38 | })
39 | expect(handleChange).toHaveBeenCalledTimes(1)
40 | })
41 |
42 | // Functional Test - Error Display
43 | it('displays an error message when error prop is provided', () => {
44 | const errorMessage = 'Error message'
45 | render(
46 | ,
53 | )
54 | expect(screen.getByText(errorMessage)).toBeInTheDocument()
55 | })
56 | })
--------------------------------------------------------------------------------
/components/InputComponent/__snapshots__/InputComponent.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`InputComponent renders correctly 1`] = `
4 |
5 |
8 |
12 | Test Label
13 |
14 |
21 |
22 |
23 | `;
24 |
--------------------------------------------------------------------------------
/components/Modal/Modal.js:
--------------------------------------------------------------------------------
1 | // components/Modal.js
2 | import React from 'react'
3 | import {
4 | ModalOverlay,
5 | ModalContent,
6 | ModalHeader,
7 | ModalButton,
8 | ModalBody,
9 | } from './ModalStyled'
10 |
11 | const Modal = ({ isOpen, onClose, onSave, children }) => {
12 | return (
13 | isOpen && (
14 |
15 |
16 |
17 | Close
18 |
19 | {children}
20 |
21 |
22 | )
23 | )
24 | }
25 |
26 | export default Modal
27 |
--------------------------------------------------------------------------------
/components/Modal/ModalStyled.js:
--------------------------------------------------------------------------------
1 | // components/ModalStyled.js
2 | import styled from 'styled-components'
3 |
4 | export const ModalOverlay = styled.div`
5 | position: fixed;
6 | top: 0;
7 | left: 0;
8 | width: 100%;
9 | height: 100%;
10 | background: rgba(
11 | 0,
12 | 0,
13 | 0,
14 | 0.5
15 | ); /* Semi-transparent black background for the blur effect */
16 | display: flex;
17 | justify-content: center;
18 | align-items: center;
19 | `
20 |
21 | export const ModalContent = styled.div`
22 | background: white; /* Background color of the modal */
23 | padding: 20px;
24 | border-radius: 8px;
25 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); /* Box shadow for a subtle lift effect */
26 | `
27 |
28 | export const ModalHeader = styled.div`
29 | display: flex;
30 | justify-content: flex-end;
31 | `
32 |
33 | export const ModalButton = styled.span`
34 | cursor: pointer;
35 | `
36 |
37 | export const ModalBody = styled.div`
38 | /* Add any additional styling for the modal body */
39 | `
40 |
--------------------------------------------------------------------------------
/components/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import React from 'react';
3 |
4 | const NavBar = () => {
5 | return (
6 |
7 |
8 |
9 |
10 | Home
11 |
12 |
13 |
14 |
15 | SSR Page
16 |
17 |
18 |
19 |
20 | CSR Page
21 |
22 |
23 |
24 |
25 | Gallery
26 |
27 |
28 |
29 |
30 | Slug Page 01
31 |
32 |
33 |
34 |
35 | Slug Page 02
36 |
37 |
38 |
39 |
40 | Dynamic Routing 01
41 |
42 |
43 |
44 |
45 | Dynamic Routing 02
46 |
47 |
48 |
49 |
50 | )
51 | }
52 |
53 | export default NavBar
54 |
--------------------------------------------------------------------------------
/components/RegistrationForm/RegistrationForm.js:
--------------------------------------------------------------------------------
1 | // components/RegistrationForm.js
2 | import React from 'react'
3 | import { useFormik } from 'formik'
4 | import * as Yup from 'yup'
5 | import InputComponent from '../InputComponent/InputComponent'
6 | import TailwindButton from '../TailwindButton/TailwindButton'
7 |
8 | const RegistrationForm = ({ onClose }) => {
9 | const formik = useFormik({
10 | initialValues: {
11 | firstName: '',
12 | lastName: '',
13 | age: '',
14 | email: '',
15 | password: '',
16 | confirmPassword: '',
17 | },
18 | validationSchema: Yup.object({
19 | firstName: Yup.string().required('Required'),
20 | lastName: Yup.string().required('Required'),
21 | age: Yup.number()
22 | .positive('Must be a positive number')
23 | .integer('Must be an integer')
24 | .required('Required'),
25 | email: Yup.string().email('Invalid email address').required('Required'),
26 | password: Yup.string()
27 | .min(8, 'Must be at least 8 characters')
28 | .required('Required'),
29 | confirmPassword: Yup.string()
30 | .oneOf([Yup.ref('password'), null], 'Passwords must match')
31 | .required('Required'),
32 | }),
33 | validateOnBlur: true, // Validate on blur
34 | validateOnChange: false, // Do not validate on change
35 | onSubmit: (values) => {
36 | // Handle form submission here
37 | console.log('Form submitted with values:', values)
38 | onClose() // Close the form after submission
39 | },
40 | })
41 |
42 | return (
43 |
44 |
113 |
114 | )
115 | }
116 |
117 | export default RegistrationForm
118 |
--------------------------------------------------------------------------------
/components/TailwindButton/TailwindButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const TailwindButton = ({ children, onClick }) => {
4 | return (
5 |
9 | {children}
10 |
11 | )
12 | }
13 |
14 | export default TailwindButton
15 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: {
3 | '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
4 | },
5 | setupFilesAfterEnv: ['/jest.setup.js'],
6 | testPathIgnorePatterns: ['/.next/', '/node_modules/'],
7 | moduleNameMapper: {
8 | '\\.(scss|sass|css)$': 'identity-obj-proxy'
9 | },
10 | testEnvironment: 'jest-environment-jsdom',
11 | };
12 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | require('@testing-library/jest-dom');
2 |
--------------------------------------------------------------------------------
/next-seo.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | title: "Default Title",
3 | description: "Default Description",
4 | openGraph: {
5 | type: 'website',
6 | locale: 'en_IE',
7 | url: 'https://www.yoursite.com/',
8 | site_name: 'SiteName',
9 | },
10 | // Additional global settings...
11 | };
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | images: {
3 | domains: ['via.placeholder.com'],
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "build": "next build",
5 | "dev": "next dev",
6 | "start": "next start",
7 | "test": "jest",
8 | "format": "prettier --check --ignore-path .gitignore .",
9 | "format:fix": "prettier --write --ignore-path .gitignore ."
10 | },
11 | "dependencies": {
12 | "autoprefixer": "^10.4.16",
13 | "axios": "^1.6.5",
14 | "date-fns": "^3.2.0",
15 | "formik": "^2.4.5",
16 | "next": "latest",
17 | "next-seo": "^6.4.0",
18 | "postcss": "^8.4.33",
19 | "react": "18.2.0",
20 | "react-dom": "18.2.0",
21 | "styled-components": "^6.1.8",
22 | "swr": "^2.2.4",
23 | "tailwindcss": "^3.4.1",
24 | "yup": "^1.3.3"
25 | },
26 | "engines": {
27 | "node": ">=18"
28 | },
29 | "devDependencies": {
30 | "@babel/preset-env": "^7.23.8",
31 | "@babel/preset-react": "^7.23.3",
32 | "@testing-library/jest-dom": "^6.2.0",
33 | "@testing-library/react": "^14.1.2",
34 | "babel-jest": "^29.7.0",
35 | "eslint": "^8.56.0",
36 | "eslint-config-prettier": "^9.1.0",
37 | "eslint-config-standard": "^17.1.0",
38 | "eslint-plugin-import": "^2.29.1",
39 | "eslint-plugin-n": "^16.6.2",
40 | "eslint-plugin-prettier": "^5.1.3",
41 | "eslint-plugin-promise": "^6.1.1",
42 | "jest": "^29.7.0",
43 | "jest-environment-jsdom": "^29.7.0",
44 | "prettier": "^3.1.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/pages/404.js:
--------------------------------------------------------------------------------
1 | // pages/404.js
2 | import Link from 'next/link';
3 | import React from 'react';
4 |
5 | export default function Custom404() {
6 | return (
7 |
8 |
404 - Page Not Found
9 |
10 | Home
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/pages/[slug].js:
--------------------------------------------------------------------------------
1 | // pages/[slug].js
2 | import React from 'react';
3 | import Link from 'next/link';
4 | import { useRouter } from 'next/router';
5 |
6 | const SlugPage = ({ post }) => {
7 | // Get the router instance
8 | const router = useRouter();
9 |
10 | // If the page has not been generated, this will be displayed
11 | if (router.isFallback) {
12 | return Loading...
;
13 | }
14 |
15 | // Render post content
16 | return (
17 |
18 | {post.title}
19 | {post.content}
20 |
21 | Back to home
22 |
23 |
24 | );
25 | };
26 |
27 | // This function gets called at build time on server-side.
28 | export async function getStaticPaths() {
29 | // Array of known paths for demonstration purposes
30 | const paths = [
31 | { params: { slug: 'first-post' } },
32 | { params: { slug: 'second-post' } },
33 | // More paths...
34 | ];
35 |
36 | // { fallback: false } means other routes should 404.
37 | return { paths, fallback: false };
38 | }
39 |
40 | // This also gets called at build time
41 | export async function getStaticProps({ params }) {
42 | // Here's an example of a hardcoded object
43 | const posts = {
44 | 'first-post': { title: 'First Post', content: 'This is the first post, generated using slug page.' },
45 | 'second-post': { title: 'Second Post', content: 'This is the second post, generated using slug page.' },
46 | // More posts...
47 | };
48 |
49 | // Get the post data based on slug
50 | const post = posts[params.slug];
51 |
52 | return {
53 | props: {
54 | post,
55 | },
56 | };
57 | }
58 |
59 | export default SlugPage;
60 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/global.css'
2 | import NavBar from "../components/Navbar/Navbar"
3 | import React from 'react';
4 |
5 | const App = ({ Component, pageProps }) => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 | export default App
15 |
--------------------------------------------------------------------------------
/pages/_error.js:
--------------------------------------------------------------------------------
1 | // pages/_error.js
2 | import Link from 'next/link';
3 | import React from 'react';
4 |
5 | function Error({ statusCode }) {
6 | return (
7 |
8 |
{statusCode ? `An error ${statusCode} occurred on server` : 'An error occurred on client'}
9 |
10 |
Go back home
11 |
12 |
13 | );
14 | }
15 |
16 | Error.getInitialProps = ({ res, err }) => {
17 | const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
18 | return { statusCode };
19 | };
20 |
21 | export default Error;
22 |
--------------------------------------------------------------------------------
/pages/csr-page.js:
--------------------------------------------------------------------------------
1 | import CsrComponent from "../components/CsrComponent/CsrComponent"
2 | import React from 'react';
3 |
4 | const CsrPage = () => {
5 | return (
6 |
7 |
CSR Page
8 |
9 |
10 | )
11 | }
12 |
13 | export default CsrPage
14 |
--------------------------------------------------------------------------------
/pages/gallery.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Head from 'next/head';
3 | import ImageDisplay from '../components/ImageDisplay/ImageDisplay';
4 |
5 | const gallery = () => {
6 | // Example: Array of 100 image URLs
7 | const imageUrls = new Array(100).fill(null).map((_, i) => `https://via.placeholder.com/500x300?text=Image+${i + 1}`);
8 | return (
9 |
10 |
11 |
Page Title
12 |
13 |
14 |
15 | {/* Additional tags as needed */}
16 |
17 | Image Gallery with Lazy Loading
18 |
19 |
20 | );
21 | };
22 |
23 | export default gallery;
24 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from '../styles/Home.module.css'
3 | import Modal from '../components/Modal/Modal'
4 | import RegistrationForm from '../components/RegistrationForm/RegistrationForm'
5 | import DateComponent from '../components/DateComponent/DateComponent'
6 | import { useState } from 'react'
7 | import TailwindButton from '../components/TailwindButton/TailwindButton'
8 | import { NextSeo } from 'next-seo';
9 |
10 | export default function Home() {
11 | const [isFormOpen, setIsFormOpen] = useState(false)
12 | // Handler for opening the registration form modal
13 | const handleOpenForm = () => {
14 | setIsFormOpen(true)
15 | }
16 |
17 | // Handler for closing the registration form modal
18 | const handleCloseForm = () => {
19 | setIsFormOpen(false)
20 | }
21 | return (
22 | <>
23 |
33 |
34 | Hello
35 |
36 |
37 |
38 |
39 | Open Form
40 |
41 |
42 |
43 | >
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/pages/posts/[category]/[postId].js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const postsData = {
4 | technology: [
5 | { id: 1, title: 'Exploring a New Programming Paradigm' },
6 | { id: 2, title: 'Reviewing the Latest Innovative Gadgets' },
7 | ],
8 | science: [
9 | { id: 3, title: 'Recent Breakthroughs in Space Exploration' },
10 | { id: 4, title: 'Promising Solutions to Address Climate Change' },
11 | ],
12 | };
13 |
14 | export async function getStaticPaths() {
15 | const paths = [];
16 | for (const category in postsData) {
17 | for (const post of postsData[category]) {
18 | paths.push({
19 | params: { category, postId: post.id.toString() },
20 | });
21 | }
22 | }
23 | return { paths, fallback: false };
24 | }
25 |
26 | export async function getStaticProps({ params }) {
27 | const postsForCategory = postsData[params.category];
28 | const post = postsForCategory.find((post) => post.id === Number(params.postId));
29 | return { props: { category: params.category, post } };
30 | }
31 |
32 | function Post({ category, post }) {
33 | return (
34 |
35 |
{post.title}
36 |
Category: {category}
37 |
{post.content}
{/* Add your content here */}
38 |
39 | );
40 | }
41 |
42 | export default Post;
43 |
--------------------------------------------------------------------------------
/pages/ssr-page.js:
--------------------------------------------------------------------------------
1 | import axiosInstance from '../utils/axiosInstance'
2 | import React from 'react';
3 |
4 | const SsrComponent = ({ data }) => {
5 | return (
6 |
7 |
Server-Side Rendered Data
8 |
{JSON.stringify(data, null, 2)}
9 |
10 | )
11 | }
12 |
13 | export async function getServerSideProps () {
14 | try {
15 | // Replace with your API endpoint
16 | const res = await axiosInstance.get('/todos/5')
17 | const data = res.data // Using .data to get the actual data from axios response
18 |
19 | return {
20 | props: { data } // will be passed to the page component as props
21 | }
22 | } catch (error) {
23 | console.error('Error fetching data:', error)
24 | return {
25 | props: { data: null } // Return null data on error
26 | }
27 | }
28 | }
29 |
30 | export default SsrComponent
31 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sanchit0496/nextjs_scaffolding/6cd72d9fe4941bba1ae4bb91b11dd40d5ec815c1/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | padding: 0 0.5rem;
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | .title a {
11 | color: #0070f3;
12 | text-decoration: none;
13 | }
14 |
15 | .title a:hover,
16 | .title a:focus,
17 | .title a:active {
18 | text-decoration: underline;
19 | }
20 |
21 | .title {
22 | margin: 0 0 1rem;
23 | line-height: 1.15;
24 | font-size: 3.6rem;
25 | }
26 |
27 | .title {
28 | text-align: center;
29 | }
30 |
31 | .title,
32 | .description {
33 | text-align: center;
34 | }
35 |
36 | .description {
37 | line-height: 1.5;
38 | font-size: 1.5rem;
39 | }
40 |
41 | .grid {
42 | display: flex;
43 | align-items: center;
44 | justify-content: center;
45 | flex-wrap: wrap;
46 |
47 | max-width: 800px;
48 | margin-top: 3rem;
49 | }
50 |
51 | .card {
52 | margin: 1rem;
53 | flex-basis: 45%;
54 | padding: 1.5rem;
55 | text-align: left;
56 | color: inherit;
57 | text-decoration: none;
58 | border: 1px solid #eaeaea;
59 | border-radius: 10px;
60 | transition:
61 | color 0.15s ease,
62 | border-color 0.15s ease;
63 | }
64 |
65 | .card:hover,
66 | .card:focus,
67 | .card:active {
68 | color: #0070f3;
69 | border-color: #0070f3;
70 | }
71 |
72 | .card h3 {
73 | margin: 0 0 1rem 0;
74 | font-size: 1.5rem;
75 | }
76 |
77 | .card p {
78 | margin: 0;
79 | font-size: 1.25rem;
80 | line-height: 1.5;
81 | }
82 |
83 | .logo {
84 | height: 1em;
85 | }
86 |
87 | @media (max-width: 600px) {
88 | .grid {
89 | width: 100%;
90 | flex-direction: column;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/styles/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | html,
6 | body {
7 | padding: 0;
8 | margin: 0;
9 | font-family:
10 | Inter,
11 | -apple-system,
12 | BlinkMacSystemFont,
13 | Segoe UI,
14 | Roboto,
15 | Oxygen,
16 | Ubuntu,
17 | Cantarell,
18 | Fira Sans,
19 | Droid Sans,
20 | Helvetica Neue,
21 | sans-serif;
22 | }
23 |
24 | a {
25 | color: inherit;
26 | text-decoration: none;
27 | }
28 |
29 | * {
30 | box-sizing: border-box;
31 | }
32 |
33 | img {
34 | max-width: 100%;
35 | height: auto;
36 | }
37 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './pages/**/*.{js,ts,jsx,tsx}',
5 | './components/**/*.{js,ts,jsx,tsx}',
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
--------------------------------------------------------------------------------
/utils/axiosInstance.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | const axiosInstance = axios.create({
4 | baseURL: 'https://jsonplaceholder.typicode.com'
5 | // other configurations
6 | })
7 |
8 | axiosInstance.interceptors.response.use(
9 | (response) => response,
10 | (error) => {
11 | if (error.response && error.response.status === 401) {
12 | console.log('call the refresh token api here')
13 | // Handle 401 error, e.g., redirect to login or refresh token
14 | }
15 | return Promise.reject(error)
16 | }
17 | )
18 |
19 | export default axiosInstance
20 |
--------------------------------------------------------------------------------
/utils/fetcher.js:
--------------------------------------------------------------------------------
1 | import axiosInstance from './axiosInstance'
2 |
3 | const fetcher = (url) => axiosInstance.get(url).then((res) => res.data)
4 |
5 | export default fetcher
6 |
--------------------------------------------------------------------------------