password manager

This commit is contained in:
root 2023-01-05 02:16:18 +00:00
commit a1bbe85b1a
25 changed files with 14423 additions and 0 deletions

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
coverage
build

60
.eslintrc Normal file
View File

@ -0,0 +1,60 @@
{
"env": {
"browser": true,
"es2021": true
},
"parser": "babel-eslint",
"extends": ["react-app", "airbnb", "prettier"],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": ["prettier"],
"overrides": [
{
"files": ["styledComponents.js"],
"rules": {
"import/prefer-default-export": "off"
}
}
],
"rules": {
"prettier/prettier": "error",
"react/jsx-filename-extension": [1, {"extensions": [".js", ".jsx"]}],
"react/state-in-constructor": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
"no-console": "off",
"react/prop-types": "off",
"jsx-a11y/label-has-associated-control": [
2,
{
"labelAttributes": ["htmlFor"]
}
],
"jsx-a11y/click-events-have-key-events": 0,
"jsx-a11y/no-noninteractive-element-interactions": [
"off",
{
"handlers": ["onClick"]
}
],
"react/prefer-stateless-function": [
0,
{
"ignorePureComponents": true
}
],
"no-unused-vars": "warn",
"jsx-a11y/alt-text": 1,
"react/no-unused-state": "warn",
"react/button-has-type": "warn",
"react/no-unescaped-entities": "warn",
"react/jsx-props-no-spreading": "off",
"operator-assignment": ["warn", "always"],
"radix": "off"
}
}

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea/
.eslintcache
.vscode/
.results

3
.npmrc Normal file
View File

@ -0,0 +1,3 @@
registry=https://registry.npmjs.org/
package-lock=true
save-exact=true

22
.pnpm-debug.log Normal file
View File

@ -0,0 +1,22 @@
{
"0 debug pnpm:scope": {
"selected": 1
},
"1 error pnpm": {
"code": "ELIFECYCLE",
"errno": "ENOENT",
"syscall": "spawn",
"file": "sh",
"pkgid": "password-manager@1.0.0",
"stage": "start",
"script": "react-scripts start",
"pkgname": "password-manager",
"err": {
"name": "pnpm",
"message": "password-manager@1.0.0 start: `react-scripts start`\nspawn ENOENT",
"code": "ELIFECYCLE",
"stack": "pnpm: password-manager@1.0.0 start: `react-scripts start`\nspawn ENOENT\n at ChildProcess.<anonymous> (/usr/local/lib/node_modules/pnpm/dist/pnpm.cjs:92384:22)\n at ChildProcess.emit (events.js:400:28)\n at maybeClose (internal/child_process.js:1058:16)\n at Process.ChildProcess._handle.onexit (internal/child_process.js:293:5)"
}
},
"2 warn pnpm:global": " Local package.json exists, but node_modules missing, did you mean to install?"
}

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
coverage
build

26
.prettierrc Normal file
View File

@ -0,0 +1,26 @@
{
"arrowParens": "avoid",
"bracketSpacing": false,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"printWidth": 80,
"overrides": [
{
"files": "*.md",
"options": {
"printWidth": 1000
}
}
],
"proseWrap": "always",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false
}

126
README.md Normal file
View File

@ -0,0 +1,126 @@
In this project, let's build a **Password Manager** by applying the concepts we have learned till now.
### Refer to the image below:
<br/>
<div style="text-align: center;">
<img src="https://assets.ccbp.in/frontend/content/react-js/passowrd-manager-output-v0.gif" alt="password manager" style="max-width:70%;box-shadow:0 2.8px 2.2px rgba(0, 0, 0, 0.12)">
</div>
<br/>
### Design Files
<details>
<summary>Click to view</summary>
- [Extra Small (Size < 576px) and Small (Size >= 576px) - No Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-no-passwords-sm-output-v2.png)
- [Extra Small (Size < 576px) and Small (Size >= 576px) - Masked Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-masked-passwords-sm-output-v2.png)
- [Extra Small (Size < 576px) and Small (Size >= 576px) - Show Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-sm-output-v2.png)
- [Medium (Size >= 768px), Large (Size >= 992px) and Extra Large (Size >= 1200px) - No Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-no-passwords-lg-output.png)
- [Medium (Size >= 768px), Large (Size >= 992px) and Extra Large (Size >= 1200px) - Masked Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-masked-passwords-lg-output.png)
- [Medium (Size >= 768px), Large (Size >= 992px) and Extra Large (Size >= 1200px) - Show Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-lg-output.png)
</details>
### Set Up Instructions
<details>
<summary>Click to view</summary>
- Download dependencies by running `npm install`
- Start up the app using `npm start`
</details>
### Completion Instructions
<details>
<summary>Functionality to be added</summary>
<br/>
The app must have the following functionalities
- Initially, the website input, username input, and password input should be empty and [No Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-no-passwords-lg-output.png) should be displayed
- When non-empty values are provided for the website, username, and password and the **Add** button is clicked,
- A new password item should be added to the list of passwords
- The passwords count should be incremented by one
- The **stars image** should be displayed in the password items instead of the passwords
- The value of the input fields for website, username, and password should be updated to their initial values
- When the **Show Password** is checked, then the password should be displayed instead of the **stars image**
- When a non-empty value is provided in the search input field, then password items whose website is matched with the search input value irrespective of the case should be displayed
- When a non-empty value is provided in the search input field, and if the website of any password item does not match the value given in the search input, then [No Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-no-passwords-lg-output.png) should be displayed
- When the delete button of a password item is clicked,
- The respective password item should be deleted from the list of passwords
- The passwords count should be decremented by one
- When all password items are deleted, then [No Passwords View](https://assets.ccbp.in/frontend/content/react-js/password-manager-no-passwords-lg-output.png) should be displayed
</details>
### Important Note
<details>
<summary>Click to view</summary>
<br/>
**The following instructions are required for the tests to pass**
- HTML input element for website should have the placeholder as **Enter Website**
- HTML input element for username should have the placeholder as **Enter Username**
- HTML input element for password should have the placeholder as **Enter Password**
- The delete button for each password item should have the testid as **delete**
</details>
### Resources
<details>
<summary>Image URLs</summary>
- [https://assets.ccbp.in/frontend/react-js/password-manager-logo-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-logo-img.png) alt should be **app logo**
- [https://assets.ccbp.in/frontend/react-js/password-manager-sm-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-sm-img.png) alt should be **password manager**
- [https://assets.ccbp.in/frontend/react-js/password-manager-lg-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-lg-img.png) alt should be **password manager**
- [https://assets.ccbp.in/frontend/react-js/password-manager-website-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-website-img.png) alt should be **website**
- [https://assets.ccbp.in/frontend/react-js/password-manager-username-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-username-img.png) alt should be **username**
- [https://assets.ccbp.in/frontend/react-js/password-manager-password-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-password-img.png) alt should be **password**
- [https://assets.ccbp.in/frontend/react-js/password-manager-search-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-search-img.png) alt should be **search**
- [https://assets.ccbp.in/frontend/react-js/no-passwords-img.png](https://assets.ccbp.in/frontend/react-js/no-passwords-img.png) alt should be **no passwords**
- [https://assets.ccbp.in/frontend/react-js/password-manager-stars-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-stars-img.png) alt should be **stars**
- [https://assets.ccbp.in/frontend/react-js/password-manager-delete-img.png](https://assets.ccbp.in/frontend/react-js/password-manager-delete-img.png) alt should be **delete**
</details>
<details>
<summary>Colors</summary>
<br/>
<div style="background-color: #9ba9eb; width: 150px; padding: 10px; color: black">Hex: #9ba9eb</div>
<div style="background-color: #c3caea; width: 150px; padding: 10px; color: black">Hex: #c3caea</div>
<div style="background-color: #5763a5; width: 150px; padding: 10px; color: black">Hex: #5763a5</div>
<div style="background-color: #f8fafc; width: 150px; padding: 10px; color: black">Hex: #f8fafc</div>
<div style="background-color: #454f84; width: 150px; padding: 10px; color: white">Hex: #454f84</div>
<div style="background-color: #0b69ff; width: 150px; padding: 10px; color: black">Hex: #0b69ff</div>
<div style="background-color: #94a3b8; width: 150px; padding: 10px; color: black">Hex: #94a3b8</div>
<div style="background-color: #b6c3ca; width: 150px; padding: 10px; color: black">Hex: #b6c3ca</div>
<div style="background-color: #7683cb; width: 150px; padding: 10px; color: black">Hex: #7683cb</div>
<div style="background-color: #f59e0b; width: 150px; padding: 10px; color: black">Hex: #f59e0b</div>
<div style="background-color: #10b981; width: 150px; padding: 10px; color: black">Hex: #10b981</div>
<div style="background-color: #f97316; width: 150px; padding: 10px; color: black">Hex: #f97316</div>
<div style="background-color: #14b8a6; width: 150px; padding: 10px; color: black">Hex: #14b8a6</div>
<div style="background-color: #b91c1c; width: 150px; padding: 10px; color: black">Hex: #b91c1c</div>
<div style="background-color: #ffffff; width: 150px; padding: 10px; color: black">Hex: #ffffff</div>
<div style="background-color: #0ea5e9; width: 150px; padding: 10px; color: black">Hex: #0ea5e9</div>
<div style="background-color: #64748b; width: 150px; padding: 10px; color: white">Hex: #64748b</div>
</details>
<details>
<summary>Font-families</summary>
- Roboto
</details>
> ### _Things to Keep in Mind_
>
> - All components you implement should go in the `src/components` directory.
> - Don't change the component folder names as those are the files being imported into the tests.
> - **Do not remove the pre-filled code**
> - Want to quickly review some of the concepts youve been learning? Take a look at the Cheat Sheets.

68
package.json Normal file
View File

@ -0,0 +1,68 @@
{
"name": "password-manager",
"private": true,
"version": "1.0.0",
"engines": {
"node": "^10.13 || 12 || 14 || 15",
"npm": ">=6"
},
"dependencies": {
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.5",
"@testing-library/user-event": "12.6.2",
"chalk": "4.1.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"uuid": "8.3.2"
},
"devDependencies": {
"eslint-config-airbnb": "18.2.1",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-prettier": "3.3.1",
"husky": "4.3.8",
"lint-staged": "10.5.4",
"npm-run-all": "4.1.5",
"prettier": "2.2.1",
"react-scripts": "4.0.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"lint": "eslint .",
"lint:fix": "eslint --fix src/",
"format": "prettier --write \"./src\"",
"run-all": "npm-run-all --parallel test lint:fix"
},
"lint-staged": {
"*.js": [
"npm run lint:fix"
],
"*.{js, jsx, json, html, css}": [
"npm run format"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"jest": {
"collectCoverageFrom": [
"src/**/*.js"
]
},
"browserslist": {
"development": [
"last 2 chrome versions",
"last 2 firefox versions",
"last 2 edge versions"
],
"production": [
">1%",
"last 4 versions",
"Firefox ESR",
"not ie < 11"
]
}
}

13270
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

BIN
public/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/img/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/img/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

54
public/index.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/img/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link
href="https://fonts.googleapis.com/css2?family=Bree+Serif&family=Caveat:wght@400;500;600;700&family=Lobster&family=Monoton&family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&family=Playfair+Display+SC:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;1,100;1,300;1,400;1,500;1,700&display=swap"
rel="stylesheet"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/img/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<script type="text/javascript">
// hide HRM logs because they're distracting
// prettier-ignore
const log = console.log;
// prettier-ignore
console.log = (...args) =>
args[0]?.includes?.('[HMR]') ? null : log(...args);
</script>
</head>
<body>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

25
public/manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"short_name": "app_short_name",
"name": "App_name",
"icons": [
{
"src": "img/favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "img/logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "img/logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

12
src/App.css Normal file
View File

@ -0,0 +1,12 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

6
src/App.js Normal file
View File

@ -0,0 +1,6 @@
import PasswordManager from './components/PasswordManager'
import './App.css'
const App = () => <PasswordManager />
export default App

View File

@ -0,0 +1,79 @@
.userPasswordDetailsList_item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: #5763a5;
outline: none;
border: 0.1px solid #9ba9eb;
border-radius: 3px;
padding: 15px;
padding-right: 10px;
margin-right: 10px;
margin-bottom: 10px;
min-width: 217px;
min-height: 85px;
}
.passwordDetails_container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.initialContainer {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #0b69ff;
height: 40px;
width: 40px;
border-radius: 50%;
margin-right: 10px;
}
.initial{
font-family: "Roboto";
font-size: 16px;
font-weight: 500;
color: #ffffff;
}
.userPassword_container{
display: flex;
flex-direction: column;
justify-content: center;
}
.input_text{
font-family: "Roboto";
font-size: 12px;
color: #ffffff;
padding: 2px;
margin: 0px;
}
.stars_image {
width: 100px;
height: 15px;
margin-top: 5px;
}
.show_password {
font-family: "Roboto";
font-size: 12px;
color: #ffffff;
padding: 2px;
margin: 0px;
}
.delete_button{
background-color: transparent;
border: none;
}
.del_image{
height: 20px;
}

View File

@ -0,0 +1,43 @@
import './index.css'
const PasswordItem = props => {
const {userDetails, checked} = props
const {id, website, username, password, initialClassName} = userDetails
const initial = username ? username[0].toUpperCase() : ''
const onDelete = () => {
const {deleteUserDetails} = props
deleteUserDetails(id)
}
return (
<li className="userPasswordDetailsList_item">
<div className="passwordDetails_container">
<div className={`initialContainer ${initialClassName}`}>
<p className="initial">{initial}</p>
</div>
<div className="userPassword_container">
<p className="input_text">{website}</p>
<p className="input_text">{username}</p>
{!checked && (
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-stars-img.png"
className="stars_image"
alt="stars"
/>
)}
{checked && <p className="show_password">{password}</p>}
</div>
</div>
<button type="button" className="delete_button" onClick={onDelete}>
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-delete-img.png"
alt="delete"
className="del_image"
/>
</button>
</li>
)
}
export default PasswordItem

View File

@ -0,0 +1,327 @@
.app_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #9ba9eb;
background-size: cover;
min-width: 100%;
}
.responsive_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
border-radius: 10px;
}
@media screen and (min-width: 768px) {
.responsive_container {
width: 85%;
max-width: 1140px;
}
}
.app_logo_container {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
width: 100%;
}
.app_logo {
width: 160px;
height: 40px;
align-self: flex-start;
padding-left: 7.5%;
margin-top: 25px;
}
@media screen and (min-width: 768px) {
.app_logo {
width: 280px;
height: 60px;
}
}
.userDetails_container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #5763a5;
border-radius: 10px;
width: 85%;
margin-top: 10px;
}
@media screen and (min-width: 768px) {
.userDetails_container {
flex-direction: column;
width: 85%;
max-width: 1140px;
}
}
.add_userDetail_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 45px;
padding-left: 5%;
padding-right: 5%;
margin-bottom: 45px;
}
@media screen and (min-width: 768px) {
.add_userDetail_container {
flex-direction: row;
justify-content: space-between;
width: 100%;
max-width: 1140px;
}
}
.form {
background-color: #454f84;
border-radius: 15px;
display: flex;
flex-direction: column;
padding-left: 25px;
padding-right: 30px;
padding-top: 30px;
padding-bottom: 30px;
}
.form_heading {
font-family: "Roboto";
font-size: 22px;
font-weight: 500;
color: #ffffff;
margin-top: 24px;
margin-bottom: 24px;
}
.form_input_container {
display: flex;
flex-direction: row;
margin-bottom: 20px;
}
.input_icon_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 50px;
width: 60px;
background-color: #ffffff;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.input_icon {
height: 20px;
}
.form_input {
height: 50px;
width: 300px;
outline: none;
border: 0px;
border-left: 1px solid #94a3b8;
padding-left: 10px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.add_button {
font-family: "Roboto";
font-size: 14px;
font-weight: 500;
color: #ffffff;
align-self: flex-end;
background-color: #0b69ff;
border: none;
border-radius: 5px;
padding-left: 20px;
padding-right: 20px;
padding-top: 8px;
padding-bottom: 8px;
outline: none;
}
.password_manager_image {
height: 350px;
margin-bottom: 15px;
}
.userDetailsList_container {
display: flex;
flex-direction: column;
background-color: #5763a5;
border-radius: 10px;
width: 85%;
margin-top: 10px;
min-height: 500px;
}
.header_container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10px;
padding-left: 45px;
padding-right: 45px;
}
.header_heading_container {
display: flex;
flex-direction: row;
margin-bottom: 0px;
padding-bottom: 0px;
}
.your_passwords_heading {
font-family: "Roboto";
font-size: 20px;
font-weight: 500;
color: #ffffff;
padding-top: 6px;
}
.count {
font-family: "Roboto";
font-size: 20px;
font-weight: 500;
color: #ffffff;
border: 1px solid #ffffff;
border-radius: 10px;
padding-left: 10px;
padding-right: 10px;
margin-left: 10px;
}
.search_container {
display: flex;
flex-direction: row;
}
.search_icon_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 40px;
width: 50px;
background-color: #ffffff;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.search_icon {
height: 25px;
}
.search_input {
font-family: "Roboto";
font-size: 16px;
font-weight: 600;
height: 40px;
outline: none;
border: 0px;
border-left: 1px solid #94a3b8;
padding-left: 10px;
}
.breakout_line {
border: 1px solid #9ba9eb;
width: 93%;
padding-top: 0px;
padding-bottom: 0px;
margin-top: 0px;
margin-bottom: 0px;
}
.check_box_container {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
padding-right: 45px;
padding-top: 10px;
padding-bottom: 10px;
}
.check_box {
height: 20px;
width: 20px;
}
.label_text {
font-family: "Roboto";
font-size: 15px;
font-weight: 600;
color: #ffffff;
}
.list-item-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 20px;
border: 2px solid white;
border-radius: 10px;
margin-right: 60px;
margin-bottom: 30px;
}
.no-password-image {
height: 300px;
}
.no-results-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.no-passwords-heading {
color: white;
font-family: 'roboto';
}
.unordered-list-container {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
justify-content: flex-start;
list-style-type: none;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.green {
background-color: green;
}
.violet {
background-color: violet;
}
.indigo {
background-color: indigo;
}
.yellow {
background-color: yellow;
}

View File

@ -0,0 +1,250 @@
import {Component} from 'react'
import {v4} from 'uuid'
import PasswordItem from '../PasswordItem'
import './index.css'
const initialContainerBgClassNames = [
'red',
'blue',
'green',
'violet',
'indigo',
'yellow',
'cyan',
]
class PasswordManager extends Component {
state = {
userDetailsList: [],
websiteInput: '',
usernameInput: '',
passwordInput: '',
checked: false,
searchInput: '',
}
onWebsite = event => {
this.setState({
websiteInput: event.target.value,
})
}
onUsername = event => {
this.setState({
usernameInput: event.target.value,
})
}
onPassword = event => {
this.setState({
passwordInput: event.target.value,
})
}
onSearch = event => {
this.setState({searchInput: event.target.value})
}
onAdd = event => {
event.preventDefault()
const {websiteInput, usernameInput, passwordInput} = this.state
const initialBgColorClassNames = `initial_container ${
initialContainerBgClassNames[
Math.ceil(Math.random() * initialContainerBgClassNames.length - 1)
]
}`
const newUser = {
id: v4(),
website: websiteInput,
username: usernameInput,
password: passwordInput,
initialClassName: initialBgColorClassNames,
}
this.setState(prevState => ({
userDetailsList: [...prevState.userDetailsList, newUser],
websiteInput: '',
usernameInput: '',
passwordInput: '',
searchInput: '',
}))
}
onCheckBox = () => {
this.setState(prevState => ({
checked: !prevState.checked,
}))
}
getSearchResults = () => {
const {searchInput, userDetailsList} = this.state
const searchResults = userDetailsList.filter(eachApp =>
eachApp.appName.toLowerCase().includes(searchInput.toLowerCase()),
)
return searchResults
}
deleteUserDetails = Id => {
const {userDetailsList} = this.state
this.setState({
userDetailsList: userDetailsList.filter(
userDetails => userDetails.id !== Id,
),
})
}
render() {
const {
userDetailsList,
websiteInput,
usernameInput,
passwordInput,
checked,
searchInput,
} = this.state
const searchedResult = userDetailsList.filter(eachObject =>
eachObject.website.toLowerCase().includes(searchInput.toLowerCase()),
)
let resultView
if (userDetailsList.length === 0 || searchedResult.length === 0) {
resultView = (
<div className="no-results-container">
<img
className="no-password-image"
alt="no passwords"
src="https://assets.ccbp.in/frontend/react-js/no-passwords-img.png"
/>
<p className="no-passwords-heading">No Passwords</p>
</div>
)
} else {
resultView = (
<ul className="unordered-list-container">
{searchedResult.map(eachObject => (
<PasswordItem
key={eachObject.id}
userDetails={eachObject}
checked={checked}
deleteUserDetails={this.deleteUserDetails}
/>
))}
</ul>
)
}
return (
<div className="app_container">
<div className="responsive_container">
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-logo-img.png"
alt="app logo"
className="app_logo"
/>
<div className="userDetails_container">
<div className="add_userDetail_container">
<form className="form" onSubmit={this.onAdd}>
<h1 className="form_heading">Add New Passwords</h1>
<div className="form_input_container">
<div className="input_icon_container">
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-website-img.png"
alt="website"
className="input_icon"
/>
</div>
<input
type="text"
placeholder="Enter Website"
className="form_input"
value={websiteInput}
onChange={this.onWebsite}
/>
</div>
<div className="form_input_container">
<div className="input_icon_container">
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-username-img.png"
alt="username"
className="input_icon"
/>
</div>
<input
type="text"
placeholder="Enter Username"
className="form_input"
value={usernameInput}
onChange={this.onUsername}
/>
</div>
<div className="form_input_container">
<div className="input_icon_container">
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-password-img.png"
alt="password"
className="input_icon"
/>
</div>
<input
type="password"
placeholder="Enter Password"
className="form_input"
value={passwordInput}
onChange={this.onPassword}
/>
</div>
<button type="submit" className="add_button">
Add
</button>
</form>
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-lg-img.png"
alt="password manager"
className="password_manager_image"
/>
</div>
</div>
<div className="userDetailsList_container">
<div className="header_container">
<div className="header_heading_container">
<h1 className="your_passwords_heading">Your Passwords</h1>
<p className="count">{userDetailsList.length}</p>
</div>
<div className="search_container">
<div className="search_icon_container">
<img
src="https://assets.ccbp.in/frontend/react-js/password-manager-search-img.png"
alt="search"
className="search_icon"
/>
</div>
<input
type="search"
placeholder="Search"
className="search_input"
value={searchInput}
onChange={this.onSearch}
/>
</div>
</div>
<hr className="breakout_line" />
<div className="check_box_container">
<input
id="checkBox"
type="checkbox"
className="check_box"
value={checked}
onChange={this.onCheckBox}
/>
<label htmlFor="checkBox" className="label_text">
Show Passwords
</label>
</div>
<div className="userPasswordDetailsList">{resultView}</div>
</div>
</div>
</div>
)
}
}
export default PasswordManager

10
src/index.js Normal file
View File

@ -0,0 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
)

6
src/setupTests.js Normal file
View File

@ -0,0 +1,6 @@
/* eslint-disable */
import '@testing-library/jest-dom'
import {configure} from '@testing-library/react'
configure({testIdAttribute: 'testid'})