Лабораторная 5 с отчётом
34
Lab5/src/App.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Container } from 'react-bootstrap';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { CartProvider } from './components/cart/CartContext.jsx';
|
||||
import { AdvertProvider } from './components/advert/CartAdvertContext.jsx';
|
||||
import { UserProvider } from './components/login/users/UserContext.jsx';
|
||||
import Footer from './components/footer/Footer.jsx';
|
||||
import Navigation from './components/navigation/Navigation.jsx';
|
||||
|
||||
const App = ({ routes }) => {
|
||||
return (
|
||||
<>
|
||||
<UserProvider>
|
||||
<CartProvider>
|
||||
<AdvertProvider>
|
||||
<Navigation routes={routes}></Navigation>
|
||||
<Container className='p-2' as='main' fluid>
|
||||
<Outlet />
|
||||
</Container>
|
||||
<Footer />
|
||||
<Toaster position='top-center' reverseOrder={true} />
|
||||
</AdvertProvider>
|
||||
</CartProvider>
|
||||
</UserProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
App.propTypes = {
|
||||
routes: PropTypes.array,
|
||||
};
|
||||
|
||||
export default App;
|
BIN
Lab5/src/assets/200.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Lab5/src/assets/Image1.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
Lab5/src/assets/Image10.jpg
Normal file
After Width: | Height: | Size: 452 KiB |
BIN
Lab5/src/assets/Image11.jpg
Normal file
After Width: | Height: | Size: 655 KiB |
BIN
Lab5/src/assets/Image12.png
Normal file
After Width: | Height: | Size: 377 KiB |
BIN
Lab5/src/assets/Image13.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
Lab5/src/assets/Image2.png
Normal file
After Width: | Height: | Size: 363 KiB |
BIN
Lab5/src/assets/Image3.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
Lab5/src/assets/Image4.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
Lab5/src/assets/Image5.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
Lab5/src/assets/Image6.jpg
Normal file
After Width: | Height: | Size: 179 KiB |
BIN
Lab5/src/assets/Image7.jpg
Normal file
After Width: | Height: | Size: 156 KiB |
BIN
Lab5/src/assets/Image8.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Lab5/src/assets/Image9.jpg
Normal file
After Width: | Height: | Size: 248 KiB |
BIN
Lab5/src/assets/banner1.png
Normal file
After Width: | Height: | Size: 640 KiB |
BIN
Lab5/src/assets/banner2.png
Normal file
After Width: | Height: | Size: 812 KiB |
BIN
Lab5/src/assets/banner3.png
Normal file
After Width: | Height: | Size: 767 KiB |
BIN
Lab5/src/assets/logo.png
Normal file
After Width: | Height: | Size: 12 KiB |
40
Lab5/src/components/api/ApiClient.js
Normal file
@ -0,0 +1,40 @@
|
||||
import axios from 'axios';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export class HttpError extends Error {
|
||||
constructor(message = '') {
|
||||
super(message);
|
||||
this.name = 'HttpError';
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
toast.error(message, { id: 'HttpError' });
|
||||
}
|
||||
}
|
||||
|
||||
function responseHandler(response) {
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
const data = response?.data;
|
||||
if (!data) {
|
||||
throw new HttpError('API Error. No data!');
|
||||
}
|
||||
return data;
|
||||
}
|
||||
throw new HttpError(`API Error! Invalid status code ${response.status}!`);
|
||||
}
|
||||
|
||||
function responseErrorHandler(error) {
|
||||
if (error === null) {
|
||||
throw new Error('Unrecoverable error!! Error is null!');
|
||||
}
|
||||
toast.error(error.message, { id: 'AxiosError' });
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
|
||||
export const ApiClient = axios.create({
|
||||
baseURL: 'http://localhost:8081/',
|
||||
timeout: '3000',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
ApiClient.interceptors.response.use(responseHandler, responseErrorHandler);
|
33
Lab5/src/components/api/ApiService.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { ApiClient } from './ApiClient';
|
||||
|
||||
class ApiService {
|
||||
constructor(url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
async getAll(expand) {
|
||||
return ApiClient.get(`${this.url}${expand || ''}`);
|
||||
}
|
||||
|
||||
async get(id, expand) {
|
||||
return ApiClient.get(`${this.url}/${id}${expand || ''}`);
|
||||
}
|
||||
|
||||
async getUser(id) {
|
||||
return ApiClient.get(`${this.url}?id=${id}`);
|
||||
}
|
||||
|
||||
async create(body) {
|
||||
return ApiClient.post(this.url, body);
|
||||
}
|
||||
|
||||
async update(id, body) {
|
||||
return ApiClient.put(`${this.url}/${id}`, body);
|
||||
}
|
||||
|
||||
async delete(id) {
|
||||
return ApiClient.delete(`${this.url}/${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiService;
|
17
Lab5/src/components/cart/Cart.css
Normal file
@ -0,0 +1,17 @@
|
||||
.cart-image {
|
||||
width: 4.5rem;
|
||||
padding: .25rem;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.cart-text{
|
||||
color: White;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.textlinkcart{
|
||||
color: white;
|
||||
}
|
46
Lab5/src/components/cart/Cart.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { Button, ButtonGroup, Card } from 'react-bootstrap';
|
||||
import { XLg } from 'react-bootstrap-icons';
|
||||
import imgPlaceholder from '../../assets/200.png';
|
||||
import { GetUserId } from '../login/users/UserReducer';
|
||||
import useCart from './CartHook';
|
||||
import './Cart.css';
|
||||
|
||||
const Cart = () => {
|
||||
const {
|
||||
cart,
|
||||
removeFromCart,
|
||||
clearCart,
|
||||
} = useCart();
|
||||
|
||||
const id = GetUserId();
|
||||
console.log(id);
|
||||
|
||||
return (
|
||||
<div className='d-flex flex-column align-items-center'>
|
||||
<div className='mb-2 col-12 col-md-8 col-lg-6 d-flex align-items-center'>
|
||||
<strong className='flex-fill col-3 cart-text'> Избранные объявления </strong>
|
||||
<Button variant='danger' onClick={() => clearCart()}>
|
||||
<XLg /> Очистить
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
cart.map((cartItem) =>
|
||||
<Card key={cartItem.id} className='mb-2 col-12 col-md-8 col-lg-6 w-50'>
|
||||
<Card.Body className='p-2 d-flex flex-column flex-sm-row align-items-center'>
|
||||
<div className='cart-item flex-fill'>
|
||||
<img className='cart-image' src={cartItem.image || imgPlaceholder} alt="Cart Image" />
|
||||
{cartItem.type.name}
|
||||
</div>
|
||||
<ButtonGroup className='mt-2 mt-sm-1' aria-label="Cart counter">
|
||||
<Button variant="primary" onClick={() => removeFromCart(cartItem)}>
|
||||
<XLg />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Card.Body>
|
||||
</Card>)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cart;
|
29
Lab5/src/components/cart/CartContext.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
createContext,
|
||||
useEffect,
|
||||
useReducer,
|
||||
} from 'react';
|
||||
import { cartReducer, loadCart, saveCart } from './CartReducer';
|
||||
|
||||
const CartContext = createContext(null);
|
||||
|
||||
export const CartProvider = ({ children }) => {
|
||||
const [cart, dispatch] = useReducer(cartReducer, [], loadCart);
|
||||
|
||||
useEffect(() => {
|
||||
saveCart(cart || []);
|
||||
}, [cart]);
|
||||
|
||||
return (
|
||||
<CartContext.Provider value={{ cart, dispatch }}>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
CartProvider.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default CartContext;
|
26
Lab5/src/components/cart/CartHook.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { useContext } from 'react';
|
||||
import CartContext from './CartContext.jsx';
|
||||
import { cartAdd, cartClear, cartRemove } from './CartReducer';
|
||||
|
||||
const useCart = () => {
|
||||
const { cart, dispatch } = useContext(CartContext);
|
||||
|
||||
const cartSum = () => {
|
||||
return parseFloat(
|
||||
cart?.reduce((sum, cartItem) => {
|
||||
return sum + (cartItem.price * cartItem.count);
|
||||
}, 0)
|
||||
?? 0,
|
||||
).toFixed(2);
|
||||
};
|
||||
|
||||
return {
|
||||
cart,
|
||||
getCartSum: () => cartSum(),
|
||||
addToCart: (item) => dispatch(cartAdd(item)),
|
||||
removeFromCart: (item) => dispatch(cartRemove(item)),
|
||||
clearCart: () => dispatch(cartClear()),
|
||||
};
|
||||
};
|
||||
|
||||
export default useCart;
|
71
Lab5/src/components/cart/CartReducer.js
Normal file
@ -0,0 +1,71 @@
|
||||
const setCartCount = (cart, item) => {
|
||||
return cart.map((cartItem) => {
|
||||
if (cartItem.id === item.id) {
|
||||
return { ...cartItem, count: cartItem.count };
|
||||
}
|
||||
return cartItem;
|
||||
});
|
||||
};
|
||||
|
||||
const addToCart = (cart, item) => {
|
||||
const existsItem = cart.find((cartItem) => cartItem.id === item.id);
|
||||
if (existsItem !== undefined) {
|
||||
return setCartCount(cart, item, 1);
|
||||
}
|
||||
return [...cart, { ...item, count: 1 }];
|
||||
};
|
||||
|
||||
const removeFromCart = (cart, item) => {
|
||||
const existsItem = cart.find((cartItem) => cartItem.id === item.id);
|
||||
if (existsItem !== undefined && existsItem.count > 1) {
|
||||
return setCartCount(cart, item, -1);
|
||||
}
|
||||
return cart.filter((cartItem) => cartItem.id !== item.id);
|
||||
};
|
||||
|
||||
const CART_KEY = 'localCart';
|
||||
const CART_ADD = 'cart/add';
|
||||
const CART_REMOVE = 'cart/remove';
|
||||
const CART_CLEAR = 'cart/clear';
|
||||
|
||||
export const saveCart = (cart) => {
|
||||
localStorage.setItem(CART_KEY, JSON.stringify(cart));
|
||||
};
|
||||
|
||||
export const loadCart = (initialValue = []) => {
|
||||
const cartData = localStorage.getItem(CART_KEY);
|
||||
if (cartData) {
|
||||
return JSON.parse(cartData);
|
||||
}
|
||||
return initialValue;
|
||||
};
|
||||
|
||||
export const cartReducer = (cart, action) => {
|
||||
const { item } = action;
|
||||
switch (action.type) {
|
||||
case CART_ADD: {
|
||||
return addToCart(cart, item);
|
||||
}
|
||||
case CART_REMOVE: {
|
||||
return removeFromCart(cart, item);
|
||||
}
|
||||
case CART_CLEAR: {
|
||||
return [];
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unknown action: ${action.type}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const cartAdd = (item) => ({
|
||||
value: CART_ADD, item,
|
||||
});
|
||||
|
||||
export const cartRemove = (item) => ({
|
||||
type: CART_REMOVE, item,
|
||||
});
|
||||
|
||||
export const cartClear = () => ({
|
||||
value: CART_CLEAR,
|
||||
});
|
23
Lab5/src/components/input/Input.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form } from 'react-bootstrap';
|
||||
|
||||
const Input = ({
|
||||
name, label, value, onChange, className, ...rest
|
||||
}) => {
|
||||
return (
|
||||
<Form.Group className={`mb-2 ${className || ''}`} controlId={name}>
|
||||
<Form.Label>{label}</Form.Label>
|
||||
<Form.Control name={name || ''} value={value || ''} onChange={onChange} {...rest} />
|
||||
</Form.Group>
|
||||
);
|
||||
};
|
||||
|
||||
Input.propTypes = {
|
||||
name: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Input;
|
3
Lab5/src/components/input/Select.css
Normal file
@ -0,0 +1,3 @@
|
||||
.form1{
|
||||
width: 16rem;
|
||||
}
|
30
Lab5/src/components/input/Select.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form } from 'react-bootstrap';
|
||||
import './Select.css';
|
||||
|
||||
const Select = ({
|
||||
values, name, label, value, onChange, className, ...rest
|
||||
}) => {
|
||||
return (
|
||||
<Form.Group className={`form1 mb-2 ${className || ''}`} controlId={name}>
|
||||
<Form.Label className='form-label'>{label}</Form.Label>
|
||||
<Form.Select name={name || ''} value={value || ''} onChange={onChange} {...rest}>
|
||||
<option value=''> Все квартиры </option>
|
||||
{
|
||||
values.map((type) => <option key={type.id} value={type.id}>{type.name}</option>)
|
||||
}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
);
|
||||
};
|
||||
|
||||
Select.propTypes = {
|
||||
values: PropTypes.array,
|
||||
name: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Select;
|
49
Lab5/src/components/lines/form/LinesForm.jsx
Normal file
@ -0,0 +1,49 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useLinesItemForm from '../hooks/LinesItemFormHook';
|
||||
import LinesItemForm from './LinesItemForm.jsx';
|
||||
import './LinesItemForm.css';
|
||||
|
||||
const LinesForm = ({ id }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
item,
|
||||
validated,
|
||||
handleSubmit,
|
||||
handleChange,
|
||||
} = useLinesItemForm(id);
|
||||
|
||||
const onBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
const onSubmit = async (event) => {
|
||||
if (await handleSubmit(event)) {
|
||||
onBack();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form className='m-0 p-2' noValidate validated={validated} onSubmit={onSubmit}>
|
||||
<LinesItemForm item={item} handleChange={handleChange} />
|
||||
<Form.Group className='row justify-content-center m-0 mt-3'>
|
||||
<Button className='col-5 col-lg-2 m-0 me-2' variant='secondary' onClick={() => onBack()}>
|
||||
Назад
|
||||
</Button>
|
||||
<Button className='col-5 col-lg-2 m-0 ms-2' type='submit' variant='primary'>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
LinesForm.propTypes = {
|
||||
id: PropTypes.string,
|
||||
};
|
||||
|
||||
export default LinesForm;
|
3
Lab5/src/components/lines/form/LinesItemForm.css
Normal file
@ -0,0 +1,3 @@
|
||||
#image-preview {
|
||||
width: 200px;
|
||||
}
|
30
Lab5/src/components/lines/form/LinesItemForm.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import imgPlaceholder from '../../../assets/200.png';
|
||||
import Input from '../../input/Input.jsx';
|
||||
import Select from '../../input/Select.jsx';
|
||||
import useTypes from '../../types/hooks/TypesHook';
|
||||
import './LinesItemForm.css';
|
||||
|
||||
const LinesItemForm = ({ item, handleChange }) => {
|
||||
const { types } = useTypes();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='text-center'>
|
||||
<img id='image-preview' className='rounded' alt='placeholder'
|
||||
src={item.image || imgPlaceholder} />
|
||||
</div>
|
||||
<Select values={types} name='typeId' label='Квартиры' value={item.typeId} onChange={handleChange} required />
|
||||
<Input name='price' label='Цена' value={item.price} onChange={handleChange} type='number' min='1000.0' step='0.50' required />
|
||||
<Input name='description' label='Текст' value={item.description} onChange={handleChange} type='text' required />
|
||||
<Input name='image' label='Изображение' onChange={handleChange} type='file' accept='image/*' />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
LinesItemForm.propTypes = {
|
||||
item: PropTypes.object,
|
||||
handleChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default LinesItemForm;
|
11
Lab5/src/index.css
Normal file
@ -0,0 +1,11 @@
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.1em;
|
||||
}
|
85
Lab5/src/main.jsx
Normal file
@ -0,0 +1,85 @@
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
|
||||
import App from './App.jsx';
|
||||
import './index.css';
|
||||
import ErrorPage from './pages/ErrorPage.jsx';
|
||||
import PageMain from './pages/PageMain.jsx';
|
||||
import Page1 from './pages/Page1.jsx';
|
||||
import Page2 from './pages/Page2.jsx';
|
||||
import Page3 from './pages/Page3.jsx';
|
||||
import Page4 from './pages/Page4.jsx';
|
||||
import Page5 from './pages/Page5.jsx';
|
||||
import PageMyBulletin from './pages/PageMyBulletin.jsx';
|
||||
import PageEdit from './pages/PageEdit.jsx';
|
||||
import LogIn from './pages/LogIn.jsx';
|
||||
import SignIn from './pages/SignIn.jsx';
|
||||
import OrderPage from './pages/OrderPage.jsx';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
index: true,
|
||||
path: '/',
|
||||
element: <PageMain />,
|
||||
},
|
||||
{
|
||||
path: '/pageMyBulletin',
|
||||
element: <PageMyBulletin />,
|
||||
title: 'Обзор',
|
||||
},
|
||||
{
|
||||
path: '/page2',
|
||||
element: <Page2 />,
|
||||
title: 'О нас',
|
||||
},
|
||||
{
|
||||
path: '/page3',
|
||||
element: <Page3 />,
|
||||
title: 'Избранное',
|
||||
},
|
||||
{
|
||||
path: '/page4',
|
||||
element: <Page4 />,
|
||||
title: 'Контакты',
|
||||
},
|
||||
{
|
||||
path: '/page5',
|
||||
element: <Page5 />,
|
||||
},
|
||||
{
|
||||
path: '/page1',
|
||||
element: <Page1 />,
|
||||
},
|
||||
{
|
||||
path: '/page-edit/:id?',
|
||||
element: <PageEdit />,
|
||||
},
|
||||
{
|
||||
path: '/logIn',
|
||||
element: <LogIn />,
|
||||
},
|
||||
{
|
||||
path: '/signIn',
|
||||
element: <SignIn />,
|
||||
},
|
||||
{
|
||||
path: '/orderPage',
|
||||
element: <OrderPage />,
|
||||
},
|
||||
];
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <App routes={routes} />,
|
||||
children: routes,
|
||||
errorElement: <ErrorPage />,
|
||||
},
|
||||
]);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</React.StrictMode>,
|
||||
);
|
5
Lab5/src/pages/ErrorPage.css
Normal file
@ -0,0 +1,5 @@
|
||||
.back{
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: red;
|
||||
}
|
20
Lab5/src/pages/ErrorPage.jsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { Alert, Button, Container } from 'react-bootstrap';
|
||||
import { useNavigate, useRouteError } from 'react-router-dom';
|
||||
|
||||
const ErrorPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const error = useRouteError();
|
||||
|
||||
return (
|
||||
<Container fluid className='m-0 p-3 row justify-content-center'>
|
||||
<Alert className='col-12 col-lg-6' variant='danger'>
|
||||
{error?.message ?? 'Страница не найдена'}
|
||||
</Alert>
|
||||
<div className='w-100'></div>
|
||||
<Button className='col-12 col-lg-6' variant='primary'
|
||||
onClick={() => navigate(-1)}> Назад </Button>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorPage;
|
21
Lab5/src/pages/LogIn.css
Normal file
@ -0,0 +1,21 @@
|
||||
.TitleLogin{
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 42px;
|
||||
}
|
||||
|
||||
.btnLog{
|
||||
color: red;
|
||||
}
|
||||
|
||||
@media (max-width: 550px) {
|
||||
.imgg1{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.col-7{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
37
Lab5/src/pages/LogIn.jsx
Normal file
@ -0,0 +1,37 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Container, Row } from 'react-bootstrap';
|
||||
import LoginForm from '../components/login/form/LoginForm.jsx';
|
||||
import Image12 from '../assets/Image12.png';
|
||||
import './LogIn.css';
|
||||
|
||||
const LogIn = () => {
|
||||
return (
|
||||
<Container fluid className='container-fluid wrapper'>
|
||||
<div className="TitleLogin">
|
||||
<p>Войти в профиль</p>
|
||||
</div>
|
||||
|
||||
<Row>
|
||||
<div className='col-7'>
|
||||
<div className="row justify-content-center">
|
||||
<LoginForm />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col">
|
||||
<div className="col-md-10 offset">
|
||||
<img className="imgg1 w-100 h-100" src={Image12} alt="banner" />
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
LogIn.propTypes = {
|
||||
index: PropTypes.number,
|
||||
line: PropTypes.object,
|
||||
};
|
||||
|
||||
export default LogIn;
|
12
Lab5/src/pages/OrderPage.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import LinesFormOrder from '../components/lines/formOrder/LinesFormOrder.jsx';
|
||||
|
||||
const OrderPage = () => {
|
||||
const { id } = useParams();
|
||||
|
||||
return (
|
||||
<LinesFormOrder id={id} />
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderPage;
|
44
Lab5/src/pages/Page1.css
Normal file
@ -0,0 +1,44 @@
|
||||
body{
|
||||
background-color: #85DADA;
|
||||
}
|
||||
|
||||
.card-title{
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.textlinkBut{
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.textlinkButMy{
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card{
|
||||
width: 18rem;
|
||||
/* margin:0 !important; */
|
||||
padding:0 !important;
|
||||
}
|
||||
|
||||
.btn1{
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
|
||||
.btn11{
|
||||
background-color: #0d6efd;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.btn2{
|
||||
color: red;
|
||||
width: 30rem;
|
||||
}
|
||||
|
||||
@media (max-width: 325px) {
|
||||
.card{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
12
Lab5/src/pages/Page1.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import Lines from '../components/lines/table/Lines.jsx';
|
||||
import './Page1.css';
|
||||
|
||||
const Page1 = () => {
|
||||
return (
|
||||
<>
|
||||
<Lines />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page1;
|
27
Lab5/src/pages/Page2.css
Normal file
@ -0,0 +1,27 @@
|
||||
body{
|
||||
background-color: #85DADA;
|
||||
}
|
||||
|
||||
.card-title{
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card{
|
||||
width: 18rem;
|
||||
}
|
||||
|
||||
.btn1{
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
|
||||
.btn2{
|
||||
color: red;
|
||||
width: 30rem;
|
||||
}
|
||||
|
||||
@media (max-width: 325px) {
|
||||
.card{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
49
Lab5/src/pages/Page2.jsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Container } from 'react-bootstrap';
|
||||
import './Page2.css';
|
||||
|
||||
const Page2 = () => {
|
||||
return (
|
||||
<>
|
||||
<Container fluid className='container-fluid wrapper flex-shrink-0 p-0'>
|
||||
<div className="container">
|
||||
<div className="textus">
|
||||
<p> <u> О портале</u></p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p> <marquee direction="right"> Позвольте нам сэкономить ваше время и деньги </marquee> </p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p>Нужна квартира в Ульяновске? Не хотите перепачивать? Именно на нашем
|
||||
сайте UlRent мы поможем вам найти необходимое жильё. На нашем портале
|
||||
аренды недвижимости. Место, в котором можно снять квартиру посуточно
|
||||
или на долгий срок с максимальной экономией. Почему портал назван
|
||||
независимым? Потому что мы не связаны ни с одним агенством недвижимости
|
||||
Ульяновска, И предлагаем только честные цены на квартиры от реальных
|
||||
собственников. Почему портав назван выгодным и экономным?
|
||||
Статистика показывает, что клиенты, работащие с посредниками переплатили
|
||||
бы на 20%-30%
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p>Для собственников</p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p>Если вы являетесь собственником уютной, удобной и комфортной квартиры,
|
||||
то вы можете разместить своё личное объявление об аренде вашего жилья.
|
||||
Посуточно или на определённый продолжительный срок - выбор за вами.
|
||||
Для вас в качестве арендодателя представляется возможность
|
||||
определять цену и размещать свои контактные данные
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page2;
|
11
Lab5/src/pages/Page3.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import Cart from '../components/cart/Cart.jsx';
|
||||
|
||||
const Page3 = () => {
|
||||
return (
|
||||
<>
|
||||
<Cart/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page3;
|
32
Lab5/src/pages/Page4.css
Normal file
@ -0,0 +1,32 @@
|
||||
.textus{
|
||||
color: white;
|
||||
font-size: 36px
|
||||
}
|
||||
|
||||
.ifram{
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.ifram{
|
||||
height: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
.ifram{
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.ifram{
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.ifram{
|
||||
height: auto;
|
||||
}
|
||||
}
|
46
Lab5/src/pages/Page4.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { Container } from 'react-bootstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Image9 from '../assets/Image9.jpg';
|
||||
import './Page4.css';
|
||||
|
||||
const Page4 = () => {
|
||||
return (
|
||||
<>
|
||||
<Container fluid className="container-fluid wrapper mt-4">
|
||||
<div className="container">
|
||||
|
||||
<Link to="/page3">
|
||||
<img className="img223 w-100" src={Image9} alt="banner" />
|
||||
</Link>
|
||||
|
||||
<div className="textus mt-2">
|
||||
<p> Контактные данные </p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p> +7(909)-985-87-54</p>
|
||||
<p> +7(909)-657-82-94</p>
|
||||
<p> +7(909)-247-47-11</p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p> <i> E-mail </i> </p>
|
||||
</div>
|
||||
|
||||
<div className="textus">
|
||||
<p> Ulrent@mail.ru</p>
|
||||
<p> Ulrent73@yandex.</p>
|
||||
<p> RussianRent@mail.ru</p>
|
||||
</div>
|
||||
|
||||
<div className="div d-flex justify-content-center">
|
||||
<iframe src="https://yandex.ru/map-widget/v1/?um=constructor%3A0643c92cbdf3809080e5dfb2804b473ea00af31cfabe6fee08676c59d8675f01&source=constructor" className="ifram w-75 mb-4"></iframe>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page4;
|
68
Lab5/src/pages/Page5.css
Normal file
@ -0,0 +1,68 @@
|
||||
.slide1{
|
||||
color:aqua
|
||||
}
|
||||
|
||||
.Slide {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.textus1{
|
||||
font-size: 28px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media(max-width: 1200px) {
|
||||
.textus1{
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 1100px) {
|
||||
.textus1{
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 1000px) {
|
||||
.textus1{
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 900px) {
|
||||
.textus1{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 500px) {
|
||||
.textus1{
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.card-img-top1{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 412px) {
|
||||
.textus1{
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.card-img-top1{
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 360px) {
|
||||
.textus1{
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 270px) {
|
||||
.textus1{
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
51
Lab5/src/pages/Page5.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Container } from 'react-bootstrap';
|
||||
import Carousel from 'react-bootstrap/Carousel';
|
||||
import CartAdvert from '../components/advert/CartAdvert.jsx';
|
||||
import Image6 from '../assets/Image6.jpg';
|
||||
import Image7 from '../assets/Image7.jpg';
|
||||
import Image9 from '../assets/Image9.jpg';
|
||||
import './Page5.css';
|
||||
|
||||
const Page5 = () => {
|
||||
return (
|
||||
<>
|
||||
<Container fluid></Container>
|
||||
<article>
|
||||
<div className="row mb-5 mx-auto w-100">
|
||||
<CartAdvert/>
|
||||
</div>
|
||||
|
||||
<div className="row margo me-4 ms-4 mt-5 mb-3 mx-auto">
|
||||
<Carousel data-bs-theme="dark">
|
||||
<Carousel.Item>
|
||||
<img className="d-block w-100" src={Image7} alt="First slide"/>
|
||||
<Carousel.Caption>
|
||||
<div className='slide1'> <h5>First slide label</h5> </div>
|
||||
</Carousel.Caption>
|
||||
</Carousel.Item>
|
||||
<Carousel.Item>
|
||||
<img className="d-block w-100" src={Image9} alt="Second slide"/>
|
||||
<Carousel.Caption>
|
||||
<div className='slide1'> <h5>Second slide label</h5> </div>
|
||||
</Carousel.Caption>
|
||||
</Carousel.Item>
|
||||
<Carousel.Item>
|
||||
<img className="d-block w-100" src={Image6} alt="Third slide"/>
|
||||
<Carousel.Caption>
|
||||
<div className='slide1'> <h5>Third slide label</h5> </div>
|
||||
</Carousel.Caption>
|
||||
</Carousel.Item>
|
||||
</Carousel>
|
||||
</div>
|
||||
</article>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Page5.propTypes = {
|
||||
index: PropTypes.number,
|
||||
line: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Page5;
|
12
Lab5/src/pages/PageEdit.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import LinesForm from '../components/lines/form/LinesForm.jsx';
|
||||
|
||||
const PageEdit = () => {
|
||||
const { id } = useParams();
|
||||
|
||||
return (
|
||||
<LinesForm id={id} />
|
||||
);
|
||||
};
|
||||
|
||||
export default PageEdit;
|
68
Lab5/src/pages/PageMain.css
Normal file
@ -0,0 +1,68 @@
|
||||
body{
|
||||
background-color: #85DADA;
|
||||
}
|
||||
|
||||
.btnmain{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
/* max-width: 100%; */
|
||||
}
|
||||
|
||||
.textlink{
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* .col-8{
|
||||
width: 200px;
|
||||
} */
|
||||
|
||||
.img22{
|
||||
height: 600px;
|
||||
border-radius: 0px 120px;
|
||||
}
|
||||
|
||||
@media(max-width: 600px) {
|
||||
.img22{
|
||||
width: 100px;
|
||||
height:180px;
|
||||
}
|
||||
|
||||
.img22{
|
||||
width: 160px;
|
||||
height: 210px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px){
|
||||
.col1{
|
||||
display: none;
|
||||
}
|
||||
.textlink{
|
||||
font-size: 16px;
|
||||
}
|
||||
.col-8{
|
||||
width: 100%;
|
||||
}
|
||||
.img22{
|
||||
display: table; /*Отмена display: none*/
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px){
|
||||
.col-8{
|
||||
width: 100%;
|
||||
justify-self: center;
|
||||
}
|
||||
.textlink{
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 270px){
|
||||
.img22{
|
||||
display: table; /*Отмена display: none*/
|
||||
}
|
||||
}
|
72
Lab5/src/pages/PageMain.jsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Container, Row, Button } from 'react-bootstrap';
|
||||
import Banner from '../components/banner/Banner.jsx';
|
||||
import Image1 from '../assets/Image2.png';
|
||||
import { GetUserId } from '../components/login/users/UserReducer';
|
||||
import './PageMain.css';
|
||||
|
||||
const PageMain = () => {
|
||||
const ForwardGo = useNavigate();
|
||||
|
||||
const GoSign = () => {
|
||||
ForwardGo('/SignIn');
|
||||
};
|
||||
|
||||
const GoLog = () => {
|
||||
ForwardGo('/LogIn');
|
||||
};
|
||||
|
||||
const id = GetUserId();
|
||||
console.log(id);
|
||||
|
||||
const image = [Image1];
|
||||
return (
|
||||
<>
|
||||
<Container fluid className='container-fluid p-4 wrapper flex-shrink-0 p-0'>
|
||||
<Row>
|
||||
<div className="col-8 pe-5 ps-5">
|
||||
|
||||
<div className="row">
|
||||
<div className="col col1">
|
||||
<Banner />
|
||||
</div>
|
||||
|
||||
<div className="col col1">
|
||||
<Banner />
|
||||
<div className="pt-5"> </div>
|
||||
</div>
|
||||
|
||||
<div className="col col1">
|
||||
<Banner />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="flex-column w-100 mx-auto">
|
||||
<div className="pb-4 pe-4">
|
||||
<Button type="button" onClick={GoLog} className="btnmain btn-default btn-lg w-100 pt-3 pb-3">
|
||||
<Link to="LogIn" className='textlink'>Войти</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="pb-4 pe-4">
|
||||
<Button type="button" onClick={GoSign} className="btnmain btn-default btn-lg w-100 pt-3 pb-3">
|
||||
<Link to="SignIn" className='textlink' target='_blank'>Регистрация</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col">
|
||||
<div className='ms-3' >
|
||||
<img className="img22 w-100 h-100" src={image} alt="banner" />
|
||||
</div >
|
||||
</div>
|
||||
|
||||
</Row>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageMain;
|
5
Lab5/src/pages/PageMyBulletin.css
Normal file
@ -0,0 +1,5 @@
|
||||
.TitleMyBulletin{
|
||||
color: white;
|
||||
font-size: 32px;
|
||||
text-align: center;
|
||||
}
|
12
Lab5/src/pages/PageMyBulletin.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import Lines from '../components/lines/tableuser/Lines.jsx';
|
||||
import './PageMyBulletin.css';
|
||||
|
||||
const pageMyBulletin = () => {
|
||||
return (
|
||||
<>
|
||||
<Lines />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default pageMyBulletin;
|
16
Lab5/src/pages/SignIn.css
Normal file
@ -0,0 +1,16 @@
|
||||
.imgSign {
|
||||
display: block;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.imgSign{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.col-7{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
34
Lab5/src/pages/SignIn.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Container, Row } from 'react-bootstrap';
|
||||
import LinesFormSign from '../components/registration/formSign/LinesFormSign.jsx';
|
||||
import Image5 from '../assets/Image5.png';
|
||||
import './SignIn.css';
|
||||
|
||||
const SignIn = () => {
|
||||
const { id } = useParams();
|
||||
|
||||
return (
|
||||
<Container fluid className='container-fluid wrapper mb-5'>
|
||||
<div className="TitleLogin mb-4">
|
||||
<p> Регистрация </p>
|
||||
</div>
|
||||
|
||||
<Row>
|
||||
<div className='col-7'>
|
||||
<div className="row justify-content-center">
|
||||
<LinesFormSign id={id} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col">
|
||||
<div className="col-md-10 offset">
|
||||
<img className="imgSign" src={Image5} alt="banner" />
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignIn;
|