From 4f26d6fae09804f57b334684c5bf6cdb402a55fc Mon Sep 17 00:00:00 2001 From: aurinex Date: Mon, 7 Jul 2025 23:58:43 +0500 Subject: [PATCH] feedback(adapt on mobile and pc) --- src/components/Feedback.tsx | 182 ++++++++++++--------- src/components/test/LoungePage.tsx | 254 +++++++++++++++++++++++++++++ src/theme/theme.tsx | 15 ++ src/theme/useResponsive.tsx | 8 + 4 files changed, 379 insertions(+), 80 deletions(-) create mode 100644 src/components/test/LoungePage.tsx create mode 100644 src/theme/theme.tsx create mode 100644 src/theme/useResponsive.tsx diff --git a/src/components/Feedback.tsx b/src/components/Feedback.tsx index df87a69..84f704a 100644 --- a/src/components/Feedback.tsx +++ b/src/components/Feedback.tsx @@ -12,11 +12,13 @@ import { FormControl, InputAdornment, InputLabel, - OutlinedInput + OutlinedInput, + Checkbox } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import { IMaskInput } from 'react-imask'; +import { useResponsive } from '../theme/useResponsive'; interface CustomProps { onChange: (event: { target: { name: string; value: string } }) => void; @@ -57,9 +59,14 @@ const Feedback: React.FC = ({ open, onClose }) => { const [budget, setBudget] = useState('до 3 млн'); const [description, setDescription] = useState(''); const [agreeToPolicy, setAgreeToPolicy] = useState(false); + const { isMobile } = useResponsive(); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + if (!agreeToPolicy) { + alert('Для отправки формы необходимо согласиться с политикой конфиденциальности'); + return; + } // логика отправки формы console.log({ name, phone, country, budget, description, agreeToPolicy }); onClose(); @@ -75,22 +82,22 @@ const Feedback: React.FC = ({ open, onClose }) => { }, }, '& .MuiOutlinedInput-root, fieldset': { - borderRadius: '1.5vw', - fontSize: '1.25vw' + borderRadius: isMobile ? '3vw' : '1.5vw', + fontSize: isMobile ? '2.8vw' : '1.25vw' }, '& .MuiInputLabel-root': { - fontSize: '1.25vw', - transform: 'translate(1.5vw, 1.1vw) scale(1)', + fontSize: isMobile ? '2.8vw' : '1.25vw', + transform: isMobile ? 'translate(2.5vw, 3.1vw) scale(1)' : 'translate(1.5vw, 1.1vw) scale(1)', '&.MuiInputLabel-shrink': { - transform: 'translate(1.5vw, -0.6vw) scale(0.75)', + transform: isMobile ? 'translate(3vw, -1.6vw) scale(0.75)' : 'translate(1.5vw, -0.6vw) scale(0.75)', }, '&.Mui-focused': { color: '#c22d1a', } }, '& .MuiInputBase-input': { - fontSize: '1.25vw', - padding: '1vw 1.5vw', + fontSize: isMobile ? '2.8vw' : '1.25vw', + padding: isMobile ? '3vw 2.5vw' : '1vw 1.5vw', }, }; @@ -103,25 +110,17 @@ const Feedback: React.FC = ({ open, onClose }) => { return ( @@ -129,11 +128,12 @@ const Feedback: React.FC = ({ open, onClose }) => { sx={{ position: 'relative', bgcolor: 'background.paper', - maxWidth: '52vw', - borderRadius: '1.5vw', - p: '6vw', + borderRadius: isMobile ? '3vw' : '1.5vw', + p: isMobile ? '6vw' : '6vw', + maxWidth: '70vw', overflow: 'hidden', boxShadow: 24, // тень как у диалога + margin: 'auto', // для дополнительной центровки }} onClick={(e) => e.stopPropagation()} // предотвращаем закрытие при клике на контент > @@ -150,24 +150,24 @@ const Feedback: React.FC = ({ open, onClose }) => { - + Оставьте заявку - + И наш менеджер свяжется с вами для уточнения деталей заказа - + setName(e.target.value)} sx={{ - mb: '1vw', + mb: isMobile ? '3vw' : '1vw', ...textFieldSx, }} /> @@ -178,7 +178,7 @@ const Feedback: React.FC = ({ open, onClose }) => { fullWidth variant="outlined" sx={{ - mb: '1vw', + mb: isMobile ? '3vw' : '1vw', ...textFieldSx, '&:hover .MuiOutlinedInput-notchedOutline': { borderColor: '#c22d1a', @@ -214,8 +214,8 @@ const Feedback: React.FC = ({ open, onClose }) => { /> - - + + Из какой страны привезти автомобиль? @@ -231,13 +231,13 @@ const Feedback: React.FC = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={Европа} - sx={{ marginRight: '1.5vw', ml: '0vw' }} + label={Европа} + sx={{ marginRight: isMobile ? '2.5vw' : '1.5vw', ml: '0vw' }} /> = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={США} - sx={{ marginRight: '1.5vw' }} + label={США} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, - padding: '0.5vw' + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, + padding: isMobile ? '0.5vw' : '0.5vw' }} /> } - label={Китай} - sx={{ marginRight: '1.5vw' }} + label={Китай} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={Корея} + label={Корея} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> - - + + Какой у вас бюджет на автомобиль? @@ -304,13 +305,13 @@ const Feedback: React.FC = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={до 3 млн} - sx={{ marginRight: '1.5vw', ml: '0vw' }} + label={до 3 млн} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={до 5 млн} - sx={{ marginRight: '1.5vw' }} + label={3-5 млн} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={5-10 млн} - sx={{ marginRight: '1.5vw' }} + label={5-10 млн} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> = ({ open, onClose }) => { sx={{ color: '#c22d1a', '&.Mui-checked': { color: '#c22d1a' }, - '& .MuiSvgIcon-root': { fontSize: '1.5vw' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '4.5vw' : '1.5vw' }, padding: '0.5vw' }} /> } - label={более 10 млн} + label={10+ млн} + sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }} /> @@ -368,7 +370,7 @@ const Feedback: React.FC = ({ open, onClose }) => { value={description} onChange={(e) => setDescription(e.target.value)} sx={{ - mb: '1.7vw', + mb: isMobile ? '2.7vw' : '1.7vw', ...textFieldSx }} /> @@ -381,31 +383,51 @@ const Feedback: React.FC = ({ open, onClose }) => { bgcolor: '#c22d1a', color: 'white', py: '0.9vw', - borderRadius: '1vw', - fontSize: '1.25vw', - '&:hover': { bgcolor: '#a42517' } + borderRadius: isMobile ? '3vw' : '1vw', + fontSize: isMobile ? '3.5vw' : '1.25vw', + '&:hover': { bgcolor: '#a42517' }, + textTransform: 'none', }} - endIcon={} + endIcon={} > Отправить - - - Я подтверждаю, что ознакомлен{' '} - - с политикой конфиденциальности - - + + setAgreeToPolicy(e.target.checked)} + sx={{ + color: '#c22d1a', + '&.Mui-checked': { color: '#c22d1a' }, + '& .MuiSvgIcon-root': { fontSize: isMobile ? '5vw' : '2vw' }, + padding: isMobile ? '0.5vw' : '0vw', + marginRight: isMobile ? '0.5vw' : '0.5vw', + }} + /> + } + label={ + + Я подтверждаю, что ознакомлен{' '} + + с политикой конфиденциальности + + + } + /> diff --git a/src/components/test/LoungePage.tsx b/src/components/test/LoungePage.tsx new file mode 100644 index 0000000..e4fb233 --- /dev/null +++ b/src/components/test/LoungePage.tsx @@ -0,0 +1,254 @@ +// src/pages/client/SunbedPage.tsx +import { useState, useEffect } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import Box from "@mui/material/Box"; +import { Typography, CircularProgress } from "@mui/material"; +import { useResponsive } from "../../theme/useResponsive"; +import { motion } from "framer-motion"; +import LocationOnIcon from "@mui/icons-material/LocationOn"; +import { LoungeData, getSunbedById, getSunbedByTitle } from "../../services/api"; + +function LoungePage() { + const { sunbedId, sunbedTitle } = useParams<{ sunbedId: string, sunbedTitle: string }>(); + const navigate = useNavigate(); + const [sunbedData, setSunbedData] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const { isMobile } = useResponsive(); + + // Загрузка данных о шезлонге при монтировании компонента + useEffect(() => { + const fetchSunbedData = async () => { + if (!sunbedTitle) { + setError("ID или Title шезлонга не указан"); + setIsLoading(false); + return; + } + + try { + const data = await getSunbedByTitle(sunbedTitle); + if (data) { + setSunbedData(data); + console.log("Загружены данные шезлонга:", data); + + window.dispatchEvent( + new CustomEvent("sunbedStatusLoaded", { + detail: { status: data.status, id: data.id } + }) + ); + } else { + setError("Шезлонг не найден"); + } + } catch (error) { + console.error("Ошибка при загрузке данных шезлонга:", error); + setError(error instanceof Error ? error.message : "Неизвестная ошибка"); + } finally { + setIsLoading(false); + } + }; + + fetchSunbedData(); + }, [sunbedTitle]); + + return ( + + {!isLoading && sunbedData && sunbedData.status === "booked" && ( + + + Этот шезлонг уже забронирован другим пользователем + + + )} + + + + {isLoading ? ( + + ) : error ? ( + + {error} + + ) : sunbedData ? ( + + + Шезлонг №{sunbedData.number} + + + + {sunbedData.tariff?.name + ? sunbedData.tariff.name.charAt(0).toUpperCase() + + sunbedData.tariff.name.slice(1) + : ""}{" "} + тариф + + + + + + {sunbedData.location + ? `Сектор ${sunbedData.location.sector}, Ряд ${sunbedData.location.row}, Место ${sunbedData.location.column}` + : "Расположение не указано"} + + + + {sunbedData.full_description && ( + + {sunbedData.full_description} + + )} + + {sunbedData.tariff?.base_price && ( + + Цена: {sunbedData.tariff.base_price} руб/час + + )} + + {sunbedData.status && ( + + Статус:{" "} + {sunbedData.status === "free" + ? "Свободен" + : sunbedData.status === "occupied" + ? "Занят" + : sunbedData.status === "booked" + ? "Забронирован" + : "Зарезервирован"} + + )} + + ) : ( + + Информация о шезлонге не найдена + + )} + + + + ); +} + +export default LoungePage; diff --git a/src/theme/theme.tsx b/src/theme/theme.tsx new file mode 100644 index 0000000..d18b889 --- /dev/null +++ b/src/theme/theme.tsx @@ -0,0 +1,15 @@ +import { createTheme } from '@mui/material/styles'; + +const theme = createTheme({ + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 900, + lg: 1200, + xl: 1536, + }, + }, +}); + +export default theme; \ No newline at end of file diff --git a/src/theme/useResponsive.tsx b/src/theme/useResponsive.tsx new file mode 100644 index 0000000..3967c0d --- /dev/null +++ b/src/theme/useResponsive.tsx @@ -0,0 +1,8 @@ +import { useMediaQuery, useTheme } from '@mui/material'; + +export function useResponsive() { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + return { isMobile }; +} \ No newline at end of file