Skip to content

Commit

Permalink
Merge pull request #542 from kevinalfito69/main
Browse files Browse the repository at this point in the history
Contact Apps React JS
  • Loading branch information
Ayushparikh-code authored Oct 27, 2024
2 parents 94f3e0f + 976536b commit dcf1f3f
Show file tree
Hide file tree
Showing 28 changed files with 27,732 additions and 0 deletions.
9 changes: 9 additions & 0 deletions contacts-app/README.md
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>
26,671 changes: 26,671 additions & 0 deletions contacts-app/package-lock.json

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions contacts-app/package.json
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"
}
}
Binary file added contacts-app/public/images/arifaizin.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contacts-app/public/images/default.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contacts-app/public/images/dimasmds.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contacts-app/public/images/rfajri27.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions contacts-app/public/index.html
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>
126 changes: 126 additions & 0 deletions contacts-app/src/components/ContactApp.js
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;
53 changes: 53 additions & 0 deletions contacts-app/src/components/ContactInput.js
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;
23 changes: 23 additions & 0 deletions contacts-app/src/components/ContactItem.js
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;
16 changes: 16 additions & 0 deletions contacts-app/src/components/ContactItemBody.js
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;
15 changes: 15 additions & 0 deletions contacts-app/src/components/ContactItemImage.js
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;
18 changes: 18 additions & 0 deletions contacts-app/src/components/ContactList.js
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;
15 changes: 15 additions & 0 deletions contacts-app/src/components/DeleteButton.js
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;
60 changes: 60 additions & 0 deletions contacts-app/src/components/LoginInput.js
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;
Loading

0 comments on commit dcf1f3f

Please sign in to comment.