This commit is contained in:
Kirill
2026-05-24 13:59:14 +05:00
parent 2fe426b70a
commit c2c4099fd7
10 changed files with 853 additions and 189 deletions
+54 -15
View File
@@ -1,4 +1,6 @@
import Box from '@mui/material/Box'
import Container from '@mui/material/Container'
import Divider from '@mui/material/Divider'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import { DeliverySection } from './sections/DeliverySection'
@@ -9,21 +11,58 @@ import { ReturnsSection } from './sections/ReturnsSection'
export function InfoPage() {
return (
<Box>
<Typography variant="h4" gutterBottom>
Информация для покупателей
</Typography>
<Typography color="text.secondary" sx={{ mb: 3 }}>
Как оформить заказ, как проходит доставка, оплата и другие важные детали.
</Typography>
<Container maxWidth="lg" sx={{ py: { xs: 4 } }}>
{/* Hero */}
<Box sx={{ mb: 8 }}>
<Typography
variant="h3"
sx={{
fontWeight: 700,
fontSize: { xs: '2rem', md: '2.75rem' },
letterSpacing: '-0.035em',
lineHeight: 1.1,
mb: 2,
textWrap: 'balance',
}}
>
Информация для покупателей
</Typography>
<Typography
sx={{
fontSize: '1rem',
color: 'text.secondary',
lineHeight: 1.7,
maxWidth: '58ch',
}}
>
Как оформить заказ, как проходит доставка, оплата и другие важные детали.
</Typography>
</Box>
<Stack spacing={3}>
<HowToOrderSection />
<DeliverySection />
<PaymentSection />
<OrderStatusesSection />
<ReturnsSection />
</Stack>
</Box>
{/* Main content grid */}
<Box
sx={{
display: 'grid',
gridTemplateColumns: { xs: '1fr', lg: '1fr 1fr' },
gap: { xs: 6, md: 8 },
}}
>
{/* Left column */}
<Stack spacing={6}>
<HowToOrderSection />
<Divider />
<DeliverySection />
</Stack>
{/* Right column */}
<Stack spacing={6}>
<PaymentSection />
<Divider />
<OrderStatusesSection />
<Divider />
<ReturnsSection />
</Stack>
</Box>
</Container>
)
}
@@ -1,6 +1,5 @@
import Grid from '@mui/material/Grid'
import Box from '@mui/material/Box'
import Link from '@mui/material/Link'
import Paper from '@mui/material/Paper'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import { Package, Store } from 'lucide-react'
@@ -10,57 +9,163 @@ import { PICKUP_ADDRESS_FULL } from '@/shared/constants/pickup-point'
export function DeliverySection() {
return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom>
<Box>
<Typography
variant="h5"
sx={{
fontWeight: 700,
letterSpacing: '-0.03em',
lineHeight: 1.15,
mb: 3,
}}
>
Доставка
</Typography>
<Grid container spacing={2}>
<Grid size={{ xs: 12, sm: 6 }}>
<Paper variant="outlined" sx={{ p: 2, borderRadius: 2, height: '100%' }}>
<Stack spacing={1.5}>
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
<Store size={28} />
<Typography variant="h6">Самовывоз</Typography>
</Stack>
<Typography variant="body2" color="text.secondary">
Бесплатно.
</Typography>
<Typography variant="body2" color="text.secondary">
{PICKUP_ADDRESS_FULL}
</Typography>
<Typography variant="body2" color="text.secondary">
Перед визитом согласуем время чтобы заказ точно был готов к выдаче.
</Typography>
<Link component={RouterLink} to="/about" variant="body2">
Посмотреть на карте
</Link>
</Stack>
</Paper>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<Paper variant="outlined" sx={{ p: 2, borderRadius: 2, height: '100%' }}>
<Stack spacing={1.5}>
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
<Package size={28} />
<Typography variant="h6">Доставка по России</Typography>
</Stack>
<Typography variant="body2" color="text.secondary">
Доступные службы доставки:
</Typography>
<Stack component="ul" spacing={0.5} sx={{ pl: 2, m: 0, listStyle: 'disc' }}>
{DELIVERY_CARRIER_OPTIONS.map((c) => (
<Typography key={c.code} component="li" variant="body2" color="text.secondary">
{c.label}
</Typography>
))}
</Stack>
<Typography variant="body2" color="text.secondary">
Стоимость рассчитывается по тарифу перевозчика. Админ скорректирует цену после оформления заказа.
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{
p: 3,
borderRadius: '14px',
border: '1px solid',
borderColor: 'divider',
}}
>
{/* Pickup */}
<Box sx={{ flex: 1 }}>
<Stack spacing={2}>
<Stack direction="row" spacing={1.5} sx={{ alignItems: 'center' }}>
<Box
sx={{
width: 40,
height: 40,
borderRadius: '10px',
backgroundColor: 'action.hover',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'text.secondary',
}}
>
<Store size={20} />
</Box>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.95rem',
letterSpacing: '-0.01em',
}}
>
Самовывоз
</Typography>
</Stack>
</Paper>
</Grid>
</Grid>
</Paper>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
Бесплатно.
</Typography>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
{PICKUP_ADDRESS_FULL}
</Typography>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
Перед визитом согласуем время чтобы заказ точно был готов к выдаче.
</Typography>
<Link component={RouterLink} to="/about" sx={{ fontSize: '0.8rem', fontWeight: 500, mt: 0.5 }}>
Посмотреть на карте
</Link>
</Stack>
</Box>
<Box
sx={{
width: 1,
display: { xs: 'none', sm: 'block' },
borderLeft: '1px solid',
borderColor: 'divider',
}}
/>
{/* Delivery */}
<Box sx={{ flex: 1 }}>
<Stack spacing={2}>
<Stack direction="row" spacing={1.5} sx={{ alignItems: 'center' }}>
<Box
sx={{
width: 40,
height: 40,
borderRadius: '10px',
backgroundColor: 'action.hover',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'text.secondary',
}}
>
<Package size={20} />
</Box>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.95rem',
letterSpacing: '-0.01em',
}}
>
Доставка по России
</Typography>
</Stack>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
Доступные службы доставки:
</Typography>
<Stack component="ul" spacing={0.75} sx={{ pl: 2, m: 0, listStyle: 'disc' }}>
{DELIVERY_CARRIER_OPTIONS.map((c) => (
<Typography
key={c.code}
component="li"
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.5,
}}
>
{c.label}
</Typography>
))}
</Stack>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
Стоимость рассчитывается по тарифу перевозчика. Админ скорректирует цену после оформления заказа.
</Typography>
</Stack>
</Box>
</Stack>
</Box>
)
}
@@ -1,11 +1,7 @@
import { useState } from 'react'
import Box from '@mui/material/Box'
import Link from '@mui/material/Link'
import Paper from '@mui/material/Paper'
import Step from '@mui/material/Step'
import StepContent from '@mui/material/StepContent'
import StepLabel from '@mui/material/StepLabel'
import Stepper from '@mui/material/Stepper'
import Stack from '@mui/material/Stack'
import Tab from '@mui/material/Tab'
import Tabs from '@mui/material/Tabs'
import Typography from '@mui/material/Typography'
@@ -27,12 +23,12 @@ import { PICKUP_ADDRESS_SHORT } from '@/shared/constants/pickup-point'
const commonSteps = [
{
label: 'Выберите товары',
icon: <ShoppingCart size={20} />,
icon: <ShoppingCart size={18} />,
text: 'Найдите нужные изделия в каталоге и добавьте их в корзину. Вы можете выбрать несколько товаров от разных мастеров — все они соберутся в одном заказе.',
},
{
label: 'Проверьте корзину',
icon: <ClipboardList size={20} />,
icon: <ClipboardList size={18} />,
text: 'Перейдите в корзину и проверьте состав заказа: названия товаров, количество и итоговую сумму. Здесь же можно изменить количество или удалить позиции.',
},
]
@@ -40,22 +36,22 @@ const commonSteps = [
const deliverySteps = [
{
label: 'Укажите адрес доставки и получателя',
icon: <MapPin size={20} />,
icon: <MapPin size={18} />,
text: 'Заполните имя, телефон, email и адрес доставки. Если у вас уже есть сохранённые адреса — выберите из списка или добавьте новый в личном кабинете.',
},
{
label: 'Выберите способ доставки',
icon: <Truck size={20} />,
icon: <Truck size={18} />,
text: 'Доступны: Почта России, Озон ПВЗ, Яндекс ПВЗ, 5Post, WB ПВЗ. После оформления админ рассчитает точную стоимость доставки и скорректирует цену заказа.',
},
{
label: 'Оплатите заказ',
icon: <CreditCard size={20} />,
icon: <CreditCard size={18} />,
text: 'Онлайн-оплата через ЮKassa — банковские карты и СБП. Перенаправление на защищённую платёжную страницу.',
},
{
label: 'Получите заказ',
icon: <PackageOpen size={20} />,
icon: <PackageOpen size={18} />,
text: 'После отправки вы получите трек-номер для отслеживания. Следите за статусом заказа в личном кабинете.',
},
]
@@ -63,33 +59,86 @@ const deliverySteps = [
const pickupSteps = [
{
label: 'Подтвердите заказ',
icon: <CheckCircle size={20} />,
icon: <CheckCircle size={18} />,
text: 'Выберите способ оплаты: онлайн через ЮKassa (карты, СБП) или при получении (наличные / карта).',
},
{
label: 'Согласуйте время получения',
icon: <Clock size={20} />,
icon: <Clock size={18} />,
text: 'Админ свяжется с вами, чтобы договориться об удобном времени выдачи заказа.',
},
{
label: 'Получите заказ',
icon: <Store size={20} />,
icon: <Store size={18} />,
text: `Адрес: ${PICKUP_ADDRESS_SHORT}. Перед визитом согласуем время, чтобы заказ точно был готов.`,
},
]
function StepRow({ step, isLast }: { step: typeof commonSteps[0]; isLast: boolean }) {
return (
<Box sx={{ display: 'flex', gap: 2.5, pb: isLast ? 0 : 2.5, position: 'relative' }}>
<Stack spacing={0} sx={{ alignItems: 'center', flexShrink: 0, width: 20, pt: 0.25 }}>
<Box
sx={{
width: 20,
height: 20,
borderRadius: '6px',
backgroundColor: 'action.hover',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'text.secondary',
flexShrink: 0,
zIndex: 1,
}}
>
{step.icon}
</Box>
{!isLast && (
<Box
sx={{
width: 1.5,
flex: 1,
minHeight: 16,
backgroundColor: 'divider',
borderRadius: 1,
mt: 0.5,
}}
/>
)}
</Stack>
<Box sx={{ flex: 1, pt: 0.1 }}>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.875rem',
letterSpacing: '-0.01em',
mb: 0.5,
}}
>
{step.label}
</Typography>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
{step.text}
</Typography>
</Box>
</Box>
)
}
function BranchStepper({ steps }: { steps: typeof deliverySteps }) {
return (
<Stepper orientation="vertical" activeStep={-1}>
{steps.map((step) => (
<Step key={step.label} completed={false}>
<StepLabel slots={{ stepIcon: () => step.icon }}>{step.label}</StepLabel>
<StepContent>
<Typography color="text.secondary">{step.text}</Typography>
</StepContent>
</Step>
<Stack spacing={0} sx={{ mt: 1 }}>
{steps.map((step, i) => (
<StepRow key={step.label} step={step} isLast={i === steps.length - 1} />
))}
</Stepper>
</Stack>
)
}
@@ -97,34 +146,88 @@ export function HowToOrderSection() {
const [tab, setTab] = useState(0)
return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom>
<Box>
<Typography
variant="h5"
sx={{
fontWeight: 700,
letterSpacing: '-0.03em',
lineHeight: 1.15,
mb: 3,
}}
>
Как оформить заказ
</Typography>
<BranchStepper steps={commonSteps} />
<Box sx={{ mt: 2, mb: 1 }}>
<Tabs value={tab} onChange={(_, v) => setTab(v)} variant="fullWidth">
<Box sx={{ mt: 2.5, mb: 1 }}>
<Tabs
value={tab}
onChange={(_, v) => setTab(v)}
variant="fullWidth"
sx={{
minHeight: 36,
'& .MuiTab-root': {
minHeight: 36,
fontSize: '0.8rem',
fontWeight: 600,
letterSpacing: '-0.01em',
},
'& .MuiTabs-indicator': {
height: 2,
borderRadius: 1,
},
}}
>
<Tab label="Доставка" />
<Tab label="Самовывоз" />
</Tabs>
</Box>
{tab === 0 && <BranchStepper steps={deliverySteps} />}
{tab === 1 && (
<Box>
<BranchStepper steps={pickupSteps} />
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
<Link component={RouterLink} to="/about">
<Typography variant="body2" sx={{ mt: 1.5 }}>
<Link component={RouterLink} to="/about" sx={{ fontSize: '0.8rem', fontWeight: 500 }}>
Посмотреть на карте
</Link>
</Typography>
</Box>
)}
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
<Star size={18} />
<Typography variant="body2" color="text.secondary">
<Box
sx={{
mt: 3,
display: 'flex',
alignItems: 'flex-start',
gap: 1.5,
p: 2,
borderRadius: '10px',
backgroundColor: 'action.hover',
}}
>
<Box
sx={{
color: 'text.secondary',
flexShrink: 0,
mt: 0.1,
}}
>
<Star size={16} />
</Box>
<Typography
variant="body2"
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.6,
}}
>
После получения заказа вы можете оставить отзыв в личном кабинете.
</Typography>
</Box>
</Paper>
</Box>
)
}
@@ -1,47 +1,247 @@
import type { ReactElement } from 'react'
import Chip from '@mui/material/Chip'
import Paper from '@mui/material/Paper'
import Box from '@mui/material/Box'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import { Banknote, CheckCircle, Package, PackageCheck, PackageSearch, Store, XCircle } from 'lucide-react'
import { ORDER_STATUS_DATA, type StatusIconName } from '@/shared/lib/order-status-data'
const iconMap: Record<StatusIconName, ReactElement> = {
banknote: <Banknote size={18} />,
'check-circle': <CheckCircle size={18} />,
'package-search': <PackageSearch size={18} />,
package: <Package size={18} />,
'package-check': <PackageCheck size={18} />,
store: <Store size={18} />,
'x-circle': <XCircle size={18} />,
const iconMap: Record<StatusIconName, React.ReactElement> = {
banknote: (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="2" y="6" width="20" height="12" rx="2" />
<circle cx="12" cy="12" r="3" />
<path d="M6 12h.01M18 12h.01" />
</svg>
),
'check-circle': (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</svg>
),
'package-search': (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="16" cy="12" r="4" />
<path d="M19 19l-3-3" />
<path d="M21 10V7a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 7v3" />
<path d="M3 10l9 5 9-5" />
</svg>
),
package: (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
<line x1="12" y1="22.08" x2="12" y2="12" />
</svg>
),
'package-check': (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
<line x1="12" y1="22.08" x2="12" y2="12" />
<polyline points="9 13 11 15 15 11" />
</svg>
),
store: (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
),
'x-circle': (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="10" />
<path d="M15 9l-6 6M9 9l6 6" />
</svg>
),
}
const colorMap: Record<string, { bg: string; border: string; text: string; dot: string; iconBg: string }> = {
warning: {
bg: 'rgba(245, 124, 0, 0.04)',
border: 'rgba(245, 124, 0, 0.15)',
text: '#c27a00',
dot: '#F57C00',
iconBg: 'rgba(245, 124, 0, 0.1)',
},
success: {
bg: 'rgba(46, 139, 87, 0.04)',
border: 'rgba(46, 139, 87, 0.15)',
text: '#1e6e42',
dot: '#2E8B57',
iconBg: 'rgba(46, 139, 87, 0.1)',
},
info: {
bg: 'rgba(84, 110, 122, 0.04)',
border: 'rgba(84, 110, 122, 0.15)',
text: '#3d5a68',
dot: '#546E7A',
iconBg: 'rgba(84, 110, 122, 0.1)',
},
error: {
bg: 'rgba(211, 47, 47, 0.04)',
border: 'rgba(211, 47, 47, 0.15)',
text: '#a83232',
dot: '#D32F2F',
iconBg: 'rgba(211, 47, 47, 0.1)',
},
}
export function OrderStatusesSection() {
return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom>
Статусы заказа
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
Текущий статус отображается в личном кабинете. Вот что означает каждый из них:
</Typography>
<Stack spacing={2}>
{ORDER_STATUS_DATA.map((s) => (
<Stack key={s.code} direction="row" spacing={2} sx={{ alignItems: 'flex-start' }}>
<Chip
icon={iconMap[s.iconName]}
label={s.label}
color={s.color}
size="small"
variant="outlined"
sx={{ minWidth: 180, flexShrink: 0 }}
/>
<Typography variant="body2" color="text.secondary" sx={{ pt: 0.3 }}>
{s.description}
</Typography>
</Stack>
))}
<Box>
<Box sx={{ mb: 4 }}>
<Typography
variant="h5"
sx={{
fontWeight: 700,
letterSpacing: '-0.03em',
lineHeight: 1.15,
mb: 1,
}}
>
Статусы заказа
</Typography>
<Typography
sx={{
fontSize: '0.875rem',
color: 'text.secondary',
lineHeight: 1.6,
maxWidth: '56ch',
}}
>
Текущий статус отображается в личном кабинете. Каждый этап отражает, что происходит с вашим заказом.
</Typography>
</Box>
<Stack spacing={0} sx={{ position: 'relative' }}>
{ORDER_STATUS_DATA.map((s, index) => {
const colors = colorMap[s.color] ?? colorMap.info
const isLast = index === ORDER_STATUS_DATA.length - 1
return (
<Box
key={s.code}
sx={{
display: 'flex',
gap: 3,
pb: isLast ? 0 : 3,
position: 'relative',
}}
>
{/* Content column */}
<Box sx={{ flex: 1, pt: 0.25 }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1.5,
mb: 0.75,
}}
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 36,
height: 36,
borderRadius: '10px',
backgroundColor: colors.iconBg,
color: colors.text,
flexShrink: 0,
}}
>
{iconMap[s.iconName]}
</Box>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.9rem',
color: colors.text,
letterSpacing: '-0.01em',
}}
>
{s.label}
</Typography>
</Box>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
maxWidth: '60ch',
pl: '45px',
}}
>
{s.description}
</Typography>
</Box>
</Box>
)
})}
</Stack>
</Paper>
</Box>
)
}
@@ -1,19 +1,16 @@
import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem'
import ListItemIcon from '@mui/material/ListItemIcon'
import ListItemText from '@mui/material/ListItemText'
import Paper from '@mui/material/Paper'
import Box from '@mui/material/Box'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import { Banknote, CreditCard } from 'lucide-react'
const methods = [
{
icon: <CreditCard size={22} />,
primary: 'Онлайн-оплата через ЮKassa (карты, СБП)',
secondary: 'Оплата после подтверждения заказа админом. Перенаправление на защищённую платёжную страницу ЮKassa.',
icon: <CreditCard size={18} />,
primary: 'Онлайн-оплата через ЮKassa',
secondary: 'Карты, СБП. Оплата после подтверждения заказа админом. Перенаправление на защищённую платёжную страницу.',
},
{
icon: <Banknote size={22} />,
icon: <Banknote size={18} />,
primary: 'Оплата при получении',
secondary: 'Наличными или картой при самовывозе.',
},
@@ -21,22 +18,86 @@ const methods = [
export function PaymentSection() {
return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom>
<Box>
<Typography
variant="h5"
sx={{
fontWeight: 700,
letterSpacing: '-0.03em',
lineHeight: 1.15,
mb: 1,
}}
>
Оплата
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Оплата происходит после подтверждения заказа админом. Вы получите уведомление, когда заказ будет подтверждён и
готов к оплате.
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
mb: 3,
maxWidth: '56ch',
}}
>
Оплата происходит после подтверждения заказа админом. Вы получите уведомление, когда заказ будет подтверждён и готов к оплате.
</Typography>
<List disablePadding>
<Stack spacing={2}>
{methods.map((m) => (
<ListItem key={m.primary} disableGutters>
<ListItemIcon sx={{ minWidth: 40 }}>{m.icon}</ListItemIcon>
<ListItemText primary={m.primary} secondary={m.secondary} />
</ListItem>
<Stack
key={m.primary}
direction="row"
spacing={2}
sx={{
alignItems: 'flex-start',
p: 2.5,
borderRadius: '12px',
border: '1px solid',
borderColor: 'divider',
transition: 'all 0.2s ease',
'&:hover': {
borderColor: 'action.disabledBackground',
},
}}
>
<Box
sx={{
width: 36,
height: 36,
borderRadius: '8px',
backgroundColor: 'action.hover',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'text.secondary',
flexShrink: 0,
}}
>
{m.icon}
</Box>
<Stack spacing={0.25}>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.875rem',
letterSpacing: '-0.01em',
}}
>
{m.primary}
</Typography>
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.6,
}}
>
{m.secondary}
</Typography>
</Stack>
</Stack>
))}
</List>
</Paper>
</Stack>
</Box>
)
}
@@ -1,35 +1,67 @@
import Paper from '@mui/material/Paper'
import Box from '@mui/material/Box'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
export function ReturnsSection() {
return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom>
<Box>
<Typography
variant="h5"
sx={{
fontWeight: 700,
letterSpacing: '-0.03em',
lineHeight: 1.15,
mb: 3,
}}
>
Возврат и гарантии
</Typography>
<Stack spacing={2}>
<Paper variant="outlined" sx={{ p: 2, borderRadius: 2 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }} gutterBottom>
<Stack spacing={2.5}>
<Box>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.875rem',
letterSpacing: '-0.01em',
mb: 1,
}}
>
Возврат
</Typography>
<Typography variant="body2" color="text.secondary">
Если товар не соответствует описанию или имеет производственный дефект, свяжитесь с нами в течение 7 дней
после получения. Мы заменим изделие на аналогичное или вернём деньги. Возврат товара надлежащего качества
возможен в течение 14 дней, если изделие не было в употреблении и сохранён его товарный вид.
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
Если товар не соответствует описанию или имеет производственный дефект, свяжитесь с нами в течение 7 дней после получения. Мы заменим изделие на аналогичное или вернём деньги. Возврат товара надлежащего качества возможен в течение 14 дней, если изделие не было в употреблении и сохранён его товарный вид.
</Typography>
</Paper>
<Paper variant="outlined" sx={{ p: 2, borderRadius: 2 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }} gutterBottom>
</Box>
<Box>
<Typography
sx={{
fontWeight: 600,
fontSize: '0.875rem',
letterSpacing: '-0.01em',
mb: 1,
}}
>
Гарантия качества
</Typography>
<Typography variant="body2" color="text.secondary">
Мы отвечаем за качество каждого изделия ручной работы. Все дефекты, возникшие не по вине покупателя,
устраняются или компенсируются заменой изделия. Если у вас возникли вопросы по качеству напишите нам, и мы
решим проблему в кратчайшие сроки.
<Typography
sx={{
fontSize: '0.8rem',
color: 'text.secondary',
lineHeight: 1.65,
}}
>
Мы отвечаем за качество каждого изделия ручной работы. Все дефекты, возникшие не по вине покупателя, устраняются или компенсируются заменой изделия. Если у вас возникли вопросы по качеству напишите нам, и мы решим проблему в кратчайшие сроки.
</Typography>
</Paper>
</Box>
</Stack>
</Paper>
</Box>
)
}
+138 -14
View File
@@ -1,34 +1,158 @@
import type { ReactElement } from 'react'
import Chip from '@mui/material/Chip'
import { type ReactNode, useState } from 'react'
import Box from '@mui/material/Box'
import Tooltip from '@mui/material/Tooltip'
import { Banknote, CheckCircle, Package, PackageCheck, PackageSearch, Store, XCircle } from 'lucide-react'
import Typography from '@mui/material/Typography'
import { getOrderStatusData, type StatusIconName } from '@/shared/lib/order-status-data'
const iconMap: Record<StatusIconName, ReactElement> = {
banknote: <Banknote size={18} />,
'check-circle': <CheckCircle size={18} />,
'package-search': <PackageSearch size={18} />,
package: <Package size={18} />,
'package-check': <PackageCheck size={18} />,
store: <Store size={18} />,
'x-circle': <XCircle size={18} />,
const iconMap: Record<StatusIconName, ReactNode> = {
banknote: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="6" width="20" height="12" rx="2" />
<circle cx="12" cy="12" r="3" />
<path d="M6 12h.01M18 12h.01" />
</svg>
),
'check-circle': (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</svg>
),
'package-search': (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M16 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8z" />
<path d="M19 19l-3-3" />
<path d="M21 10V7a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 7v3" />
<path d="M3 10l9 5 9-5" />
</svg>
),
package: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
<line x1="12" y1="22.08" x2="12" y2="12" />
</svg>
),
'package-check': (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
<line x1="12" y1="22.08" x2="12" y2="12" />
<polyline points="9 13 11 15 15 11" />
</svg>
),
store: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
),
'x-circle': (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" />
<path d="M15 9l-6 6M9 9l6 6" />
</svg>
),
}
const colorMap: Record<string, { bg: string; border: string; text: string; dot: string }> = {
warning: {
bg: 'rgba(245, 124, 0, 0.06)',
border: 'rgba(245, 124, 0, 0.25)',
text: '#c27a00',
dot: '#F57C00',
},
success: {
bg: 'rgba(46, 139, 87, 0.06)',
border: 'rgba(46, 139, 87, 0.25)',
text: '#1e6e42',
dot: '#2E8B57',
},
info: {
bg: 'rgba(84, 110, 122, 0.06)',
border: 'rgba(84, 110, 122, 0.25)',
text: '#3d5a68',
dot: '#546E7A',
},
error: {
bg: 'rgba(211, 47, 47, 0.06)',
border: 'rgba(211, 47, 47, 0.25)',
text: '#a83232',
dot: '#D32F2F',
},
}
interface OrderStatusChipProps {
status: string
tooltipOverride?: string
size?: 'sm' | 'md'
}
export function OrderStatusChip({ status, tooltipOverride }: OrderStatusChipProps) {
export function OrderStatusChip({ status, tooltipOverride, size = 'md' }: OrderStatusChipProps) {
const data = getOrderStatusData(status)
const label = data?.label ?? status
const color = data?.color ?? 'default'
const colorKey = data?.color ?? 'default'
const icon = data ? iconMap[data.iconName] : undefined
const tooltip = tooltipOverride ?? data?.description ?? ''
const colors = colorMap[colorKey] ?? colorMap.info
const [hovered, setHovered] = useState(false)
const padding = size === 'sm' ? '3px 10px' : '5px 14px'
const fontSize = size === 'sm' ? '0.7rem' : '0.75rem'
const iconSize = size === 'sm' ? 14 : 16
return (
<Tooltip title={tooltip} arrow placement="top">
<Chip icon={icon} label={label} color={color as 'default'} size="small" variant="outlined" />
<Box
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
sx={{
display: 'inline-flex',
alignItems: 'center',
gap: '6px',
padding: padding,
borderRadius: '999px',
border: `1px solid ${colors.border}`,
backgroundColor: hovered ? colors.bg : 'transparent',
transition: 'all 0.2s cubic-bezier(0.16, 1, 0.3, 1)',
cursor: 'default',
'&:active': {
transform: 'scale(0.97)',
},
}}
>
<Box
sx={{
width: '6px',
height: '6px',
borderRadius: '50%',
backgroundColor: colors.dot,
flexShrink: 0,
}}
/>
<Box
sx={{
color: colors.text,
display: 'flex',
alignItems: 'center',
width: iconSize,
height: iconSize,
}}
>
{icon}
</Box>
<Typography
sx={{
fontSize,
fontWeight: 600,
color: colors.text,
lineHeight: 1.2,
letterSpacing: '-0.01em',
}}
>
{label}
</Typography>
</Box>
</Tooltip>
)
}