-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #542 from kevinalfito69/main
Contact Apps React JS
- Loading branch information
Showing
28 changed files
with
27,732 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Contact app react | ||
|
||
this repo is made to learn react fundamentals, and react router. | ||
## feature | ||
|
||
<ul> | ||
<li>Add contact</li> | ||
<li>Delete contact</li> | ||
</ul> |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "react-starter-project", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "react-scripts start", | ||
"build": "react-scripts build", | ||
"test": "react-scripts test --env=jsdom", | ||
"eject": "react-scripts eject" | ||
}, | ||
"browserslist": [ | ||
">0.2%", | ||
"not dead", | ||
"not ie <= 11", | ||
"not op_mini all" | ||
], | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"prop-types": "^15.8.1", | ||
"react": "^18.0.0", | ||
"react-dom": "^18.0.0", | ||
"react-icons": "^4.4.0", | ||
"react-router-dom": "^6.4.1", | ||
"react-scripts": "^5.0.1" | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<!DOCTYPE html> | ||
<html lang="id"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<!-- START: Google Font --> | ||
<link rel="preconnect" href="https://fonts.googleapis.com"> | ||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;700&display=swap" rel="stylesheet"> | ||
<!-- END: Google Font --> | ||
<title>Contact Apps</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import React from "react"; | ||
import { Route, Routes } from "react-router-dom"; | ||
import Navigation from "./Navigation"; | ||
import HomePage from "../pages/HomePage"; | ||
import AddPage from "../pages/AddPage"; | ||
import RegisterPage from "../pages/RegisterPage"; | ||
import LoginPage from "../pages/LoginPage"; | ||
import { putAccessToken, getUserLogged } from "../utils/api"; | ||
import { LocaleProvider } from "../contexts/LocaleContext"; | ||
class ContactApp extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
authedUser: null, | ||
initializing: true, | ||
localeContext: { | ||
locale: localStorage.getItem("locale") || "id", | ||
localeToggle: () => { | ||
this.setState((prevState) => { | ||
const newLocale = | ||
prevState.localeContext.locale === "id" | ||
? "en" | ||
: "id"; | ||
localStorage.setItem("locale", newLocale); | ||
return { | ||
localeContext: { | ||
...prevState.localeContext, | ||
// jika locale context sama dengan id maka ubah menjadi eng | ||
locale: newLocale, | ||
}, | ||
}; | ||
}); | ||
}, | ||
}, | ||
}; | ||
this.onLoginSuccess = this.onLoginSuccess.bind(this); | ||
this.onLogout = this.onLogout.bind(this); | ||
} | ||
|
||
async componentDidMount() { | ||
const { data } = await getUserLogged(); | ||
|
||
this.setState(() => { | ||
return { | ||
authedUser: data, | ||
initializing: false, | ||
}; | ||
}); | ||
} | ||
onLogout() { | ||
this.setState(() => { | ||
return { | ||
authedUser: null, | ||
}; | ||
}); | ||
putAccessToken(""); | ||
} | ||
|
||
async onLoginSuccess({ accessToken }) { | ||
putAccessToken(accessToken); | ||
const { data } = await getUserLogged(); | ||
this.setState(() => { | ||
return { | ||
authedUser: data, | ||
}; | ||
}); | ||
} | ||
|
||
render() { | ||
if (this.state.initializing) { | ||
return null; | ||
} | ||
if (this.state.authedUser == null) { | ||
return ( | ||
<LocaleProvider value={this.state.localeContext}> | ||
<div className="contact-app"> | ||
<header className="contact-app__header"> | ||
<h1>Aplikasi Kontak</h1> | ||
</header> | ||
<main> | ||
<Routes> | ||
<Route | ||
path="/*" | ||
element={ | ||
<LoginPage | ||
loginSuccess={this.onLoginSuccess} | ||
/> | ||
} | ||
/> | ||
<Route | ||
path="/register" | ||
element={<RegisterPage />} | ||
/> | ||
</Routes> | ||
</main> | ||
</div> | ||
</LocaleProvider> | ||
); | ||
} | ||
|
||
return ( | ||
<LocaleProvider value={this.state.localeContext}> | ||
<div className="contact-app"> | ||
<header className="contact-app__header"> | ||
<h1> | ||
{this.state.localeContext.locale === "id" | ||
? "Aplikasi kontak" | ||
: "Contact App"} | ||
</h1> | ||
<Navigation | ||
logout={this.onLogout} | ||
name={this.state.authedUser.name} | ||
/> | ||
</header> | ||
<main> | ||
<Routes> | ||
<Route path="/" element={<HomePage />} /> | ||
<Route path="/add" element={<AddPage />} /> | ||
</Routes> | ||
</main> | ||
</div> | ||
</LocaleProvider> | ||
); | ||
} | ||
} | ||
export default ContactApp; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
class ContactInput extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
// inisialisasi state | ||
this.state = { | ||
name: "", | ||
tag: "", | ||
}; | ||
|
||
this.onNameChangeEventHandler = this.onNameChangeEventHandler.bind(this); | ||
this.onTagChangeEventHandler = this.onTagChangeEventHandler.bind(this); | ||
this.onSubmitEventHandler = this.onSubmitEventHandler.bind(this); | ||
} | ||
|
||
onNameChangeEventHandler(event) { | ||
this.setState(() => { | ||
return { | ||
name: event.target.value, | ||
}; | ||
}); | ||
} | ||
|
||
onTagChangeEventHandler(event) { | ||
this.setState(() => { | ||
return { | ||
tag: event.target.value, | ||
}; | ||
}); | ||
} | ||
|
||
onSubmitEventHandler(event) { | ||
event.preventDefault(); | ||
this.props.addContact(this.state); | ||
} | ||
|
||
render() { | ||
return ( | ||
<form className="contact-input" onSubmit={this.onSubmitEventHandler}> | ||
<input type="text" placeholder="Nama" value={this.state.name} onChange={this.onNameChangeEventHandler} /> | ||
<input type="text" placeholder="Tag" value={this.state.tag} onChange={this.onTagChangeEventHandler} /> | ||
<button type="submit">Tambah</button> | ||
</form> | ||
); | ||
} | ||
} | ||
ContactInput.propTypes = { | ||
addContact: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default ContactInput; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from "react"; | ||
import ContactItemBody from "./ContactItemBody"; | ||
import ContactItemImage from "./ContactItemImage"; | ||
import DeleteButton from "./DeleteButton"; | ||
import PropTypes from "prop-types"; | ||
function ContactItem({ imageUrl, name, tag, id, onDelete }) { | ||
return ( | ||
<div className="contact-item"> | ||
<ContactItemImage imageUrl={imageUrl} /> | ||
<ContactItemBody name={name} tag={tag} /> | ||
<DeleteButton id={id} onDelete={onDelete} /> | ||
</div> | ||
); | ||
} | ||
ContactItem.propTypes = { | ||
imageUrl: PropTypes.string.isRequired, | ||
name: PropTypes.string.isRequired, | ||
tag: PropTypes.string.isRequired, | ||
id: PropTypes.number.isRequired, | ||
onDelete: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default ContactItem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
function ContactItemBody({ name, tag }) { | ||
return ( | ||
<div className="contact-item__body"> | ||
<h3 className="contact-item__title">{name}</h3> | ||
<p className="contact-item__username">@{tag}</p> | ||
</div> | ||
); | ||
} | ||
|
||
ContactItemBody.propTypes = { | ||
name: PropTypes.string.isRequired, | ||
tag: PropTypes.string.isRequired, | ||
}; | ||
export default ContactItemBody; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
|
||
function ContactItemImage({ imageUrl }) { | ||
return ( | ||
<div className="contact-item__image"> | ||
<img src={imageUrl} alt="contact avatar" /> | ||
</div> | ||
); | ||
} | ||
ContactItemImage.propTypes = { | ||
imageUrl: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default ContactItemImage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from "react"; | ||
import ContactItem from "./ContactItem"; | ||
import PropTypes from "prop-types"; | ||
function ContactList({ contacts, onDelete }) { | ||
return ( | ||
<div className="contact-list"> | ||
{contacts.map((contact) => ( | ||
<ContactItem key={contact.id} id={contact.id} onDelete={onDelete} {...contact} /> | ||
))} | ||
</div> | ||
); | ||
} | ||
ContactList.propTypes = { | ||
contacts: PropTypes.arrayOf(PropTypes.object).isRequired, | ||
onDelete: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default ContactList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import { FiXCircle } from "react-icons/fi"; | ||
function DeleteButton({ id, onDelete }) { | ||
return ( | ||
<button className="contact-item__delete" onClick={() => onDelete(id)}> | ||
<FiXCircle /> | ||
</button> | ||
); | ||
} | ||
DeleteButton.propTypes = { | ||
id: PropTypes.string.isRequired, | ||
onDelete: PropTypes.func.isRequired, | ||
}; | ||
export default DeleteButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import PropTypes from "prop-types"; | ||
import React, { Component } from "react"; | ||
|
||
class LoginInput extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
email: "", | ||
password: "", | ||
}; | ||
this.onEmailChangeHandler = this.onEmailChangeHandler.bind(this); | ||
this.onPasswordChangeHandler = this.onPasswordChangeHandler.bind(this); | ||
this.onSubmitHandler = this.onSubmitHandler.bind(this); | ||
} | ||
onEmailChangeHandler(e) { | ||
this.setState(() => { | ||
return { | ||
email: e.target.value, | ||
}; | ||
}); | ||
} | ||
onPasswordChangeHandler(e) { | ||
this.setState(() => { | ||
return { | ||
password: e.target.value, | ||
}; | ||
}); | ||
} | ||
onSubmitHandler(e) { | ||
e.preventDefault(); | ||
this.props.login({ | ||
email: this.state.email, | ||
password: this.state.password, | ||
}); | ||
} | ||
|
||
render() { | ||
return ( | ||
<form onSubmit={this.onSubmitHandler} className="login-input"> | ||
<input | ||
type="email" | ||
placeholder="Email" | ||
value={this.state.email} | ||
onChange={this.onEmailChangeHandler} | ||
/> | ||
<input | ||
type="password" | ||
placeholder="Password" | ||
value={this.state.password} | ||
onChange={this.onPasswordChangeHandler} | ||
/> | ||
<button>Masuk</button> | ||
</form> | ||
); | ||
} | ||
} | ||
LoginInput.propTypes = { | ||
login: PropTypes.func.isRequired, | ||
}; | ||
export default LoginInput; |
Oops, something went wrong.