diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fdcf04d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# A simple password generator built in `React.js`
+
+`npm install`
+
+`npm start`
+
+# For build production run `npm run build`
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..16fd271
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "password-gen",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-scripts": "5.0.1",
+ "react-toastify": "^9.1.1"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..65cf646
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ Simple Password Generator
+
+
+
+
+
\ No newline at end of file
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/src/App.jsx b/src/App.jsx
new file mode 100644
index 0000000..68c4ed7
--- /dev/null
+++ b/src/App.jsx
@@ -0,0 +1,188 @@
+import { React, useState} from 'react';
+import { toast, ToastContainer} from 'react-toastify';
+import './Assets/CSS/App.min.css';
+import { numbers, upperCaseLetters, lowerCaseLetters, specialCharacters } from './Components/Characters';
+import 'react-toastify/dist/ReactToastify.css';
+import { COPY_SUCCESS } from './Components/Message';
+
+function App() {
+ const [password, setPassword] = useState('')
+ const [passwordLength, setPasswordLength] = useState(20)
+ const [includeUppercase, setIncludeUppercase] = useState(false)
+ const [includeLowercase, setIncludeLowercase] = useState(false)
+ const [includeNumbers, setIncludeNumbers] = useState(false)
+ const [includeSymbols, setIncludeSymbols] = useState(false)
+
+ const handleGeneratePassword = (e) => {
+ if (
+ !includeUppercase &&
+ !includeLowercase &&
+ !includeNumbers &&
+ !includeSymbols
+ ) {
+ notify('Error! You must select atleast one option.', true)
+ }
+ let characterList = ''
+
+ if (includeLowercase) {
+ characterList = characterList + lowerCaseLetters
+ }
+
+ if (includeUppercase) {
+ characterList = characterList + upperCaseLetters
+ }
+
+ if (includeNumbers) {
+ characterList = characterList + numbers
+ }
+
+ if (includeSymbols) {
+ characterList = characterList + specialCharacters
+ }
+
+ setPassword(createPassword(characterList))
+ }
+
+ const createPassword = (characterList) => {
+ let password = ''
+ const characterListLength = characterList.length
+
+ for (let i = 0; i < passwordLength; i++) {
+ const characterIndex = Math.round(Math.random() * characterListLength)
+ password = password + characterList.charAt(characterIndex)
+ }
+
+ return password
+ }
+
+ const copyToClipboard = () => {
+ const newTextArea = document.createElement('textarea')
+ newTextArea.innerText = password
+ document.body.appendChild(newTextArea)
+ newTextArea.select()
+ document.execCommand('copy')
+ newTextArea.remove()
+ }
+
+ const notify = (message, hasError = false) => {
+ if (hasError) {
+ toast.error(message, {
+ position: 'top-center',
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: false,
+ progress: undefined,
+ })
+ } else {
+ toast(message, {
+ position: 'top-center',
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: false,
+ progress: undefined,
+ })
+ }
+ }
+
+ const handleCopyPassword = (e) => {
+ if(password === '') {
+ notify('Error! Nothing to copy', true)
+ } else {
+ copyToClipboard()
+ notify(COPY_SUCCESS)
+ }
+ }
+
+ return (
+
+
+
+
Simple Password Generator
+
+
{password}
+
+
+
+
+
+ setPasswordLength(e.target.value)}
+ type='number'
+ id='password-strength'
+ name='password-strength'
+ max='22'
+ min='10'
+ />
+
+
+
+
+ setIncludeUppercase(e.target.checked)}
+ type='checkbox'
+ id='uppercase-letters'
+ name='uppercase-letters'
+ />
+
+
+
+
+ setIncludeLowercase(e.target.checked)}
+ type='checkbox'
+ id='lowercase-letters'
+ name='lowercase-letters'
+ />
+
+
+
+
+ setIncludeNumbers(e.target.checked)}
+ type='checkbox'
+ id='include-numbers'
+ name='include-numbers'
+ />
+
+
+
+
+ setIncludeSymbols(e.target.checked)}
+ type='checkbox'
+ id='include-symbols'
+ name='include-symbols'
+ />
+
+
+
+
+
+
+
+ )
+}
+
+export default App
\ No newline at end of file
diff --git a/src/Assets/CSS/App.min.css b/src/Assets/CSS/App.min.css
new file mode 100644
index 0000000..fafab1e
--- /dev/null
+++ b/src/Assets/CSS/App.min.css
@@ -0,0 +1,77 @@
+@import url(https://fonts.bunny.net/css?family=abril-fatface:400|rubik:600,800i,900i);
+
+.App {
+ min-height: 100vh;
+ background-color: #101725;
+}
+
+.container {
+ width: 350px;
+ margin: 0 auto;
+ padding-top: 200px;
+}
+
+.generator {
+ background-color: #0b0f19;
+ border-radius: 3px;
+ box-shadow: 0px 2px 10px rgba(255, 255, 255, 0.2);
+ padding: 20px;
+}
+
+.generator__header {
+ text-align: center;
+ color: white;
+ margin-bottom: 20px;
+ font-family: 'Abril Fatface', display;
+ font-family: 'Rubik', sans-serif;
+}
+
+.generator__password {
+ position: relative;
+ background-color: #101725;
+ padding: 13px 10px;
+ color: white;
+ height: 46px;
+ margin-bottom: 15px;
+ font-family: 'Abril Fatface', display;
+ font-family: 'Rubik', sans-serif;
+}
+
+.copy__btn {
+ position: absolute;
+ background-color: #3b3b98;
+ color: white;
+ border: none;
+ height: 40px;
+ padding: 10;
+ cursor: pointer;
+ top: 3px;
+ right: 3x;
+}
+
+.generator__btn {
+ background-color: #101725;
+ border: none;
+ display: block;
+ width: 100%;
+ padding: 10px;
+ color: white;
+ font-size: 17px;
+ cursor: pointer;
+ font-family: 'Abril Fatface', display;
+ font-family: 'Rubik', sans-serif;
+}
+
+.form-group {
+ display: flex;
+ justify-content: space-between;
+ color: white;
+ margin-bottom: 15px;
+ font-family: 'Abril Fatface', display;
+ font-family: 'Rubik', sans-serif;
+}
+
+.passgen-text {
+ font-family: 'Abril Fatface', display;
+ font-family: 'Rubik', sans-serif;
+}
\ No newline at end of file
diff --git a/src/Assets/CSS/Index.min.css b/src/Assets/CSS/Index.min.css
new file mode 100644
index 0000000..cc4077c
--- /dev/null
+++ b/src/Assets/CSS/Index.min.css
@@ -0,0 +1,5 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
\ No newline at end of file
diff --git a/src/Components/Characters.js b/src/Components/Characters.js
new file mode 100644
index 0000000..63cf0c6
--- /dev/null
+++ b/src/Components/Characters.js
@@ -0,0 +1,4 @@
+export const numbers = '0123456789'
+export const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+export const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz'
+export const specialCharacters = "!'^+%&/()=?_#$½§{[]}|;:>÷`<.*-@é"
\ No newline at end of file
diff --git a/src/Components/Message.js b/src/Components/Message.js
new file mode 100644
index 0000000..37c2d36
--- /dev/null
+++ b/src/Components/Message.js
@@ -0,0 +1 @@
+export const COPY_SUCCESS = 'Password successfully copied to clipboard'
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..ae9887d
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './Assets/CSS/Index.min.css';
+import App from './App';
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('app')
+)
\ No newline at end of file