diff --git a/keynest/package-lock.json b/keynest/package-lock.json index 001bd43..3732e0e 100644 --- a/keynest/package-lock.json +++ b/keynest/package-lock.json @@ -12,11 +12,12 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.9.0", "lorem-ipsum": "^2.0.8", "react": "^19.1.0", "react-dom": "^19.1.0", "react-modal": "^3.16.3", - "react-router-dom": "^7.5.1", + "react-router-dom": "^7.5.3", "react-scripts": "5.0.1", "styled-components": "^6.1.16", "web-vitals": "^2.1.4" @@ -4928,6 +4929,32 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -13726,6 +13753,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -14046,9 +14079,9 @@ } }, "node_modules/react-router": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.1.tgz", - "integrity": "sha512-/jjU3fcYNd2bwz9Q0xt5TwyiyoO8XjSEFXJY4O/lMAlkGTHWuHRAbR9Etik+lSDqMC7A7mz3UlXzgYT6Vl58sA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.3.tgz", + "integrity": "sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -14069,12 +14102,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.1.tgz", - "integrity": "sha512-5DPSPc7ENrt2tlKPq0FtpG80ZbqA9aIKEyqX6hSNJDlol/tr6iqCK4crqdsusmOSSotq6zDsn0y3urX9TuTNmA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.3.tgz", + "integrity": "sha512-cK0jSaTyW4jV9SRKAItMIQfWZ/D6WEZafgHuuCb9g+SjhLolY78qc+De4w/Cz9ybjvLzShAmaIMEXt8iF1Cm+A==", "license": "MIT", "dependencies": { - "react-router": "7.5.1" + "react-router": "7.5.3" }, "engines": { "node": ">=20.0.0" diff --git a/keynest/package.json b/keynest/package.json index f1a5563..5cae299 100644 --- a/keynest/package.json +++ b/keynest/package.json @@ -7,11 +7,12 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.9.0", "lorem-ipsum": "^2.0.8", "react": "^19.1.0", "react-dom": "^19.1.0", "react-modal": "^3.16.3", - "react-router-dom": "^7.5.1", + "react-router-dom": "^7.5.3", "react-scripts": "5.0.1", "styled-components": "^6.1.16", "web-vitals": "^2.1.4" diff --git a/keynest/public/images/apartment1.jpg b/keynest/public/images/apartment1.jpg new file mode 100644 index 0000000..a6bc265 Binary files /dev/null and b/keynest/public/images/apartment1.jpg differ diff --git a/keynest/public/images/apartment2.jpg b/keynest/public/images/apartment2.jpg new file mode 100644 index 0000000..76a440e Binary files /dev/null and b/keynest/public/images/apartment2.jpg differ diff --git a/keynest/public/images/apartment3.jpg b/keynest/public/images/apartment3.jpg new file mode 100644 index 0000000..64fb087 Binary files /dev/null and b/keynest/public/images/apartment3.jpg differ diff --git a/keynest/public/images/apartment4.jpg b/keynest/public/images/apartment4.jpg new file mode 100644 index 0000000..8372b64 Binary files /dev/null and b/keynest/public/images/apartment4.jpg differ diff --git a/keynest/public/images/apartment5.jpg b/keynest/public/images/apartment5.jpg new file mode 100644 index 0000000..54266b5 Binary files /dev/null and b/keynest/public/images/apartment5.jpg differ diff --git a/keynest/public/images/apartment6.jpg b/keynest/public/images/apartment6.jpg new file mode 100644 index 0000000..672975f Binary files /dev/null and b/keynest/public/images/apartment6.jpg differ diff --git a/keynest/src/App.js b/keynest/src/App.js index 5b8a7df..914e709 100644 --- a/keynest/src/App.js +++ b/keynest/src/App.js @@ -8,43 +8,47 @@ import { AuthProvider, useAuth } from './components/AuthContext'; import BookingPage from './components/BookingPage'; import MyRentals from './components/MyRentals'; import Footer from './components/Footer'; +import ApartmentList from './components/ApartmentList'; +import { RentalProvider } from './components/RentalContext'; // Corrected import path function AppContent() { - const { isLoggedIn, logout, user } = useAuth(); + const { isLoggedIn, logout, user } = useAuth(); - return ( -
-
- {isLoggedIn ? ( - <> - - -
- ); + return ( +
+
+ {isLoggedIn ? ( + <> + + +
+ ); } function App() { - return ( - - - - } /> - } /> - } /> - } /> - - - - ); + return ( + + + {/* Added RentalProvider here */} + + } /> + } /> + } /> + } /> + + {/* RentalProvider wraps Routes */} + + + ); } export default App; \ No newline at end of file diff --git a/keynest/src/components/ApartmentList.css b/keynest/src/components/ApartmentList.css new file mode 100644 index 0000000..0198be8 --- /dev/null +++ b/keynest/src/components/ApartmentList.css @@ -0,0 +1,46 @@ +.apartment-list-container { + padding: 20px; +} + +.apartment-grid { + display: grid; + grid-template-columns: repeat(2, minmax(300px, 1fr)); /* Всегда два столбца */ + gap: 20px; + padding-top: 60px; + padding-right: 300px; + padding-left: 300px; +} + +.apartment-card { + border: 1px solid #ddd; + border-radius: 8px; + padding: 15px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.apartment-image { + width: 100%; + height: 200px; + object-fit: contain; /* Изображение отображается полностью, сохраняя пропорции */ + border-radius: 8px; + margin-bottom: 10px; +} + +.apartment-price { + font-weight: bold; + margin-bottom: 10px; + color: #000; +} + +.apartment-card button { + background-color: #242323; + color: white; + padding: 10px 15px; + border: none; + border-radius: 4px; + cursor: pointer; +} + +.apartment-card button:hover { + background-color: #0056b3; +} \ No newline at end of file diff --git a/keynest/src/components/ApartmentList.js b/keynest/src/components/ApartmentList.js new file mode 100644 index 0000000..78a9563 --- /dev/null +++ b/keynest/src/components/ApartmentList.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import './ApartmentList.css'; +import apartments from '../../src/storage/apartments.json'; // Импортируйте JSON-файл + +function ApartmentList() { + const navigate = useNavigate(); + + const handleBookApartment = (apartmentId) => { + navigate(`/booking/${apartmentId}`); + }; + + return ( +
+

Available Apartments

+
+ {apartments.map(apartment => ( +
+ {apartment.name} +

{apartment.name}

+

{apartment.description}

+

{apartment.price}

+ +
+ ))} +
+
+ ); +} + +export default ApartmentList; \ No newline at end of file diff --git a/keynest/src/components/ApartmentRentals.js b/keynest/src/components/ApartmentRentals.js index 1f30566..29f3786 100644 --- a/keynest/src/components/ApartmentRentals.js +++ b/keynest/src/components/ApartmentRentals.js @@ -1,37 +1,29 @@ -import React, { useState } from 'react'; +import React from 'react'; import { useNavigate } from 'react-router-dom'; import './ApartmentRentals.css'; function ApartmentRentals() { - const [loading, setLoading] = useState(false); const navigate = useNavigate(); const handleBookNowClick = () => { - setLoading(true); - // Simulate loading delay - setTimeout(() => { - setLoading(false); - // Redirect to the booking page using navigate - navigate('/booking'); - }, 2000); + navigate('/apartment-list'); // Переход на страницу ApartmentList }; return (
- изображение

Short-term and Long-term Apartment Rentals

Discover the perfect apartment for your needs, whether it's a quick stay or a long-term investment

-
diff --git a/keynest/src/components/AuthContext.js b/keynest/src/components/AuthContext.js index 829dfae..6679982 100644 --- a/keynest/src/components/AuthContext.js +++ b/keynest/src/components/AuthContext.js @@ -1,57 +1,41 @@ -import React, { createContext, useState, useContext } from 'react'; +import React, { createContext, useState, useEffect, useContext } from 'react'; const AuthContext = createContext(); -export const AuthProvider = ({ children }) => { // ONE declaration of AuthProvider - const [isLoggedIn, setIsLoggedIn] = useState(false); - const [user, setUser] = useState(null); +export const AuthProvider = ({ children }) => { + const [user, setUser] = useState(() => { + // При инициализации проверяем, есть ли данные в localStorage + const storedUser = localStorage.getItem('user'); + return storedUser ? JSON.parse(storedUser) : null; + }); - const login = async (credentials) => { - // Mock authentication logic - if (credentials.username === 'user' && credentials.password === 'password') { - setIsLoggedIn(true); - setUser({ username: credentials.username }); - return Promise.resolve(); // Resolve promise on successful login - } else { - setIsLoggedIn(false); - setUser(null); - return Promise.reject(new Error('Invalid credentials')); // Reject promise on invalid credentials - } - }; + useEffect(() => { + // При изменении user сохраняем его в localStorage + if (user) { + localStorage.setItem('user', JSON.stringify(user)); + } else { + // Если user становится null, удаляем данные из localStorage + localStorage.removeItem('user'); + } + }, [user]); - const register = async (credentials) => { - // Mock registration logic - if (credentials.username && credentials.password) { - setIsLoggedIn(true); - setUser({ username: credentials.username }); - return Promise.resolve(); // Resolve promise on successful registration - } else { - setIsLoggedIn(false); - setUser(null); - return Promise.reject(new Error('Registration failed')); // Reject promise on failed registration - } - }; + const login = (userData) => { + setUser(userData); + }; - const logout = () => { - setIsLoggedIn(false); - setUser(null); - }; + const logout = () => { + setUser(null); + }; - const value = { - isLoggedIn, - user, - login, - register, - logout - }; + const isLoggedIn = !!user; - return ( - - {children} - - ); + return ( + + {children} + + ); }; export const useAuth = () => { - return useContext(AuthContext); + return useContext(AuthContext); }; \ No newline at end of file diff --git a/keynest/src/components/AuthModal.css b/keynest/src/components/AuthModal.css new file mode 100644 index 0000000..e4abea4 --- /dev/null +++ b/keynest/src/components/AuthModal.css @@ -0,0 +1,18 @@ +.auth-modal { + position: fixed; /* Или absolute, если нужно позиционировать относительно родителя */ + top: 50%; + left: 50%; + transform: translate(-50%, -50%); /* Центрирование */ + z-index: 1000; /* Убедитесь, что z-index достаточно высок */ + /* Другие стили */ + } + + .auth-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Полупрозрачный фон */ + z-index: 999; /* Меньше, чем z-index модального окна */ + } \ No newline at end of file diff --git a/keynest/src/components/AuthModal.js b/keynest/src/components/AuthModal.js index 5256905..9c87d06 100644 --- a/keynest/src/components/AuthModal.js +++ b/keynest/src/components/AuthModal.js @@ -1,128 +1,105 @@ import React, { useState } from 'react'; import Modal from 'react-modal'; -import { useAuth } from './AuthContext'; -Modal.setAppElement('#root'); +Modal.setAppElement('#root'); -const AuthModal = ({ isOpen, onRequestClose }) => { - const [isLogin, setIsLogin] = useState(true); - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const { login, register } = useAuth(); - const [error, setError] = useState(''); +function AuthModal({ isOpen, onRequestClose, onLogin }) { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [isLogin, setIsLogin] = useState(true); - const handleSubmit = async (e) => { - e.preventDefault(); - setError(''); - try { - if (isLogin) { - await login({ username, password }); - } else { - await register({ username, password }); - } - onRequestClose(); - } catch (err) { - setError(err.message); - } - }; + const modalStyles = { + overlay: { + backgroundColor: 'rgba(0, 0, 0, 0.5)', + zIndex: 1000 + }, + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + padding: '20px', + width: '300px', + borderRadius: '8px', + boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', + border: 'none' + } + }; - const toggleAuthMode = () => { - setIsLogin(!isLogin); - setUsername(''); - setPassword(''); - setError(''); - }; + const formStyles = { + display: 'flex', + flexDirection: 'column', + gap: '15px' + }; - const modalStyles = { - overlay: { - backgroundColor: 'rgba(0, 0, 0, 0.5)' - }, - content: { - top: '50%', - left: '50%', - right: 'auto', - bottom: 'auto', - marginRight: '-50%', - transform: 'translate(-50%, -50%)', - padding: '20px', - width: '300px', - borderRadius: '8px', - boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', - border: 'none' - } - }; + const inputStyles = { + padding: '10px', + borderRadius: '4px', + border: '1px solid #ccc', + fontSize: '16px' + }; - const formStyles = { - display: 'flex', - flexDirection: 'column', - gap: '15px' - }; + const buttonStyles = { + padding: '12px', + borderRadius: '4px', + border: 'none', + backgroundColor: '#242323', + color: 'white', + fontSize: '16px', + cursor: 'pointer', + ':hover': { + backgroundColor: '#0056b3' + } + }; - const inputStyles = { - padding: '10px', - borderRadius: '4px', - border: '1px solid #ccc', - fontSize: '16px' - }; + const toggleButtonStyles = { + background: 'none', + border: 'none', + color: '#007bff', + cursor: 'pointer', + fontSize: '14px', + textDecoration: 'underline' + }; - const buttonStyles = { - padding: '12px', - borderRadius: '4px', - border: 'none', - backgroundColor: '#242323', - color: 'white', - fontSize: '16px', - cursor: 'pointer', - ':hover': { - backgroundColor: '#0056b3' - } - }; + const handleSubmit = (e) => { + e.preventDefault(); + onLogin(username, password); + }; - const toggleButtonStyles = { - background: 'none', - border: 'none', - color: '#007bff', - cursor: 'pointer', - fontSize: '14px', - textDecoration: 'underline' - }; - - return ( - -

{isLogin ? 'Login' : 'Register'}

- {error &&

{error}

} -
-
- - setUsername(e.target.value)} - style={inputStyles} - /> -
-
- - setPassword(e.target.value)} - style={inputStyles} - /> -
- -
- -
- ); -}; + return ( + +

{isLogin ? 'Login' : 'Register'}

+
+ setUsername(e.target.value)} + style={inputStyles} + /> + setPassword(e.target.value)} + style={inputStyles} + /> + +
+ +
+ ); +} export default AuthModal; \ No newline at end of file diff --git a/keynest/src/components/AuthProvider.js b/keynest/src/components/AuthProvider.js new file mode 100644 index 0000000..80011ea --- /dev/null +++ b/keynest/src/components/AuthProvider.js @@ -0,0 +1,104 @@ +import React, { createContext, useState, useEffect, useContext } from 'react'; +// import axios from 'axios'; // Убираем axios +import users from './storage/users.json'; // Импортируем JSON + +const AuthContext = createContext(); + +export const useAuth = () => useContext(AuthContext); + +export const AuthProvider = ({ children }) => { + const [user, setUser] = useState(null); + const [token, setToken] = useState(localStorage.getItem('token') || null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const initializeAuth = async () => { + setLoading(true); + if (token) { + // axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; // Убираем axios + try { + // Замените на реальный запрос к API для получения данных пользователя + // const response = await axios.get('/api/user'); // Убираем axios + // setUser(response.data); // Убираем axios + // Ищем пользователя в JSON файле по токену (username) + const storedUsername = localStorage.getItem('username'); + const foundUser = users.find(u => u.username === storedUsername); + if (foundUser) { + setUser(foundUser); + } else { + logout(); + } + } catch (error) { + console.error('Ошибка при аутентификации:', error); + logout(); // Очищаем токен, если он недействителен + } + } + setLoading(false); + }; + + initializeAuth(); + }, [token]); + + const login = async ({ username, password }) => { + return new Promise((resolve, reject) => { + const foundUser = users.find(u => u.username === username && u.password === password); + if (foundUser) { + const newToken = username; // В качестве токена используем username + localStorage.setItem('token', newToken); + localStorage.setItem('username', username); // Сохраняем username + setToken(newToken); + // axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`; // Убираем axios + setUser(foundUser); + resolve(); + } else { + reject(new Error('Неверный логин или пароль')); + } + }); + }; + + const register = async ({ username, password }) => { + return new Promise((resolve, reject) => { + // Проверяем, существует ли уже пользователь с таким именем + const existingUser = users.find(u => u.username === username); + if (existingUser) { + reject(new Error('Пользователь с таким именем уже существует')); + } else { + // В реальном приложении нужно добавить пользователя в базу данных + // Здесь мы просто эмулируем добавление + const newUser = { + id: users.length + 1, + username, + password + }; + users.push(newUser); + // Автоматически логиним после регистрации + login({ username, password }) + .then(resolve) + .catch(reject); + } + }); + }; + + const logout = () => { + localStorage.removeItem('token'); + localStorage.removeItem('username'); + setToken(null); + setUser(null); + // delete axios.defaults.headers.common['Authorization']; // Убираем axios + }; + + const value = { + user, + token, + login, + register, + logout, + loading, + }; + + return ( + + {!loading && children} + + ); +}; \ No newline at end of file diff --git a/keynest/src/components/BookingPage.css b/keynest/src/components/BookingPage.css index 3b8c3fd..f68f808 100644 --- a/keynest/src/components/BookingPage.css +++ b/keynest/src/components/BookingPage.css @@ -119,4 +119,17 @@ .back-to-home-button:focus { outline: none; box-shadow: 0 0 0 3px rgba(220, 220, 220, 0.5); +} +.apartment-price { + color: #fff; + font-size: 20px; + font-weight: bold; + text-align: center; + margin-top: 20px; +} +.booking-form h2 { + color: #fff; + font-size: 24px; + text-align: center; + margin-bottom: 20px; } \ No newline at end of file diff --git a/keynest/src/components/BookingPage.js b/keynest/src/components/BookingPage.js index f43b533..369a54e 100644 --- a/keynest/src/components/BookingPage.js +++ b/keynest/src/components/BookingPage.js @@ -1,8 +1,13 @@ import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useParams, useNavigate } from 'react-router-dom'; import './BookingPage.css'; +import apartments from '../../src/storage/apartments.json'; +import { useRentalContext } from './RentalContext'; // Import useRentalContext function BookingPage() { + const { apartmentId } = useParams(); + const navigate = useNavigate(); + const apartment = apartments.find(apartment => apartment.id === parseInt(apartmentId)); const [formData, setFormData] = useState({ name: '', email: '', @@ -11,7 +16,7 @@ function BookingPage() { }); const [message, setMessage] = useState(''); const [isSuccess, setIsSuccess] = useState(false); - const navigate = useNavigate(); + const { addRental } = useRentalContext(); // Use addRental from RentalContext const handleChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }); @@ -23,7 +28,7 @@ function BookingPage() { const checkinDate = new Date(formData.checkin); const checkoutDate = new Date(formData.checkout); const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setHours(0, 0, 0, 0); if (checkinDate < today) { setMessage('Check-in date cannot be in the past.'); @@ -38,10 +43,31 @@ function BookingPage() { } try { - await submitForm(formData); + const userId = localStorage.getItem('user_id'); + if (!userId) { + setMessage('Please log in to make a booking.'); + setIsSuccess(false); + return; + } + + const newRental = { + id: Date.now(), + userId: userId, + apartmentId: apartmentId, + name: formData.name, + email: formData.email, + checkin: formData.checkin, + checkout: formData.checkout, + apartmentName: apartment.name, // Include apartment name + apartmentPrice: apartment.price, // Include apartment price + status: 'Pending' + }; + + addRental(newRental); // Add the new rental to the context + setMessage('Booking successful!'); setIsSuccess(true); - setFormData({ name: '', email: '', checkin: '', checkout: '' }); + setFormData({ name: '', email: '', checkin: '', checkout: '' }); } catch (error) { setMessage('Booking failed. Please try again.'); setIsSuccess(false); @@ -52,13 +78,17 @@ function BookingPage() { navigate('/'); }; - const submitForm = (data) => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, 2000); - }); - }; + if (!apartment) { + return ( +
+ +

Apartment Not Found

+

The requested apartment could not be found.

+
+ ); + } return (
@@ -70,6 +100,8 @@ function BookingPage() {

Please fill out the form below to complete your booking.

+

{apartment.name}

+
@@ -90,6 +122,8 @@ function BookingPage() {
+

{apartment.price}

+
diff --git a/keynest/src/components/Header.js b/keynest/src/components/Header.js index 7a7c8ab..fb27be3 100644 --- a/keynest/src/components/Header.js +++ b/keynest/src/components/Header.js @@ -1,14 +1,31 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import './Header.css'; -import AuthModal from './AuthModal'; -import { useAuth } from './AuthContext'; +import AuthModal from './AuthModal.js'; +import { useAuth } from './AuthContext.js'; +import { useRentalContext } from './RentalContext'; // Import useRentalContext function Header() { const [isModalOpen, setIsModalOpen] = useState(false); - const { isLoggedIn, logout } = useAuth(); + const { isLoggedIn, login, logout } = useAuth(); + const [users, setUsers] = useState([]); + const { clearRentals } = useRentalContext(); // Use clearRentals from RentalContext + + useEffect(() => { + fetchUsers(); + }, []); + + const fetchUsers = async () => { + try { + const data = require('../../src/storage/users.json'); + setUsers(data); + } catch (error) { + console.error('Ошибка при загрузке пользователей:', error); + } + }; const openModal = () => { + console.log('openModal called'); setIsModalOpen(true); }; @@ -16,8 +33,25 @@ function Header() { setIsModalOpen(false); }; + const handleLogin = async (username, password) => { + console.log('handleLogin called', username, password); + const user = users.find(user => user.username === username && user.password === password); + + if (user) { + // Успешная аутентификация + login(user); // Сохраняем данные пользователя в AuthContext + localStorage.setItem('user_id', user.user_id.toString()); // Сохраняем user_id в localStorage + closeModal(); + } else { + // Неверные учетные данные + alert('Неверное имя пользователя или пароль'); + } + }; + const handleLogout = () => { logout(); + localStorage.removeItem('user_id'); // Удаляем user_id из localStorage при выходе + clearRentals(); // Clear rentals from context }; return ( @@ -30,7 +64,7 @@ function Header() { {isLoggedIn ? ( @@ -38,7 +72,7 @@ function Header() { ) : ( )} - +
); diff --git a/keynest/src/components/MyRentals.js b/keynest/src/components/MyRentals.js index 052ca02..021f32c 100644 --- a/keynest/src/components/MyRentals.js +++ b/keynest/src/components/MyRentals.js @@ -1,34 +1,36 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; // Import Link +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; import './MyRentals.css'; +import { useRentalContext } from './RentalContext'; +import rentalsData from '../../src/storage/rentals.json'; // Import rentals data function MyRentals() { - const rentals = [ - { - id: 1, - title: 'Cozy Apartment in Downtown', - location: '123 Main St, Cityville', - checkin: '2024-05-01', - checkout: '2024-05-07', - status: 'Confirmed' - }, - { - id: 2, - title: 'Luxury Condo with Ocean View', - location: '456 Ocean Ave, Beach City', - checkin: '2024-06-10', - checkout: '2024-06-17', - status: 'Pending' - }, - { - id: 3, - title: 'Modern Loft in Arts District', - location: '789 Art Lane, Culturetown', - checkin: '2024-07-01', - checkout: '2024-07-08', - status: 'Completed' - } - ]; + const { rentals } = useRentalContext(); + const [rentalsFromJson, setRentalsFromJson] = useState([]); + const currentUserId = localStorage.getItem('user_id') || null; + + useEffect(() => { + // Load rentals from JSON + setRentalsFromJson(rentalsData); + }, []); + + // Filter rentals from JSON for the current user + const userRentalsFromJson = rentalsFromJson.filter(rental => rental.userId === currentUserId); + + // Combine rentals from context and JSON + const combinedRentals = [...rentals.filter(rental => rental.userId === currentUserId), ...userRentalsFromJson]; + + if (!currentUserId) { + return ( +
+ + Back to Home + +

My Rentals

+

Please log in to view your rentals.

+
+ ); + } return (
@@ -37,15 +39,19 @@ function MyRentals() {

My Rentals

- {rentals.map((rental) => ( -
-

{rental.title}

-

Location: {rental.location}

-

Check-in: {rental.checkin}

-

Check-out: {rental.checkout}

-

Status: {rental.status}

-
- ))} + {combinedRentals.length === 0 ? ( +

No rentals found.

+ ) : ( + combinedRentals.map((rental) => ( +
+

{rental.apartmentName || rental.title}

+

Name: {rental.name}

+

Check-in: {rental.checkin}

+

Check-out: {rental.checkout}

+

Status: {rental.status}

+
+ )) + )}
); diff --git a/keynest/src/components/RentalContext.js b/keynest/src/components/RentalContext.js new file mode 100644 index 0000000..49a23a8 --- /dev/null +++ b/keynest/src/components/RentalContext.js @@ -0,0 +1,24 @@ +// RentalContext.js +import React, { createContext, useState, useContext } from 'react'; + +const RentalContext = createContext(); + +export const useRentalContext = () => useContext(RentalContext); + +export const RentalProvider = ({ children }) => { + const [rentals, setRentals] = useState([]); + + const addRental = (newRental) => { + setRentals([...rentals, newRental]); + }; + + const clearRentals = () => { + setRentals([]); + }; + + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/keynest/src/storage/apartments.json b/keynest/src/storage/apartments.json new file mode 100644 index 0000000..8bfa971 --- /dev/null +++ b/keynest/src/storage/apartments.json @@ -0,0 +1,44 @@ +[ + { + "id": 1, + "name": "Cozy Studio Apartment", + "description": "A perfect studio for solo travelers or couples.", + "imageUrl": "/images/apartment1.jpg", + "price": "$80/night" + }, + { + "id": 2, + "name": "Spacious Family Apartment", + "description": "Ideal for families with kids, featuring multiple bedrooms.", + "imageUrl": "/images/apartment2.jpg", + "price": "$150/night" + }, + { + "id": 3, + "name": "Luxury Penthouse", + "description": "Experience the ultimate in luxury living with stunning city views.", + "imageUrl": "/images/apartment3.jpg", + "price": "$300/night" + }, + { + "id": 4, + "name": "Modern Loft Apartment", + "description": "Stylish loft apartment in the heart of the city.", + "imageUrl": "/images/apartment4.jpg", + "price": "$120/night" + }, + { + "id": 5, + "name": "Beachfront Villa", + "description": "Enjoy breathtaking ocean views from this stunning beachfront villa.", + "imageUrl": "/images/apartment5.jpg", + "price": "$500/night" + }, + { + "id": 6, + "name": "Mountain Retreat Cabin", + "description": "Escape to the mountains in this cozy and secluded cabin.", + "imageUrl": "/images/apartment6.jpg", + "price": "$180/night" + } +] \ No newline at end of file diff --git a/keynest/src/storage/rentals.json b/keynest/src/storage/rentals.json new file mode 100644 index 0000000..b0d9ac4 --- /dev/null +++ b/keynest/src/storage/rentals.json @@ -0,0 +1,30 @@ +[ + { + "rentals_id": "1", + "userId": "2", + "title": "Cozy Apartment in Downtown", + "location": "123 Main St, Cityville", + "checkin": "2024-05-01", + "checkout": "2024-05-07", + "status": "Confirmed" + + }, + { + "rentals_id": "2", + "userId": "2", + "title": "Luxury Condo with Ocean View", + "location": "456 Ocean Ave, Beach City", + "checkin": "2024-06-10", + "checkout": "2024-06-17", + "status": "Pending" + }, + { + "rentals_id": "3", + "userId": "3", + "title": "Modern Loft in Arts District", + "location": "789 Art Lane, Culturetown", + "checkin": "2024-07-01", + "checkout": "2024-07-08", + "status": "Completed" + } +] \ No newline at end of file diff --git a/keynest/src/storage/users.json b/keynest/src/storage/users.json new file mode 100644 index 0000000..12631a6 --- /dev/null +++ b/keynest/src/storage/users.json @@ -0,0 +1,15 @@ +[{ + "user_id": "1", + "password":"pass", + "username": "login" + }, + { + "user_id": "2", + "password":"123", + "username": "123" + }, + { + "user_id": "3", + "password":"321", + "username": "321" + }]