diff --git a/src/components/Object/Object.jsx b/src/components/Object/Object.jsx index 5ab7ad6..5e45bac 100644 --- a/src/components/Object/Object.jsx +++ b/src/components/Object/Object.jsx @@ -1,16 +1,55 @@ -import './Object.scss'; +import "./Object.scss"; +import { useState } from "react"; function Object(props) { - return ( -
- квартира -
-

{props.price}

-

{props.desc}

-

{props.address}

-
+ const [isExpanded, setIsExpanded] = useState(false); + + const addressLimit = 30; + const isAddressTooLong = props.address && props.address.length > addressLimit; + const needsExpansion = isAddressTooLong; + + const truncateAddress = (text) => { + if (!text) return ""; + if (isExpanded) return text; + if (text.length <= addressLimit) return text; + + const truncated = text.substring(0, addressLimit); + const lastSpaceIndex = truncated.lastIndexOf(" "); + if (lastSpaceIndex > 0) { + return truncated.substring(0, lastSpaceIndex) + "..."; + } + return truncated + "..."; + }; + + const toggleExpansion = () => { + setIsExpanded(!isExpanded); + }; + + return ( +
+ квартира +
+
+

{props.price}

+

+ {props.desc} +

+

+ {truncateAddress(props.address)} +

- ); + {needsExpansion && ( + + )} +
+
+ ); } export { Object }; diff --git a/src/components/Object/Object.scss b/src/components/Object/Object.scss index b2deba1..1d64e70 100644 --- a/src/components/Object/Object.scss +++ b/src/components/Object/Object.scss @@ -1,158 +1,281 @@ -@import '../../styles/vars.scss'; +@import "../../styles/vars.scss"; .item__info p { - margin: 0 0 12px 0; + margin: 0 0 12px 0; } .item { - display: flex; - flex-direction: column; - // width: 381px; - // height: 381px; - margin: 0 20px; - border-radius: 15px; - background-color: white; + display: flex; + flex-direction: column; + margin: 0 20px; + border-radius: 15px; + background-color: white; + min-height: 450px; + transition: height 0.3s ease; - @media (max-width: 1300px) { - height: 381px; - } + &--expanded { + height: auto; + min-height: 450px; + } - @media (max-width: 1200px) { - height: 361px; - } + @media (min-width: 1800px) { + min-height: 480px; - @media (max-width: 1100px) { - height: 341px; + &--expanded { + min-height: 480px; } + } - @media (max-width: $laptopWidth) { - height: 321px; - margin: 0 15px; - } + @media (max-width: 1300px) { + min-height: 430px; - @media (max-width: $tabletWidth) { - height: 505px; + &--expanded { + min-height: 430px; } + } - @media (max-width: 740px) { - height: 473px; - } + @media (max-width: 1200px) { + min-height: 410px; - @media (max-width: 690px) { - height: 435px; + &--expanded { + min-height: 410px; } + } - @media (max-width: 635px) { - height: 400px; - } + @media (max-width: 1100px) { + min-height: 390px; - @media (max-width: 570px) { - height: 361px; + &--expanded { + min-height: 390px; } + } - @media (max-width: $mobileWidth) { - height: 381px; - } + @media (max-width: $laptopWidth) { + min-height: 370px; + margin: 0 15px; - @media (max-width: 420px) { - height: 361px; + &--expanded { + min-height: 370px; } + } + + @media (max-width: $tabletWidth) { + min-height: 500px; + + &--expanded { + min-height: 500px; + } + } + + @media (max-width: 740px) { + min-height: 480px; + + &--expanded { + min-height: 480px; + } + } + + @media (max-width: 690px) { + min-height: 460px; + + &--expanded { + min-height: 460px; + } + } + + @media (max-width: 635px) { + min-height: 440px; + + &--expanded { + min-height: 440px; + } + } + + @media (max-width: 570px) { + min-height: 420px; + + &--expanded { + min-height: 420px; + } + } + + @media (max-width: $mobileWidth) { + min-height: 400px; + margin: 0 10px; + + &--expanded { + min-height: 400px; + } + } + + @media (max-width: 420px) { + min-height: 380px; + + &--expanded { + min-height: 380px; + } + } } - .item__image:hover { - text-decoration: none; + text-decoration: none; } .item__image { - border-top-right-radius: 15px; - border-top-left-radius: 15px; - width: 100%; + border-top-right-radius: 15px; + border-top-left-radius: 15px; + width: 100%; + height: 250px; + object-fit: cover; + object-position: center; + display: block; + + @media (max-width: 1300px) { + height: 230px; + } + + @media (max-width: 1200px) { + height: 210px; + } + + @media (max-width: 1100px) { + height: 190px; + } + + @media (max-width: $laptopWidth) { + height: 170px; + } + + @media (max-width: $tabletWidth) { + height: 300px; + } + + @media (max-width: 740px) { + height: 280px; + } + + @media (max-width: 690px) { height: 250px; - object-fit: cover; - object-position: center; - display: block; + } - @media (max-width: 1300px) { - height: 230px; - } + @media (max-width: 635px) { + height: 220px; + } - @media (max-width: 1200px) { - height: 210px; - } + @media (max-width: 570px) { + height: 190px; + } - @media (max-width: 1100px) { - height: 190px; - } + @media (max-width: $mobileWidth) { + height: 210px; + } - @media (max-width: $laptopWidth) { - height: 170px; - } - - @media (max-width: $tabletWidth) { - height: 300px; - } - - @media (max-width: 740px) { - height: 280px; - } - - @media (max-width: 690px) { - height: 250px; - } - - @media (max-width: 635px) { - height: 220px; - } - - @media (max-width: 570px) { - height: 190px; - } - - @media (max-width: $mobileWidth) { - height: 210px; - } - - @media (max-width: 420px) { - height: 190px; - } + @media (max-width: 420px) { + height: 190px; + } } .item__info { - margin: 25px 0 13px 25px; + margin: 20px 20px 15px 20px; + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 0; + + @media (max-width: $mobileWidth) { + margin: 15px 15px 10px 15px; + } +} + +.item__content { + flex: 1; + display: flex; + flex-direction: column; } .item__price { - font-size: 25px; + font-size: 25px; - @media (min-width: 1800px) { - font-size: 29px; - } + @media (min-width: 1800px) { + font-size: 29px; + } - @media (min-width: 768.98px) and (max-width: $laptopWidth) { - font-size: 23px; - } + @media (min-width: 768.98px) and (max-width: $laptopWidth) { + font-size: 23px; + } } .item__desc { - font-size: 20px; + font-size: 20px; + line-height: 1.4; + margin-bottom: 8px !important; + word-wrap: break-word; + hyphens: auto; - @media (min-width: 1800px) { - font-size: 24px; - } + @media (min-width: 1800px) { + font-size: 24px; + } - @media (min-width: 768.98px) and (max-width: $laptopWidth) { - font-size: 18px; - } + @media (min-width: 768.98px) and (max-width: $laptopWidth) { + font-size: 18px; + } } .item__address { - font-size: 17px; + font-size: 17px; + line-height: 1.3; + word-wrap: break-word; + hyphens: auto; - @media (min-width: 1800px) { - font-size: 21px; - } + @media (min-width: 1800px) { + font-size: 21px; + } - @media (min-width: 768.98px) and (max-width: $laptopWidth) { - font-size: 16px; - } -} \ No newline at end of file + @media (min-width: 768.98px) and (max-width: $laptopWidth) { + font-size: 16px; + } +} + +.item__expand-btn { + background: none; + border: none; + color: #007bff; + font-size: 11px; + font-weight: 500; + cursor: pointer; + padding: 4px 0; + margin-top: auto; + margin-bottom: 5px; + text-align: left; + transition: color 0.2s ease; + font-family: inherit; + flex-shrink: 0; + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &:hover { + color: #0056b3; + text-decoration: underline; + } + + &:focus { + outline: none; + } + + @media (min-width: 1800px) { + font-size: 13px; + padding: 5px 0; + } + + @media (min-width: 768.98px) and (max-width: $laptopWidth) { + font-size: 10px; + padding: 3px 0; + } + + @media (max-width: $mobileWidth) { + font-size: 11px; + margin-bottom: 3px; + } +} diff --git a/src/components/Sliders/SliderObjects.jsx b/src/components/Sliders/SliderObjects.jsx index fc04b00..02dd93f 100644 --- a/src/components/Sliders/SliderObjects.jsx +++ b/src/components/Sliders/SliderObjects.jsx @@ -1,119 +1,124 @@ -import React, { useState, useEffect } from 'react'; -import Slider from 'react-slick'; -import { Link } from 'react-router-dom'; -import { useMediaQuery } from 'react-responsive'; +import React, { useState, useEffect } from "react"; +import Slider from "react-slick"; +import { useMediaQuery } from "react-responsive"; -import 'slick-carousel/slick/slick.css'; -import 'slick-carousel/slick/slick-theme.css'; -import './SliderObjects.scss'; -import { Object } from '../Object/Object'; -import { API_CONFIG } from '../../config/contacts'; +import "slick-carousel/slick/slick.css"; +import "slick-carousel/slick/slick-theme.css"; +import "./SliderObjects.scss"; +import { Object } from "../Object/Object"; +import { API_CONFIG } from "../../config/contacts"; -import objectPicOne from '../../assets/images/apartaments/image-44.jpg'; -import objectPicTwo from '../../assets/images/apartaments/image-45.jpg'; -import objectPicThree from '../../assets/images/apartaments/image-46.jpg'; +import objectPicOne from "../../assets/images/apartaments/image-44.jpg"; +import objectPicTwo from "../../assets/images/apartaments/image-45.jpg"; +import objectPicThree from "../../assets/images/apartaments/image-46.jpg"; const NextArrow = ({ onClick }) => { - return ( -
- ); + return ( +
+ ); }; const PrevArrow = ({ onClick }) => { - return ( -
- ); + return ( +
+ ); }; const SliderComponent = () => { - const [objects, setObjects] = useState([]); - const [loading, setLoading] = useState(true); - - const isMobileResolution = useMediaQuery({ maxWidth: 768 }); - - // Загрузка данных с API - useEffect(() => { - const fetchObjects = async () => { - try { - const response = await fetch(API_CONFIG.getFullURL(API_CONFIG.endpoints.rental), { - method: 'GET', - headers: { - 'accept': '*/*' - } - }); + const [objects, setObjects] = useState([]); + const [loading, setLoading] = useState(true); - if (response.ok) { - const data = await response.json(); - setObjects(data); - } - } catch (error) { - console.error('Ошибка при загрузке объектов:', error); - // В случае ошибки оставляем пустой массив, компонент не сломается - } finally { - setLoading(false); - } - }; + const isMobileResolution = useMediaQuery({ maxWidth: 768 }); - fetchObjects(); - }, []); - - const settings = { - dots: objects.length > (isMobileResolution ? 1 : 3), // Показываем точки если объектов больше чем влезает - infinite: objects.length > (isMobileResolution ? 1 : 3), // Бесконечная прокрутка только если объектов достаточно - speed: 500, - slidesToShow: isMobileResolution ? 1 : 3, - slidesToScroll: isMobileResolution ? 1 : 3, - arrows: true, - nextArrow: , - prevArrow: , + // Загрузка данных с API + useEffect(() => { + const fetchObjects = async () => { + try { + const response = await fetch( + API_CONFIG.getFullURL(API_CONFIG.endpoints.rental), + { + method: "GET", + headers: { + accept: "*/*", + }, + } + ); + + if (response.ok) { + const data = await response.json(); + setObjects(data); + } + } catch (error) { + console.error("Ошибка при загрузке объектов:", error); + + } finally { + setLoading(false); + } }; - // Если загружаем данные, показываем статические объекты как fallback - if (loading || objects.length === 0) { - return ( -
- - - - - - - - - ); - } + fetchObjects(); + }, []); + const settings = { + dots: objects.length > (isMobileResolution ? 1 : 3), + infinite: objects.length > (isMobileResolution ? 1 : 3), + speed: 500, + slidesToShow: isMobileResolution ? 1 : 3, + slidesToScroll: isMobileResolution ? 1 : 3, + arrows: true, + nextArrow: , + prevArrow: , + }; + + + if (loading || objects.length === 0) { return ( -
- - {objects.map((object) => ( - - - - ))} - - +
+ + + + + + ); + } + + return ( +
+ + {objects.map((object) => ( + + ))} + + + ); }; export { SliderComponent }; diff --git a/src/config/contacts.js b/src/config/contacts.js index 6dca52a..432d72e 100644 --- a/src/config/contacts.js +++ b/src/config/contacts.js @@ -15,12 +15,6 @@ export const CONTACTS = { postalCode: "454052", full: "ул. Комаровского, 4А, офис 210, Челябинск, 454052", }, - office: { - street: "Ленина, д. 60 В, оф. 701", - city: "Челябинск", - description: "Вход в офис со двора", - full: "Ленина, д. 60 В, оф. 701, Челябинск", - }, }, coordinates: [55.242355, 61.37697], diff --git a/src/pages/Office/Office.jsx b/src/pages/Office/Office.jsx index 1372afe..c4bee58 100644 --- a/src/pages/Office/Office.jsx +++ b/src/pages/Office/Office.jsx @@ -90,10 +90,8 @@ function Office() {

Адрес: - {CONTACTS.address.office.full} - - {CONTACTS.address.office.description} - + {CONTACTS.address.main.full} + Вход в офис со двора

Телефон: