├── .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 | {`Image 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 | 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 | 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 | 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 |
45 | 55 | 56 | 66 | 67 | 77 | 78 | 88 | 89 | 99 | 100 | 110 | 111 | Submit 112 | 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 | 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 | --------------------------------------------------------------------------------