feedback(adapt on mobile and pc)

This commit is contained in:
aurinex
2025-07-07 23:58:43 +05:00
parent eb6cc40f22
commit 4f26d6fae0
4 changed files with 379 additions and 80 deletions

View File

@ -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<FeedbackProps> = ({ 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<FeedbackProps> = ({ 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<FeedbackProps> = ({ open, onClose }) => {
return (
<Box
// sx={{
// position: 'fixed',
// top: 0,
// left: 0,
// width: '100%',
// height: '100%',
// display: open ? 'flex' : 'none',
// alignItems: 'center',
// justifyContent: 'center',
// zIndex: 1300, // высокий z-index как у диалога
// bgcolor: 'rgba(0, 0, 0, 0.5)', // затемнение фона
// }}
sx={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: open ? 'flex' : 'none',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1300, // высокий z-index как у диалога
bgcolor: 'rgba(0, 0, 0, 0.5)', // затемнение фона
}}
onClick={onClose} // закрытие при клике на фон
>
@ -129,11 +128,12 @@ const Feedback: React.FC<FeedbackProps> = ({ 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<FeedbackProps> = ({ open, onClose }) => {
</IconButton>
<Box sx={{ textAlign: 'center', mb: '1vw', mt: '1vw' }}>
<Typography variant="h5" fontWeight="bold" sx={{ fontSize: '1.9vw' }}>
<Typography variant="h5" fontWeight="bold" sx={{ fontSize: isMobile ? '5vw' : '1.9vw' }}>
Оставьте заявку
</Typography>
</Box>
<Box sx={{ mb: '2vw' }}>
<Typography variant="body1" sx={{ textAlign: 'center', fontSize: '1.25vw' }}>
<Typography variant="body1" sx={{ textAlign: 'center', fontSize: isMobile ? '3.5vw' : '1.25vw', maxWidth: '65vw', margin: 'auto' }}>
И наш менеджер свяжется с вами для уточнения деталей заказа
</Typography>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: '1vw' }}>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: isMobile ? '3vw' : '1vw' }}>
<TextField
fullWidth
label="Ваше имя"
value={name}
onChange={(e) => setName(e.target.value)}
sx={{
mb: '1vw',
mb: isMobile ? '3vw' : '1vw',
...textFieldSx,
}}
/>
@ -178,7 +178,7 @@ const Feedback: React.FC<FeedbackProps> = ({ 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<FeedbackProps> = ({ open, onClose }) => {
/>
</FormControl>
<Box sx={{ mb: '1vw' }}>
<Typography variant="body2" sx={{ mb: '0.5vw', fontSize: '1.1vw' }}>
<Box sx={{ mb: isMobile ? '3vw' : '1vw' }}>
<Typography variant="body2" sx={{ mb: '0.5vw', fontSize: isMobile ? '3.5vw' : '1.1vw' }}>
Из какой страны привезти автомобиль?
</Typography>
<FormControl component="fieldset" sx={{ width: '100%' }}>
@ -231,13 +231,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>Европа</Typography>}
sx={{ marginRight: '1.5vw', ml: '0vw' }}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>Европа</Typography>}
sx={{ marginRight: isMobile ? '2.5vw' : '1.5vw', ml: '0vw' }}
/>
<FormControlLabel
value="США"
@ -246,13 +246,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>США</Typography>}
sx={{ marginRight: '1.5vw' }}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>США</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
<FormControlLabel
value="Китай"
@ -261,13 +261,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>Китай</Typography>}
sx={{ marginRight: '1.5vw' }}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>Китай</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
<FormControlLabel
value="Корея"
@ -276,19 +276,20 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>Корея</Typography>}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>Корея</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
</RadioGroup>
</FormControl>
</Box>
<Box sx={{ mb: '1vw' }}>
<Typography variant="body2" sx={{ mb: '0.5vw', fontSize: '1.1vw' }}>
<Box sx={{ mb: isMobile ? '2.5vw' : '1vw' }}>
<Typography variant="body2" sx={{ mb: '0.5vw', fontSize: isMobile ? '3.5vw' : '1.1vw' }}>
Какой у вас бюджет на автомобиль?
</Typography>
<FormControl component="fieldset" sx={{ width: '100%' }}>
@ -304,13 +305,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>до 3 млн</Typography>}
sx={{ marginRight: '1.5vw', ml: '0vw' }}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>до 3 млн</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
<FormControlLabel
value="до 5 млн"
@ -319,13 +320,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>до 5 млн</Typography>}
sx={{ marginRight: '1.5vw' }}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>3-5 млн</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
<FormControlLabel
value="5-10 млн"
@ -334,13 +335,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>5-10 млн</Typography>}
sx={{ marginRight: '1.5vw' }}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>5-10 млн</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
<FormControlLabel
value="более 10 млн"
@ -349,12 +350,13 @@ const Feedback: React.FC<FeedbackProps> = ({ 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={<Typography sx={{ fontSize: '1.1vw' }}>более 10 млн</Typography>}
label={<Typography sx={{ fontSize: isMobile ? '3vw' : '1.1vw' }}>10+ млн</Typography>}
sx={{ marginRight: isMobile ? '1.5vw' : '1.5vw', ml: '0vw' }}
/>
</RadioGroup>
</FormControl>
@ -368,7 +370,7 @@ const Feedback: React.FC<FeedbackProps> = ({ 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<FeedbackProps> = ({ 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={<ArrowForwardIcon sx={{ fontSize: '1.4vw' }} />}
endIcon={<ArrowForwardIcon sx={{ fontSize: isMobile ? '1.4vw' : '3vw' }} />}
>
Отправить
</Button>
<Box sx={{ mt: '1vw', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Typography variant="body2" sx={{ fontSize: '1vw' }}>
Я подтверждаю, что ознакомлен{' '}
<Typography
component="span"
sx={{
color: '#c22d1a',
ml: '0.5vw',
cursor: 'pointer',
textDecoration: 'underline',
fontSize: '1vw'
}}
>
с политикой конфиденциальности
</Typography>
</Typography>
<Box sx={{ mt: isMobile ? '2vw' : '1vw', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<FormControlLabel
sx={{
margin: '0vw'
}}
control={
<Checkbox
checked={agreeToPolicy}
onChange={(e) => 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={
<Typography variant="body2" sx={{ fontSize: isMobile ? '2.5vw' : '1vw' }}>
Я подтверждаю, что ознакомлен{' '}
<Typography
component="span"
sx={{
color: '#c22d1a',
cursor: 'pointer',
textDecoration: 'underline',
fontSize: isMobile ? '2.5vw' : '1vw'
}}
>
с политикой конфиденциальности
</Typography>
</Typography>
}
/>
</Box>
</Box>
</Box>

View File

@ -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<LoungeData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(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 (
<Box
sx={{
display: "flex",
flexDirection: "column",
height: "100%",
bgcolor: "#1d1d1d",
overflow: "hidden",
}}
>
{!isLoading && sunbedData && sunbedData.status === "booked" && (
<Box
sx={{
position: "fixed",
top: "5vw",
left: "50%",
transform: "translateX(-50%)",
bgcolor: "white",
padding: "3vw 6vw",
borderRadius: "5vw",
boxShadow: "2vw",
zIndex: 1000,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Typography variant="body1" sx={{ whiteSpace: "nowrap", fontSize: '3vw' }}>
Этот шезлонг уже забронирован другим пользователем
</Typography>
</Box>
)}
<Box
sx={{
display: "flex",
flexDirection: "column",
flex: 1,
gap: "1vw",
overflow: "hidden",
position: "relative",
}}
>
<motion.div
style={{
flex: 1,
background:
"linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)), url(../image.webp) no-repeat center center",
borderRadius: isMobile ? "3vw" : "1vw",
overflow: "hidden",
padding: isMobile ? "0" : "1vw",
backgroundSize: "cover",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
height: isMobile ? "100%" : "auto",
position: isMobile ? "absolute" : "relative",
width: isMobile ? "100%" : "auto",
zIndex: isMobile ? 10 : 100,
}}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{isLoading ? (
<CircularProgress sx={{ color: "white" }} />
) : error ? (
<Typography
sx={{ color: "white", fontSize: isMobile ? "5vw" : "1.5vw" }}
>
{error}
</Typography>
) : sunbedData ? (
<Box
sx={{
background: "rgba(0, 0, 0, 0)",
padding: "15vw 2vw 2vw 2vw",
borderRadius: "1vw",
maxWidth: "90%",
width: "90%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}}
>
<Typography
variant="h4"
sx={{
color: "white",
fontSize: isMobile ? "9vw" : "2vw",
textAlign: "center",
textWrap: "nowrap",
}}
>
Шезлонг {sunbedData.number}
</Typography>
<Typography
variant="h4"
sx={{
color: "white",
fontSize: isMobile ? "6vw" : "2vw",
textAlign: "center",
mb: "3vw",
}}
>
{sunbedData.tariff?.name
? sunbedData.tariff.name.charAt(0).toUpperCase() +
sunbedData.tariff.name.slice(1)
: ""}{" "}
тариф
</Typography>
<Box
sx={{
display: "flex",
alignItems: "stretch",
justifyContent: "center",
gap: "1vw",
}}
>
<LocationOnIcon
sx={{ color: "white", fontSize: isMobile ? "5vw" : "2vw" }}
/>
<Typography
variant="h4"
sx={{
color: "white",
fontSize: isMobile ? "4.5vw" : "1.2vw",
mb: "2vw",
}}
>
{sunbedData.location
? `Сектор ${sunbedData.location.sector}, Ряд ${sunbedData.location.row}, Место ${sunbedData.location.column}`
: "Расположение не указано"}
</Typography>
</Box>
{sunbedData.full_description && (
<Typography
variant="h4"
sx={{
color: "white",
fontSize: isMobile ? "2.5vw" : "1vw",
textAlign: "center",
mb: "2vw",
}}
>
{sunbedData.full_description}
</Typography>
)}
{sunbedData.tariff?.base_price && (
<Typography
sx={{
color: "white",
fontSize: isMobile ? "4vw" : "1vw",
textAlign: "center",
fontWeight: "bold",
mb: "5vw",
}}
>
Цена: {sunbedData.tariff.base_price} руб/час
</Typography>
)}
{sunbedData.status && (
<Typography
sx={{
color: sunbedData.status === "booked" ? "#ff0000" : "white",
fontSize: isMobile ? "5vw" : "1vw",
textAlign: "center",
fontWeight: "bold",
}}
>
Статус:{" "}
{sunbedData.status === "free"
? "Свободен"
: sunbedData.status === "occupied"
? "Занят"
: sunbedData.status === "booked"
? "Забронирован"
: "Зарезервирован"}
</Typography>
)}
</Box>
) : (
<Typography
sx={{ color: "white", fontSize: isMobile ? "5vw" : "1.5vw" }}
>
Информация о шезлонге не найдена
</Typography>
)}
</motion.div>
</Box>
</Box>
);
}
export default LoungePage;

15
src/theme/theme.tsx Normal file
View File

@ -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;

View File

@ -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 };
}