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.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 (
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}
}
-
-
-
- );
-};
+ return (
+
+ {isLogin ? 'Login' : 'Register'}
+
+
+
+ );
+}
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.
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"
+ }]