diff --git a/pairent_frontend_react/src/API/ApartamentService.js b/pairent_frontend_react/src/API/ApartamentService.js
new file mode 100644
index 0000000..acfbb63
--- /dev/null
+++ b/pairent_frontend_react/src/API/ApartamentService.js
@@ -0,0 +1,18 @@
+import axios from 'axios';
+
+export default class ApartamentService {
+ static async getAll(limit, offest) {
+ const response = await axios.get('http://127.0.0.1:8000/api/apartaments/', {
+ params: {
+ limit: limit,
+ ...(offest !== 0 ? {offset: offest} : {})
+ }
+ })
+ return response;
+ }
+
+ static async getById(id) {
+ const response = await axios.get('http://127.0.0.1:8000/api/apartament/' + id)
+ return response;
+ }
+}
\ No newline at end of file
diff --git a/pairent_frontend_react/src/App.css b/pairent_frontend_react/src/App.css
index 9f3fd83..e4c12d3 100644
--- a/pairent_frontend_react/src/App.css
+++ b/pairent_frontend_react/src/App.css
@@ -24,3 +24,59 @@ main{
height: 1px;
background-color: #CCC;
}
+
+/*Кнопки выбора страницы*/
+.btnSection{
+ display: flex;
+ margin-top: 24px;
+}
+
+/*Кнопка "Показать весь список*/
+.allBtn{
+ width: 208px;
+ height: 32px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border-radius: 12px;
+ font-weight: 500;
+ font-size: 14px;
+}
+
+/*Кнопки переключения страниц*/
+.choiceBtn{
+ display: flex;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/*Кнопки "предыдущая" и "следующая"*/
+.btnPrevious, .btnNext {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 20px;
+ background: #fff;
+ box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.25);
+}
+
+.btnNext{
+ margin-left: 10px;
+}
+
+.pageBtn{
+ width: 36px;
+ height: 36px;
+ margin-left: 10px;
+ background: #D9D9D9;
+ border-radius: 12px;
+ font-weight: 700;
+ font-size: 16px;
+}
+
+.activePage{
+ border: 2px solid #175FA9;
+ background-color: #fff;
+ color: #175FA9;
+}
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/CardSmallApartament/CardSmallApartament.jsx b/pairent_frontend_react/src/components/CardSmallApartament/CardSmallApartament.jsx
new file mode 100644
index 0000000..d6aed4f
--- /dev/null
+++ b/pairent_frontend_react/src/components/CardSmallApartament/CardSmallApartament.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import './styles/CardSmallApartament.css';
+
+const CardSmallApartament = function (props) {
+ return (
+
+
+
{props.results.perimetrs} м², {props.results.rooms}-х комнатная
+
{props.results.price}₽ / мес.
+
{props.results.address}
+
+ );
+}
+
+export default CardSmallApartament;
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/CardSmallApartament/styles/CardSmallApartament.css b/pairent_frontend_react/src/components/CardSmallApartament/styles/CardSmallApartament.css
new file mode 100644
index 0000000..e85c458
--- /dev/null
+++ b/pairent_frontend_react/src/components/CardSmallApartament/styles/CardSmallApartament.css
@@ -0,0 +1,29 @@
+/*Квартира*/
+.viewedElem{
+ width: 24%;
+ padding-bottom: 1%;
+ background: #fff;
+ border: 2px solid #ccc;
+ border-radius: 20px;
+}
+
+.viewedElem img{
+ width: 100%;
+ margin-bottom: 3%;
+}
+
+.viewedElem h3, .viewedElem p{
+ margin-left: 5%;
+}
+
+.viewedElem h3{
+ font-weight: 600;
+ font-size: 24px;
+ color: #222;
+}
+
+.viewedElem p{
+ font-weight: 500;
+ font-size: 16px;
+ color: #222;
+}
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/Favorites/Favorites.jsx b/pairent_frontend_react/src/components/Favorites/Favorites.jsx
index 6b40f60..a2f35f0 100644
--- a/pairent_frontend_react/src/components/Favorites/Favorites.jsx
+++ b/pairent_frontend_react/src/components/Favorites/Favorites.jsx
@@ -1,29 +1,31 @@
-import React, { useEffect, useState } from 'react';
-import CardApartament from '../../components/CardApartament/CardApartament';
-import axios from 'axios'; /* БЛЯ 10 КБ СУКА */
+import React from 'react';
+
+import FavoritesApartamentsList from './FavoritesApartamentsList';
+import LastView from '../../components/LastView/LastView';
+
import './styles/Favorites.css';
const Favorites = function () {
- const [apartaments, setApartaments] = useState([])
-
- async function fetchApartaments() {
- const response = await axios.get('http://127.0.0.1:8000/api/apartaments/')
- setApartaments(response.data.results)
- }
-
- useEffect(() => {
- fetchApartaments()
- })
return(
<>
Избранное
- {apartaments.map((apartament, index) =>
- <>
-
-
- >
- )}
+
+
+
+
+
+
+
>
);
}
diff --git a/pairent_frontend_react/src/components/Favorites/FavoritesApartamentsList.jsx b/pairent_frontend_react/src/components/Favorites/FavoritesApartamentsList.jsx
new file mode 100644
index 0000000..91277a1
--- /dev/null
+++ b/pairent_frontend_react/src/components/Favorites/FavoritesApartamentsList.jsx
@@ -0,0 +1,60 @@
+import React, { useEffect, useState } from 'react';
+import ApartamentService from '../../API/ApartamentService';
+import {useFetching} from '../../hooks/useFetching';
+import {getPageCount} from '../../utils/Pages';
+
+import CardApartament from '../../components/CardApartament/CardApartament';
+import Pagination from '../../components/UI/Pagination/Pagination';
+
+const FavoritesApartamentsList = () => {
+ const [apartaments, setApartaments] = useState([])
+ const [totalPages, setTotalPages] = useState(0);
+ const limit = 1;
+ const [page, setPage] = useState(1);
+
+ const [fetchApartaments, isApartamentsLoading, apartamentsError] = useFetching(async (limit) => {
+ let offset = (page - 1) * limit;
+ const response = await ApartamentService.getAll(limit, offset);
+ setApartaments(response.data.results)
+ const totalCount = response.data.count
+ setTotalPages(getPageCount(totalCount, limit))
+ })
+
+ useEffect(() => {
+ fetchApartaments(limit)
+ }, [page])
+
+ const changePage = (page) => {
+ setPage(page)
+ }
+
+ // TODO: Переделать, сейчас сделанно для MVP (то есть заглушки)
+ if (!apartaments.length && !isApartamentsLoading && !apartamentsError) {
+ return (
+ В избранном ничего нет :/
+ )
+ }
+
+ if (apartamentsError){
+ return(
+ Произошла ошибка ({apartamentsError})
+ )
+ }
+
+ return (
+ <>
+ {isApartamentsLoading &&
+ Идет загрузка...
+ }
+ {apartaments.map((apartament, index) =>
+ <>
+
+
+ >
+ )}
+
+ >
+ );
+};
+
+export default FavoritesApartamentsList;
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/Favorites/styles/Favorites.css b/pairent_frontend_react/src/components/Favorites/styles/Favorites.css
index d4ee7be..df20b4f 100644
--- a/pairent_frontend_react/src/components/Favorites/styles/Favorites.css
+++ b/pairent_frontend_react/src/components/Favorites/styles/Favorites.css
@@ -14,3 +14,54 @@ h2{
margin-left: 34%;
border: 1px solid #ccc;
}
+
+/*Кнопки с вопросами*/
+.questionBtnSection{
+ display: flex;
+ justify-content: space-between;
+ margin-top: 55px;
+}
+
+.questionBtn{
+ width: 323px;
+ height: 128px;
+ padding: 2% 5% 2% 3%;
+ border-radius: 12px;
+ text-align: left;
+}
+
+.questionBtn h3{
+ font-size: 18px;
+ color: #fff;
+}
+
+.questionBtn p{
+ margin-top: 16px;
+ font-size: 16px;
+ line-height: 19px;
+ color: #fff;
+}
+
+.orangeBtn{
+ background: linear-gradient(180deg, #F76D09 0%, #FFA800 100%);
+}
+
+.orangeBtn:active{
+ background: linear-gradient(180deg, #FFA800 0%, #F76D09 100%);
+}
+
+.greenBtn{
+ background: linear-gradient(180deg, #107A34 0%, #51A633 100%);
+}
+
+.greenBtn:active{
+ background: linear-gradient(180deg, #51A633 0%, #107A34 100%);
+}
+
+.blueBtn{
+ background: linear-gradient(180deg, #2E50A7 0%, #0993F7 100%);
+}
+
+.blueBtn:active{
+ background: linear-gradient(180deg, #0993F7 0%, #2E50A7 100%);
+}
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/LastView/LastView.jsx b/pairent_frontend_react/src/components/LastView/LastView.jsx
new file mode 100644
index 0000000..3ea6935
--- /dev/null
+++ b/pairent_frontend_react/src/components/LastView/LastView.jsx
@@ -0,0 +1,51 @@
+import React, { useEffect, useState } from 'react';
+import CardSmallApartament from '../../components/CardSmallApartament/CardSmallApartament';
+import axios from 'axios';
+import './styles/LastView.css';
+
+const LastView = function () {
+ const [apartamentsLastView, setApartamentsLastView] = useState([])
+
+ async function fetchLastApartamentsView() {
+ const response = await axios.get('http://127.0.0.1:8000/api/apartaments/')
+ setApartamentsLastView(response.data.results)
+ }
+
+ useEffect(() => {
+ fetchLastApartamentsView()
+ }, [])
+
+ return(
+ <>
+
+
+
Недавно просмотренные 15
+
+
+
+
+
+
+ {apartamentsLastView.map((apartament, index) =>
+ <>
+
+ >
+ )}
+
+
+ >
+ );
+}
+
+export default LastView;
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/LastView/styles/LastView.css b/pairent_frontend_react/src/components/LastView/styles/LastView.css
new file mode 100644
index 0000000..65d1af6
--- /dev/null
+++ b/pairent_frontend_react/src/components/LastView/styles/LastView.css
@@ -0,0 +1,53 @@
+/*Недавно просмотренные*/
+.viewedSection{
+ margin-top: 46px;
+}
+
+.viewedTittle{
+ display: flex;
+ justify-content: space-between;
+}
+
+.viewedTittle h2{
+ font-weight: 700;
+ font-size: 20px;
+ color: #222;
+}
+
+.viewedTittle span{
+ margin-left: 30px;
+ font-size: 16px;
+ color: #A9A9A9;
+}
+
+/*Кнопки*/
+.viewedBtnSection{
+ display: flex;
+}
+
+/*Кнопка "посмотреть все недавние"*/
+.btnViewed{
+ width: 208px;
+ height: 32px;
+ margin-top: 15px;
+ margin-right: 22px;
+ background: #fff;
+ border: 1px solid #CCCCCC;
+ border-radius: 12px;
+ font-size: 14px;
+ font-weight: 500;
+ color: #ccc;
+}
+
+/*Кнопки "предыдущие" и "следующие"*/
+.viewedBtn{
+ display: flex;
+ align-items: center;
+}
+
+/*Недавно просмотренные квартиры*/
+.viewedBlock{
+ display: flex;
+ justify-content: space-between;
+ margin-top: 27px;
+}
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/UI/Pagination/Pagination.jsx b/pairent_frontend_react/src/components/UI/Pagination/Pagination.jsx
new file mode 100644
index 0000000..b6acba2
--- /dev/null
+++ b/pairent_frontend_react/src/components/UI/Pagination/Pagination.jsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import {getPagesArray} from "../../../utils/Pages";
+
+const Pagination = ({totalPages, page, changePage}) => {
+ let pagesArray = getPagesArray(totalPages);
+ let previousPage = page !== 1 ? page - 1 : page
+ let nextPage = page < pagesArray.length ? page + 1 : page
+
+ return (
+
+
+
+
+ {pagesArray.map(p =>
+
+ )}
+
+
+
+ );
+};
+
+export default Pagination;
\ No newline at end of file
diff --git a/pairent_frontend_react/src/components/UI/Pagination/styles/Pagination.css b/pairent_frontend_react/src/components/UI/Pagination/styles/Pagination.css
new file mode 100644
index 0000000..e69de29
diff --git a/pairent_frontend_react/src/hooks/useFetching.js b/pairent_frontend_react/src/hooks/useFetching.js
new file mode 100644
index 0000000..44c39b2
--- /dev/null
+++ b/pairent_frontend_react/src/hooks/useFetching.js
@@ -0,0 +1,19 @@
+import {useState} from "react";
+
+export const useFetching = (callback) => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState('');
+
+ const fetching = async (...args) => {
+ try {
+ setIsLoading(true)
+ await callback(...args)
+ } catch (e) {
+ setError(e.message);
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return [fetching, isLoading, error]
+}
\ No newline at end of file
diff --git a/pairent_frontend_react/src/utils/Pages.js b/pairent_frontend_react/src/utils/Pages.js
new file mode 100644
index 0000000..f774d0d
--- /dev/null
+++ b/pairent_frontend_react/src/utils/Pages.js
@@ -0,0 +1,11 @@
+export const getPageCount = (totalCount, limit) => {
+ return Math.ceil(totalCount / limit)
+}
+
+export const getPagesArray = (totalPages) => {
+ let result = [];
+ for (let i = 0; i < totalPages; i++) {
+ result.push(i + 1)
+ }
+ return result;
+}
\ No newline at end of file