Skip to content

Commit

Permalink
homepage ok, store better
Browse files Browse the repository at this point in the history
  • Loading branch information
lewypopescu committed Aug 25, 2024
1 parent 07aad9d commit 31af227
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 35 deletions.
42 changes: 42 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
Expand Down
4 changes: 2 additions & 2 deletions src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const resourcesApi = (resource) => {

const contactsApi = resourcesApi("contacts");

const usersApi = {
const userApi = {
signup: async (data) => {
const response = await api.post("/users/signup", data);
setAuthHeader(response.data.token);
Expand All @@ -38,4 +38,4 @@ const usersApi = {
},
};

export { contactsApi, usersApi };
export { contactsApi, userApi };
84 changes: 61 additions & 23 deletions src/components/App.jsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";

import { Routes, Route, Navigate } from "react-router-dom";
import ContactsForm from "./ContactsForm/ContactsForm";
import ContactsList from "./ContactsList/ContactsList";
import ContactsFilter from "./ContactsFilter/ContactsFilter";

import LoginPage from "./LoginPage";
import RegisterPage from "./RegisterPage";
import PrivateRoute from "./PrivateRoute";
import RestrictedRoute from "./RestrictedRoute";
import HomePage from "./HomePage";
import { updateFilter } from "../redux/contactsSlice";
import { fetchContacts, addContact, deleteContact } from "../redux/operations";

import {
selectContacts,
selectContactsIsLoading,
selectContactsError,
selectFilterContacts,
} from "../redux/selectors";

import { selectIsAuthenticated } from "../redux/selectors";

function App() {
const contacts = useSelector(selectContacts);
const filter = useSelector(selectFilterContacts);
const isLoading = useSelector(selectContactsIsLoading);
const error = useSelector(selectContactsError);

const isAuthenticated = useSelector(selectIsAuthenticated);

const dispatch = useDispatch();

useEffect(() => {
dispatch(fetchContacts());
}, [dispatch]);
if (isAuthenticated) {
dispatch(fetchContacts());
}
}, [dispatch, isAuthenticated]);

const handleAddContact = (newContact) => {
const contactExists = contacts.some(
Expand Down Expand Up @@ -61,25 +70,54 @@ function App() {
<header className="mb-6">
<h1 className="text-4xl font-bold text-blue-700">Phonebook App</h1>
</header>
<section className="bg-white shadow-md rounded-lg p-6 mb-8 w-full max-w-md">
<h2 className="text-2xl font-semibold text-blue-600 mb-4">
Add contact
</h2>
<ContactsForm onSubmit={handleAddContact} />
</section>
<section className="bg-white shadow-md rounded-lg p-6 w-full max-w-md">
<h2 className="text-2xl font-semibold text-blue-600 mb-4">Contacts:</h2>
<ContactsFilter value={filter} onChange={handleChangeFilter} />
{isLoading && <p className="text-blue-500">Loading...</p>}
{error && <p className="text-red-500">Error: {error}</p>}
{contacts.length === 0 && isLoading === false && (
<p className="text-gray-500">No contacts available.</p>
)}
<ContactsList
contacts={filteredContacts}
onDeleteContact={handleDeleteContact}
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/contacts"
element={
<PrivateRoute>
<section className="bg-white shadow-md rounded-lg p-6 mb-8 w-full max-w-md">
<h2 className="text-2xl font-semibold text-blue-600 mb-4">
Add contact
</h2>
<ContactsForm onSubmit={handleAddContact} />
</section>
<section className="bg-white shadow-md rounded-lg p-6 w-full max-w-md">
<h2 className="text-2xl font-semibold text-blue-600 mb-4">
Contacts:
</h2>
<ContactsFilter value={filter} onChange={handleChangeFilter} />
{isLoading && <p className="text-blue-500">Loading...</p>}
{error && <p className="text-red-500">Error: {error}</p>}
{contacts.length === 0 && isLoading === false && (
<p className="text-gray-500">No contacts available.</p>
)}
<ContactsList
contacts={filteredContacts}
onDeleteContact={handleDeleteContact}
/>
</section>
</PrivateRoute>
}
/>
<Route
path="/login"
element={
<RestrictedRoute>
<LoginPage />
</RestrictedRoute>
}
/>
<Route
path="/register"
element={
<RestrictedRoute>
<RegisterPage />
</RestrictedRoute>
}
/>
</section>
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</div>
);
}
Expand Down
25 changes: 25 additions & 0 deletions src/components/HomePage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import { Link } from "react-router-dom";

const HomePage = () => {
return (
<div className="flex flex-col items-center justify-center h-screen bg-blue-100">
<div className="flex space-x-4">
<Link
to="/login"
className="px-4 py-2 bg-blue-500 text-white rounded shadow"
>
Login
</Link>
<Link
to="/register"
className="px-4 py-2 bg-green-500 text-white rounded shadow"
>
Register
</Link>
</div>
</div>
);
};

export default HomePage;
42 changes: 42 additions & 0 deletions src/components/LoginPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { login } from "../redux/operations";

const LoginPage = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const navigate = useNavigate();

const handleSubmit = (event) => {
event.preventDefault();
dispatch(login({ email, password }))
.then(() => navigate("/"))
.catch((error) => console.log(error));
};

return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
/>
</label>
<label>
Password:
<input
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
</label>
<button type="submit">Login</button>
</form>
);
};

export default LoginPage;
10 changes: 10 additions & 0 deletions src/components/PrivateRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { selectIsAuthenticated } from "../redux/selectors";

const PrivateRoute = ({ children }) => {
const isAuthenticated = useSelector(selectIsAuthenticated);
return isAuthenticated ? children : <Navigate to="/login" />;
};

export default PrivateRoute;
54 changes: 54 additions & 0 deletions src/components/RegisterPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { register } from "../redux/operations";
import { useNavigate } from "react-router-dom";

const RegisterPage = () => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const navigate = useNavigate();

const handleSubmit = (event) => {
event.preventDefault();
dispatch(register({ name, email, password }))
.then(() => navigate("/"))
.catch((error) => console.error("Failed to register:", error));
};

return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</label>
<label>
Email:
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</label>
<label>
Password:
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</label>
<button type="submit">Register</button>
</form>
);
};

export default RegisterPage;
10 changes: 10 additions & 0 deletions src/components/RestrictedRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { selectIsAuthenticated } from "../redux/selectors";

const RestrictedRoute = ({ children }) => {
const isAuthenticated = useSelector(selectIsAuthenticated);
return isAuthenticated ? <Navigate to="/" /> : children;
};

export default RestrictedRoute;
10 changes: 5 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";

import "./index.css";

import App from "./components/App";

import { store } from "./redux/store";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<Provider store={store}>
<App />
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
Loading

0 comments on commit 31af227

Please sign in to comment.