diff --git a/src/components/mvpsdvlpr/Display.jsx b/src/components/mvpsdvlpr/Display.jsx new file mode 100644 index 000000000..d6c34d0af --- /dev/null +++ b/src/components/mvpsdvlpr/Display.jsx @@ -0,0 +1,132 @@ +import React, { useState, useRef } from 'react' +import Button from './container/button/Button' +import Container from './container/Container' +import Tooltip from './container/tooltip/Tooltip' + +import './styles/Display.css' +import { copyToClipBoard, generatePassword } from './utils/Helpers' + +const Display = () => { + const [password, setPassword] = useState('') + const [rangeValue, setRange] = useState() + const [passwordProps, setPasswordProps] = useState() + const [tooltip, setTooltip] = useState(false) + const [type, setType] = useState('password') + const passwordRef = useRef(null) + let pwdDescription = '' + + const generateNewPassword = () => { + const pwd = + rangeValue > 3 + ? generatePassword(passwordProps, rangeValue) + : generatePassword(passwordProps, 3) + setPassword(pwd) + } + + const copyClipBoard = (e) => { + e.preventDefault() + copyToClipBoard(passwordRef.current) + setTooltip(true) + setTimeout(() => { + setTooltip(false) + }, 2000) + } + + const onSelectTag = (e) => { + setType(e.target.value) + } + + const setBackgroundColor = (password) => { + if (password && password.length === 1 && password.length <= 5) { + pwdDescription = 'Bad password' + return '#cb473e' + } else if (password && password.length >= 6 && password.length <= 10) { + pwdDescription = 'Weak password' + return '#f07d58' + } else if (password && password.length > 10) { + pwdDescription = 'Strong password' + return '#55a95d' + } else { + pwdDescription = 'Bad password' + return '#cb473e' + } + } + + return ( + <> +
+ +
+
+
+
+
+ +
+ +
+ {password && password.length > 10 + ? ( + <> + {pwdDescription} + + ) + : ( + <> + {pwdDescription} + + )} +
+
+ +
+
+
+
+ + + + ) +} + +const selectTagStyle = { + backgroundColor: 'inherit', + color: '#506175', + width: '20%', + height: 'auto', + marginLeft: '-4px' +} + +export default Display diff --git a/src/components/mvpsdvlpr/Generatorpsswrd.jsx b/src/components/mvpsdvlpr/Generatorpsswrd.jsx new file mode 100644 index 000000000..bccac83e7 --- /dev/null +++ b/src/components/mvpsdvlpr/Generatorpsswrd.jsx @@ -0,0 +1,14 @@ +import React from 'react' +import Display from './Display' +import Header from './Header' + +const Generatorpsswrd = () => { + return ( + <> +
+ + + ) +} + +export default Generatorpsswrd diff --git a/src/components/mvpsdvlpr/Header.jsx b/src/components/mvpsdvlpr/Header.jsx new file mode 100644 index 000000000..1a876df4d --- /dev/null +++ b/src/components/mvpsdvlpr/Header.jsx @@ -0,0 +1,17 @@ +import React from 'react' + +import './styles/Header.css' +const Header = () => { + return ( +
+
+

Strong Password Generator

+
+

Create strong passwords with Password Generator

+
+
+
+ ) +} + +export default Header diff --git a/src/components/mvpsdvlpr/container/Container.jsx b/src/components/mvpsdvlpr/container/Container.jsx new file mode 100644 index 000000000..2085b47f3 --- /dev/null +++ b/src/components/mvpsdvlpr/container/Container.jsx @@ -0,0 +1,219 @@ +import React, { useState, useEffect, useMemo } from 'react' + +import '../styles/Container.css' +import { copyToClipBoard, generatePassword, setPasswordLength } from '../utils/Helpers' +import Button from './button/Button' +import CheckBox from './checkbox/CheckBox' +import Slider from './slider/Slider' + +const CHECKBOX_LIST = [ + { + id: 0, + name: 'uppercase', + label: 'Uppercase', + isChecked: true + }, + { + id: 1, + name: 'lowercase', + label: 'Lowercase', + isChecked: true + }, + { + id: 2, + name: 'symbols', + label: 'Symbols', + isChecked: true + }, + { + id: 3, + name: 'numbers', + label: 'Numbers', + isChecked: true + } +] + +const Container = (props) => { + const { setPassword, setRange, setPasswordProps, passwordRef, type } = props + + const [rangeValue, setRangeValue] = useState(12) + const [checkbox, setCheckBox] = useState({ + uppercase: true, + lowercase: true, + symbols: true, + numbers: true + }) + const [checked, setChecked] = useState(false) + const [checkedName, setCheckedName] = useState('') + const [minMaxValue, setMinMaxValue] = useState({ + min: 1, + max: 60 + }) + + const { uppercase, lowercase, symbols, numbers } = checkbox + const { min, max } = minMaxValue + + useEffect(() => { + setPasswordLength(rangeValue) + setRange(rangeValue) + setRangeValue(rangeValue) + passwordGenerated(checkbox, rangeValue) + + checkBoxCount() + + // eslint-disable-next-line + }, [uppercase, lowercase, symbols, numbers]) + + const checkBoxCount = () => { + const checkedCount = Object.keys(checkbox).filter((key) => checkbox[key]) + const disabled = checkedCount.length === 1 + const name = checkedCount[0] + if (disabled) { + setChecked(disabled) + setCheckedName(name) + } else { + setChecked(false) + setCheckedName('') + } + } + + const updateCheckBoxes = () => { + if (type === 'pin') { + CHECKBOX_LIST.map((checkbox) => { + const name = checkbox.name + if (name !== 'numbers') { + checkbox.isChecked = false + const checkboxProps = { + name, + checkedName: name, + checked: true, + isChecked: checkbox.isChecked, + min: 0, + max: 15, + length: 3 + } + checkBoxProperties(checkboxProps) + } + return '' + }) + } else { + CHECKBOX_LIST.map((checkbox) => { + const name = checkbox.name + checkbox.isChecked = true + const checkboxProps = { + name, + checkedName: '', + checked: false, + isChecked: checkbox.isChecked, + min: 1, + max: 60, + length: 12 + } + checkBoxProperties(checkboxProps) + return '' + }) + } + } + + const checkBoxProperties = (checkBoxProps) => { + const { name, checked, isChecked, checkedName, min, max, length } = checkBoxProps + + setCheckBox((prevState) => ({ ...prevState, [name]: isChecked })) + setChecked(checked) + setCheckedName(checkedName) + setPasswordLength(length) + setMinMaxValue({ min, max }) + setRangeValue(length) + setRange(length) + } + + useMemo(updateCheckBoxes, [type]) + + const passwordGenerated = (checkbox, rangeValue) => { + const pwd = + rangeValue > 3 ? generatePassword(checkbox, rangeValue) : generatePassword(checkbox, 3) + setPassword(pwd) + setPasswordProps(checkbox) + } + + const onChangeSlider = (e) => { + setPasswordLength(e.target.value) + setRangeValue(e.target.value) + setRange(e.target.value) + passwordGenerated(checkbox, e.target.value) + } + + const onChangeCheckBox = (e) => { + if (type !== 'pin') { + const { name, checked } = e.target + CHECKBOX_LIST.map((checkbox) => { + if (checkbox.name === name) { + checkbox.isChecked = checked + setCheckBox((prevState) => ({ ...prevState, [name]: checkbox.isChecked })) + setPasswordLength(rangeValue) + setRangeValue(rangeValue) + } + + return '' + }) + } + } + + const copyClipBoard = (elementRef) => (e) => { + e.preventDefault() + copyToClipBoard(elementRef) + } + + return ( +
+

Use the slider, and select from the options.

+ +
+
+
+   + +
+
+ +
+
+ {CHECKBOX_LIST.map((checkbox) => ( + + ))} +
+
+
+
+ +
+
+
+
+
+
+
+ ) +} + +export default Container diff --git a/src/components/mvpsdvlpr/container/button/Button.jsx b/src/components/mvpsdvlpr/container/button/Button.jsx new file mode 100644 index 000000000..619abb33e --- /dev/null +++ b/src/components/mvpsdvlpr/container/button/Button.jsx @@ -0,0 +1,15 @@ +import React from 'react' + +const Button = (props) => { + const { label, className, iconClass, handleClick } = props + + return ( + <> + + + ) +} + +export default Button diff --git a/src/components/mvpsdvlpr/container/checkbox/CheckBox.jsx b/src/components/mvpsdvlpr/container/checkbox/CheckBox.jsx new file mode 100644 index 000000000..41273ebfe --- /dev/null +++ b/src/components/mvpsdvlpr/container/checkbox/CheckBox.jsx @@ -0,0 +1,28 @@ +import React from 'react' + +import '../../styles/CheckBox.css' +const CheckBox = (props) => { + const { label, value, checked, name, onChange, disabled } = props + + return ( + <> +
+ +
+ + ) +} + +export default CheckBox diff --git a/src/components/mvpsdvlpr/container/slider/Slider.jsx b/src/components/mvpsdvlpr/container/slider/Slider.jsx new file mode 100644 index 000000000..27783b08b --- /dev/null +++ b/src/components/mvpsdvlpr/container/slider/Slider.jsx @@ -0,0 +1,54 @@ +import React, { useState, useRef } from 'react' + +import '../../styles/Slider.css' + +const Slider = (props) => { + const { step, min, max, value, defaultLength, onChangeValue } = props + + const rangeRef = useRef() + let [range, setRange] = useState() + + const activeRangeColor = '#4aa1f3' + const rangeBackground = '#d7dcdf' + + const handleChange = (max) => (e) => { + onChangeValue(e) + const value = e.target.value + setRange(value) + const progress = (value / max) * 100 + '%' + const newBackgroundStyle = `linear-gradient(90deg, ${activeRangeColor} 0% ${progress}, ${rangeBackground} ${progress} 100%)` + rangeRef.current.style.background = newBackgroundStyle + } + + if (range !== defaultLength || !range) { + range = defaultLength + } + + const progressValue = range + const progress = (progressValue / max) * 100 + '%' + + const styleInput = { + background: `linear-gradient(90deg, ${activeRangeColor} 0% ${progress}, ${rangeBackground} ${progress} 100%)` + } + + return ( +
+
+ + {progressValue} +
+
+ ) +} + +export default Slider diff --git a/src/components/mvpsdvlpr/container/tooltip/Tooltip.jsx b/src/components/mvpsdvlpr/container/tooltip/Tooltip.jsx new file mode 100644 index 000000000..5512d8ddc --- /dev/null +++ b/src/components/mvpsdvlpr/container/tooltip/Tooltip.jsx @@ -0,0 +1,22 @@ +import React from 'react' + +import '../../styles/Tooltip.css' +const Tooltip = (props) => { + const { message, position, displayTooltip } = props + + return ( + <> + {displayTooltip + ? ( +
+
{message}
+
+ ) + : ( + '' + )} + + ) +} + +export default Tooltip diff --git a/src/components/mvpsdvlpr/styles/CheckBox.css b/src/components/mvpsdvlpr/styles/CheckBox.css new file mode 100644 index 000000000..66c65618e --- /dev/null +++ b/src/components/mvpsdvlpr/styles/CheckBox.css @@ -0,0 +1,73 @@ +.checkbox-container { + margin: 0 auto; + text-align: center; + bottom: 10px; +} + +.container { + display: block; + position: relative; + padding-left: 35px; + margin-bottom: 12px; + cursor: pointer; + font-size: 20px; + user-select: none; +} + +.container h1 { + font-size: 20px; + font-weight: normal; +} + +/* Hide the browser's default checkbox */ +.container .checkbox-input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +/* Create a custom checkbox */ +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 25px; + width: 25px; + background-color: #eee; + float: right; +} + +/* On mouse-over, add a grey background color */ +.container:hover input ~ .checkmark { + background-color: #ccc; +} + +/* When the checkbox is checked, add a blue background */ +.container input:checked ~ .checkmark { + background-color: #2196f3; +} + +/* Create the checkmark/indicator (hidden when not checked) */ +.checkmark::after { + content: ''; + position: absolute; + display: none; +} + +/* Style the checkmark/indicator */ +.container .checkmark::after { + left: 10px; + top: 7px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 3px 3px 0; + transform: rotate(45deg); +} + +/* Show the checkmark when checked */ +.container input:checked ~ .checkmark::after { + display: block; +} diff --git a/src/components/mvpsdvlpr/styles/Container.css b/src/components/mvpsdvlpr/styles/Container.css new file mode 100644 index 000000000..916cda64c --- /dev/null +++ b/src/components/mvpsdvlpr/styles/Container.css @@ -0,0 +1,19 @@ +.password-settings { + border: 1px solid #eaeaea; + border-radius: 5px; + background-color: #fff; + max-width: 800px; + margin: -10px auto 15px; + padding: 25px; +} + +.password-settings h3 { + font-size: 18px; + font-weight: normal; +} + +.password-btn { + background-color: #4aa1f3; + color: #fff !important; + font-weight: 600; +} diff --git a/src/components/mvpsdvlpr/styles/Display.css b/src/components/mvpsdvlpr/styles/Display.css new file mode 100644 index 000000000..e38d376c6 --- /dev/null +++ b/src/components/mvpsdvlpr/styles/Display.css @@ -0,0 +1,36 @@ +.password-display-container { + border: 1px solid #eaeaea; + border-radius: 5px; + background-color: #cb473e; + display: flex; + position: relative; + max-width: 1100px; + height: 150px; + margin: 0 auto 10px; +} + +.password-display { + width: 100%; + max-width: auto; + border: 0; + color: white; + font-size: 1.4rem; + text-align: left; + padding-left: 5px; + height: 100%; + line-height: 150px; +} + +.password-display-input { + background-color: inherit; + border: none !important; + outline: none !important; + color: white; + height: 50%; + width: 100%; + font-weight: bold; +} + +.password-display-input::selection { + background: none; +} diff --git a/src/components/mvpsdvlpr/styles/Header.css b/src/components/mvpsdvlpr/styles/Header.css new file mode 100644 index 000000000..80ffb55be --- /dev/null +++ b/src/components/mvpsdvlpr/styles/Header.css @@ -0,0 +1,18 @@ +.header { + color: #282c34; + position: relative; + padding: 55px 0; + text-align: center; +} + +.h1 { + color: #ff554f; + font-size: 2.5rem !important; + text-align: center; + padding: 0 10px; +} + +h4 { + color: #506175; + font-size: 1.5rem; +} diff --git a/src/components/mvpsdvlpr/styles/Slider.css b/src/components/mvpsdvlpr/styles/Slider.css new file mode 100644 index 000000000..bd1d0417e --- /dev/null +++ b/src/components/mvpsdvlpr/styles/Slider.css @@ -0,0 +1,73 @@ +.slider-container { + margin-bottom: 50px; +} + +.slider { + width: 100%; +} + +.range-slider { + appearance: none; + width: 90%; + height: 10px; + border-radius: 5px; + background: #d7dcdf; + outline: none; + padding: 0; + margin: 0; +} + +.range-slider::-webkit-slider-thumb { + appearance: none; + width: 20px; + height: 20px; + border-radius: 50%; + background: #4aa1f3; + cursor: pointer; + box-shadow: 0 0 0 3px #fff, 0 0 0 6px #4aa1f3; + transition: background 0.15s ease-in-out; +} + +.range-slider:active::-webkit-slider-thumb { + background: #4aa1f3; +} + +.range-slider::-moz-range-thumb { + width: 20px; + height: 20px; + border: 0; + border-radius: 50%; + background: red; + cursor: pointer; + transition: background 0.15s ease-in-out; +} + +::-moz-range-track { + background: #fff; + border: 0; +} + +.range-slider-value { + display: inline-block; + position: relative; + width: 60px; + color: #fff; + line-height: 20px; + text-align: center; + border-radius: 3px; + background: #4aa1f3; + padding: 5px 10px; + margin-left: 12px; +} + +.range-slider-value::after { + position: absolute; + top: 8px; + left: -7px; + width: 0; + height: 0; + border-top: 7px solid transparent; + border-right: 7px solid #4aa1f3; + border-bottom: 7px solid transparent; + content: ''; +} diff --git a/src/components/mvpsdvlpr/styles/Tooltip.css b/src/components/mvpsdvlpr/styles/Tooltip.css new file mode 100644 index 000000000..63953a253 --- /dev/null +++ b/src/components/mvpsdvlpr/styles/Tooltip.css @@ -0,0 +1,90 @@ +.tooltip { + position: relative; +} + +.tooltip-bubble { + min-width: 120px; + max-width: 210px; + position: absolute; + z-index: 10; +} + +.tooltip-bubble::after { + content: ''; + position: absolute; +} + +.tooltip-top { + bottom: 100%; + left: 50%; + padding-bottom: 9px; + transform: translateX(-50%); +} + +.tooltip-top::after { + border-left: 9px solid transparent; + border-right: 9px solid transparent; + border-top: 9px solid rgb(0 0 0 / 50%); + bottom: 0; + left: 50%; + transform: translateX(-50%); +} + +.tooltip-bottom { + top: 100%; + left: 50%; + padding-top: 9px; + transform: translateX(-50%); +} + +.tooltip-bottom::after { + border-left: 9px solid transparent; + border-right: 9px solid transparent; + border-bottom: 9px solid #fff; + top: 0; + left: 50%; + transform: translateX(-50%); +} + +.tooltip-left { + top: 50%; + right: 100%; + padding-right: 9px; + transform: translateY(-50%); +} + +.tooltip-left::after { + border-left: 9px solid rgb(0 0 0 / 50%); + border-top: 9px solid transparent; + border-bottom: 9px solid transparent; + top: 50%; + right: 0; + transform: translateY(-50%); +} + +.tooltip-right { + top: 50%; + left: 100%; + padding-left: 9px; + transform: translateY(-50%); +} + +.tooltip-right::after { + border-right: 9px solid rgb(0 0 0 / 50%); + border-top: 9px solid transparent; + border-bottom: 9px solid transparent; + top: 50%; + left: 0; + transform: translateY(-50%); +} + +.tooltip-message { + background: rgb(0 0 0 / 50%); + border-radius: 3px; + color: #fff; + font-size: 0.75rem; + line-height: 1.4; + padding: 0.75em; + text-align: center; + transition: opacity 1s ease-in-out; +} diff --git a/src/components/mvpsdvlpr/utils/Helpers.js b/src/components/mvpsdvlpr/utils/Helpers.js new file mode 100644 index 000000000..6996cbc05 --- /dev/null +++ b/src/components/mvpsdvlpr/utils/Helpers.js @@ -0,0 +1,75 @@ +let characters = '' +let passwordLength = 0 + +const setUpperCase = (isUpperCase) => { + if (isUpperCase) { + characters += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + } + return '' +} + +const setLowerCase = (isLowerCase) => { + if (isLowerCase) { + characters += 'abcdefghijklmnopqrstuvwxyz' + } + return '' +} + +const setSymbols = (isSymbol) => { + if (isSymbol) { + characters += '!@#$%^&*()<>,.?/[]{}-=_+|/' + } + return '' +} + +const setNumber = (isNumeric) => { + if (isNumeric) { + characters += '0123456789' + } + return '' +} + +const getRandomInteger = (min, max) => { + return Math.floor(Math.random() * (max - min + 1)) + min +} + +const passwordCharacters = () => { + const characterList = characters + let password = '' + if (characterList.length > 0) { + for (let i = 0; i < passwordLength; i++) { + password += characterList[getRandomInteger(0, characterList.length - 1)] + } + characters = '' + passwordLength = 0 + + return password + } +} + +export const setPasswordLength = (length) => { + passwordLength = length + return passwordLength +} + +export const generatePasswordLength = () => { + return passwordLength +} + +export const generatePassword = (passwordProps, pwdLength) => { + const { uppercase, lowercase, symbols, numbers } = passwordProps + + setPasswordLength(pwdLength) + setUpperCase(uppercase) + setLowerCase(lowercase) + setSymbols(symbols) + setNumber(numbers) + + const password = passwordCharacters() + return password +} + +export const copyToClipBoard = (elementRef) => { + elementRef.select() + document.execCommand('copy') +} diff --git a/src/pages/entry/mvpsdvlpr/index.astro b/src/pages/entry/mvpsdvlpr/index.astro new file mode 100644 index 000000000..8b7186774 --- /dev/null +++ b/src/pages/entry/mvpsdvlpr/index.astro @@ -0,0 +1,8 @@ +--- +import Layout from '@layout' +import Generatorpsswrd from '@components/mvpsdvlpr/Generatorpsswrd' +--- + + + +