This commit is contained in:
Kirill
2026-05-24 13:43:23 +05:00
parent 75841342c6
commit 2fe426b70a
17 changed files with 425 additions and 82 deletions
@@ -0,0 +1 @@
5700
@@ -0,0 +1 @@
{"type":"server-started","port":53669,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:53669","screen_dir":"/mnt/d/my_projects/shop/.superpowers/brainstorm/7074-1779609479/content","state_dir":"/mnt/d/my_projects/shop/.superpowers/brainstorm/7074-1779609479/state"}
@@ -0,0 +1 @@
7319
+2
View File
@@ -3,6 +3,7 @@ import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { DeliverySection } from './sections/DeliverySection' import { DeliverySection } from './sections/DeliverySection'
import { HowToOrderSection } from './sections/HowToOrderSection' import { HowToOrderSection } from './sections/HowToOrderSection'
import { OrderStatusesSection } from './sections/OrderStatusesSection'
import { PaymentSection } from './sections/PaymentSection' import { PaymentSection } from './sections/PaymentSection'
import { ReturnsSection } from './sections/ReturnsSection' import { ReturnsSection } from './sections/ReturnsSection'
@@ -20,6 +21,7 @@ export function InfoPage() {
<HowToOrderSection /> <HowToOrderSection />
<DeliverySection /> <DeliverySection />
<PaymentSection /> <PaymentSection />
<OrderStatusesSection />
<ReturnsSection /> <ReturnsSection />
</Stack> </Stack>
</Box> </Box>
@@ -1,27 +1,13 @@
import Grid from '@mui/material/Grid' import Grid from '@mui/material/Grid'
import Link from '@mui/material/Link'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import Stack from '@mui/material/Stack' import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { Package, Store } from 'lucide-react' import { Package, Store } from 'lucide-react'
import { Link as RouterLink } from 'react-router-dom'
import { DELIVERY_CARRIER_OPTIONS } from '@/shared/constants/delivery-carrier'
import { PICKUP_ADDRESS_FULL } from '@/shared/constants/pickup-point' import { PICKUP_ADDRESS_FULL } from '@/shared/constants/pickup-point'
const deliveries = [
{
title: 'Самовывоз',
icon: <Store size={28} />,
lines: ['Бесплатно.', PICKUP_ADDRESS_FULL, 'Перед визитом согласуем время — чтобы заказ точно был готов к выдаче.'],
},
{
title: 'Почта / Службы доставки',
icon: <Package size={28} />,
lines: [
'Отправка в другие города.',
'Каждому заказу присваивается трек-номер для отслеживания.',
'Стоимость рассчитывается по тарифу перевозчика при оформлении.',
],
},
]
export function DeliverySection() { export function DeliverySection() {
return ( return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}> <Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
@@ -29,23 +15,51 @@ export function DeliverySection() {
Доставка Доставка
</Typography> </Typography>
<Grid container spacing={2}> <Grid container spacing={2}>
{deliveries.map((d) => ( <Grid size={{ xs: 12, sm: 6 }}>
<Grid key={d.title} size={{ xs: 12, sm: 6, md: 4 }}> <Paper variant="outlined" sx={{ p: 2, borderRadius: 2, height: '100%' }}>
<Paper variant="outlined" sx={{ p: 2, borderRadius: 2, height: '100%' }}> <Stack spacing={1.5}>
<Stack spacing={1.5}> <Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}> <Store size={28} />
{d.icon} <Typography variant="h6">Самовывоз</Typography>
<Typography variant="h6">{d.title}</Typography> </Stack>
</Stack> <Typography variant="body2" color="text.secondary">
{d.lines.map((line, i) => ( Бесплатно.
<Typography key={i} variant="body2" color="text.secondary"> </Typography>
{line} <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> </Typography>
))} ))}
</Stack> </Stack>
</Paper> <Typography variant="body2" color="text.secondary">
</Grid> Стоимость рассчитывается по тарифу перевозчика. Админ скорректирует цену после оформления заказа.
))} </Typography>
</Stack>
</Paper>
</Grid>
</Grid> </Grid>
</Paper> </Paper>
) )
@@ -1,12 +1,30 @@
import { useState } from 'react'
import Box from '@mui/material/Box'
import Link from '@mui/material/Link'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import Step from '@mui/material/Step' import Step from '@mui/material/Step'
import StepContent from '@mui/material/StepContent' import StepContent from '@mui/material/StepContent'
import StepLabel from '@mui/material/StepLabel' import StepLabel from '@mui/material/StepLabel'
import Stepper from '@mui/material/Stepper' import Stepper from '@mui/material/Stepper'
import Tab from '@mui/material/Tab'
import Tabs from '@mui/material/Tabs'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { CheckCircle, ClipboardList, Mail, ShoppingCart, Truck } from 'lucide-react' import {
CheckCircle,
ClipboardList,
Clock,
CreditCard,
MapPin,
PackageOpen,
ShoppingCart,
Star,
Store,
Truck,
} from 'lucide-react'
import { Link as RouterLink } from 'react-router-dom'
import { PICKUP_ADDRESS_SHORT } from '@/shared/constants/pickup-point'
const steps = [ const commonSteps = [
{ {
label: 'Выберите товары', label: 'Выберите товары',
icon: <ShoppingCart size={20} />, icon: <ShoppingCart size={20} />,
@@ -17,39 +35,96 @@ const steps = [
icon: <ClipboardList size={20} />, icon: <ClipboardList size={20} />,
text: 'Перейдите в корзину и проверьте состав заказа: названия товаров, количество и итоговую сумму. Здесь же можно изменить количество или удалить позиции.', text: 'Перейдите в корзину и проверьте состав заказа: названия товаров, количество и итоговую сумму. Здесь же можно изменить количество или удалить позиции.',
}, },
]
const deliverySteps = [
{ {
label: 'Укажите контакты и адрес', label: 'Укажите адрес доставки и получателя',
icon: <Mail size={20} />, icon: <MapPin size={20} />,
text: 'Заполните имя, телефон и email для связи. Укажите адрес доставки — город, улицу, дом и квартиру. Эти данные нужны для расчёта стоимости и сроков.', text: 'Заполните имя, телефон, email и адрес доставки. Если у вас уже есть сохранённые адреса — выберите из списка или добавьте новый в личном кабинете.',
}, },
{ {
label: 'Выберите доставку и оплату', label: 'Выберите способ доставки',
icon: <Truck size={20} />, icon: <Truck size={20} />,
text: 'Выберите способ доставки: самовывоз, курьер или почта/СДЭК. Затем укажите способ оплаты: картой онлайн или при получении.', text: 'Доступны: Почта России, Озон ПВЗ, Яндекс ПВЗ, 5Post, WB ПВЗ. После оформления админ рассчитает точную стоимость доставки и скорректирует цену заказа.',
}, },
{ {
label: 'Подтвердите заказ', label: 'Оплатите заказ',
icon: <CheckCircle size={20} />, icon: <CreditCard size={20} />,
text: 'Проверьте все данные ещё раз и нажмите «Оформить заказ». После этого мастер получит уведомление и начнёт подготовку вашего изделия.', text: 'Онлайн-оплата через ЮKassa — банковские карты и СБП. Перенаправление на защищённую платёжную страницу.',
},
{
label: 'Получите заказ',
icon: <PackageOpen size={20} />,
text: 'После отправки вы получите трек-номер для отслеживания. Следите за статусом заказа в личном кабинете.',
}, },
] ]
const pickupSteps = [
{
label: 'Подтвердите заказ',
icon: <CheckCircle size={20} />,
text: 'Выберите способ оплаты: онлайн через ЮKassa (карты, СБП) или при получении (наличные / карта).',
},
{
label: 'Согласуйте время получения',
icon: <Clock size={20} />,
text: 'Админ свяжется с вами, чтобы договориться об удобном времени выдачи заказа.',
},
{
label: 'Получите заказ',
icon: <Store size={20} />,
text: `Адрес: ${PICKUP_ADDRESS_SHORT}. Перед визитом согласуем время, чтобы заказ точно был готов.`,
},
]
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>
))}
</Stepper>
)
}
export function HowToOrderSection() { export function HowToOrderSection() {
const [tab, setTab] = useState(0)
return ( return (
<Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}> <Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom> <Typography variant="h5" gutterBottom>
Как оформить заказ Как оформить заказ
</Typography> </Typography>
<Stepper orientation="vertical" activeStep={-1}> <BranchStepper steps={commonSteps} />
{steps.map((step) => ( <Box sx={{ mt: 2, mb: 1 }}>
<Step key={step.label} completed={false}> <Tabs value={tab} onChange={(_, v) => setTab(v)} variant="fullWidth">
<StepLabel slots={{ stepIcon: () => step.icon }}>{step.label}</StepLabel> <Tab label="Доставка" />
<StepContent> <Tab label="Самовывоз" />
<Typography color="text.secondary">{step.text}</Typography> </Tabs>
</StepContent> </Box>
</Step> {tab === 0 && <BranchStepper steps={deliverySteps} />}
))} {tab === 1 && (
</Stepper> <Box>
<BranchStepper steps={pickupSteps} />
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
<Link component={RouterLink} to="/about">
Посмотреть на карте
</Link>
</Typography>
</Box>
)}
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
<Star size={18} />
<Typography variant="body2" color="text.secondary">
После получения заказа вы можете оставить отзыв в личном кабинете.
</Typography>
</Box>
</Paper> </Paper>
) )
} }
@@ -0,0 +1,47 @@
import type { ReactElement } from 'react'
import Chip from '@mui/material/Chip'
import Paper from '@mui/material/Paper'
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} />,
}
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>
))}
</Stack>
</Paper>
)
}
@@ -9,13 +9,13 @@ import { Banknote, CreditCard } from 'lucide-react'
const methods = [ const methods = [
{ {
icon: <CreditCard size={22} />, icon: <CreditCard size={22} />,
primary: 'Банковская карта онлайн', primary: 'Онлайн-оплата через ЮKassa (карты, СБП)',
secondary: 'Оплата картой Visa, Mastercard или МИР сразу при оформлении заказа.', secondary: 'Оплата после подтверждения заказа админом. Перенаправление на защищённую платёжную страницу ЮKassa.',
}, },
{ {
icon: <Banknote size={22} />, icon: <Banknote size={22} />,
primary: 'Оплата при получении', primary: 'Оплата при получении',
secondary: 'Оплата наличными или картой при получении заказа.', secondary: 'Наличными или картой при самовывозе.',
}, },
] ]
@@ -26,7 +26,7 @@ export function PaymentSection() {
Оплата Оплата
</Typography> </Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}> <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Оплата происходит после подтверждения заказа мастером. Вы получите уведомление, когда заказ будет подтверждён и Оплата происходит после подтверждения заказа админом. Вы получите уведомление, когда заказ будет подтверждён и
готов к оплате. готов к оплате.
</Typography> </Typography>
<List disablePadding> <List disablePadding>
@@ -25,7 +25,7 @@ import { deliveryCarrierLabelRu } from '@/shared/constants/delivery-carrier'
import { PICKUP_ADDRESS_FULL } from '@/shared/constants/pickup-point' import { PICKUP_ADDRESS_FULL } from '@/shared/constants/pickup-point'
import { formatPriceRub } from '@/shared/lib/format-price' import { formatPriceRub } from '@/shared/lib/format-price'
import { parseOrderAddressSnapshot } from '@/shared/lib/order-address-snapshot' import { parseOrderAddressSnapshot } from '@/shared/lib/order-address-snapshot'
import { orderStatusLabelRu } from '@/shared/lib/order-status-labels' import { OrderStatusChip } from '@/shared/ui/OrderStatusChip'
export function OrderDetailPage() { export function OrderDetailPage() {
const { id } = useParams() const { id } = useParams()
@@ -130,7 +130,14 @@ export function OrderDetailPage() {
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ mb: 2, alignItems: { sm: 'center' } }}> <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ mb: 2, alignItems: { sm: 'center' } }}>
<Box sx={{ flexGrow: 1 }}> <Box sx={{ flexGrow: 1 }}>
<Typography variant="h4">Заказ #{order.id.slice(-6)}</Typography> <Typography variant="h4">Заказ #{order.id.slice(-6)}</Typography>
<Typography color="text.secondary">Статус: {orderStatusLabelRu(order.status)}</Typography> <OrderStatusChip
status={order.status}
tooltipOverride={
order.status === 'PENDING_PAYMENT' && order.deliveryType === 'delivery' && !order.deliveryFeeLocked
? 'Оплата происходит после подтверждения заказа админом. Вы получите уведомление, когда заказ будет подтверждён и готов к оплате.'
: undefined
}
/>
</Box> </Box>
<Button component={RouterLink} to="/me/orders" variant="outlined"> <Button component={RouterLink} to="/me/orders" variant="outlined">
К списку К списку
@@ -4,3 +4,6 @@ export const PICKUP_COORDINATES = { lat: 58.09898000206914, lng: 57.813169680997
/** Полная строка адреса для текстовых блоков. */ /** Полная строка адреса для текстовых блоков. */
export const PICKUP_ADDRESS_FULL = export const PICKUP_ADDRESS_FULL =
'618909, Россия, Пермский край, Лысьвенский муниципальный округ, Лысьва, улица Мира, 34' '618909, Россия, Пермский край, Лысьвенский муниципальный округ, Лысьва, улица Мира, 34'
/** Короткий адрес для компактных блоков. */
export const PICKUP_ADDRESS_SHORT = 'Лысьва, ул. Мира, 34'
@@ -0,0 +1,67 @@
export type StatusColor = 'warning' | 'success' | 'info' | 'error'
export type StatusIconName = 'banknote' | 'check-circle' | 'package-search' | 'package' | 'package-check' | 'store' | 'x-circle'
export interface OrderStatusData {
code: string
label: string
iconName: StatusIconName
color: StatusColor
description: string
}
export const ORDER_STATUS_DATA: ReadonlyArray<OrderStatusData> = [
{
code: 'PENDING_PAYMENT',
label: 'Ожидает оплаты',
iconName: 'banknote',
color: 'warning',
description:
'Заказ оформлен и подтверждён администратором. Оплатите онлайн через ЮKassa или дождитесь получения (для самовывоза с оплатой при получении).',
},
{
code: 'PAID',
label: 'Оплачен',
iconName: 'check-circle',
color: 'success',
description: 'Оплата получена. Админ скоро возьмёт заказ в работу.',
},
{
code: 'IN_PROGRESS',
label: 'Подготовка к отправке',
iconName: 'package-search',
color: 'info',
description: 'Админ готовит заказ к отправке или выдаче. Скоро статус обновится.',
},
{
code: 'SHIPPED',
label: 'Отправлен',
iconName: 'package',
color: 'info',
description: 'Заказ передан в службу доставки. Трек-номер для отслеживания(при наличии) будет указан в сообщении админа.',
},
{
code: 'READY_FOR_PICKUP',
label: 'Готов к получению',
iconName: 'store',
color: 'success',
description: 'Заказ готов к самовывозу. Приезжайте в согласованное время.',
},
{
code: 'DONE',
label: 'Завершён',
iconName: 'package-check',
color: 'success',
description: 'Заказ получен. Вы можете оставить отзыв в личном кабинете.',
},
{
code: 'CANCELLED',
label: 'Отменён',
iconName: 'x-circle',
color: 'error',
description: 'Заказ отменён. Если оплата была произведена, средства вернутся на карту.',
},
]
export function getOrderStatusData(code: string): OrderStatusData | undefined {
return ORDER_STATUS_DATA.find((s) => s.code === code)
}
+1 -1
View File
@@ -4,7 +4,7 @@ export function orderStatusLabelRu(code: string): string {
DRAFT: 'Черновик', DRAFT: 'Черновик',
PENDING_PAYMENT: 'Ожидает оплаты', PENDING_PAYMENT: 'Ожидает оплаты',
PAID: 'Оплачен', PAID: 'Оплачен',
IN_PROGRESS: 'В работе', IN_PROGRESS: 'Подготовка к отправке',
SHIPPED: 'Отправлен', SHIPPED: 'Отправлен',
READY_FOR_PICKUP: 'Готово к получению', READY_FOR_PICKUP: 'Готово к получению',
DONE: 'Завершён', DONE: 'Завершён',
+34
View File
@@ -0,0 +1,34 @@
import type { ReactElement } from 'react'
import Chip from '@mui/material/Chip'
import Tooltip from '@mui/material/Tooltip'
import { Banknote, CheckCircle, Package, PackageCheck, PackageSearch, Store, XCircle } from 'lucide-react'
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} />,
}
interface OrderStatusChipProps {
status: string
tooltipOverride?: string
}
export function OrderStatusChip({ status, tooltipOverride }: OrderStatusChipProps) {
const data = getOrderStatusData(status)
const label = data?.label ?? status
const color = data?.color ?? 'default'
const icon = data ? iconMap[data.iconName] : undefined
const tooltip = tooltipOverride ?? data?.description ?? ''
return (
<Tooltip title={tooltip} arrow placement="top">
<Chip icon={icon} label={label} color={color as 'default'} size="small" variant="outlined" />
</Tooltip>
)
}
@@ -0,0 +1,91 @@
# Редизайн страницы /info — Спецификация
**Дата:** 2026-05-24
**Страница:** `/info` (Информация для покупателей)
**Подход:** Минимальные правки — обновить контент существующих секций, не меняя структуру страницы.
---
## 1. Секция «Как оформить заказ» (HowToOrderSection)
MUI Stepper с ветвлением на шаге 3. Два сценария: доставка и самовывоз.
### Общие шаги (1–2):
| Шаг | Название | Описание |
|-----|----------|----------|
| 1 | Выберите товары | Найдите нужное в каталоге, добавьте в корзину |
| 2 | Проверьте корзину | Убедитесь, что состав и количество верны |
### Ветвление на шаге 3:
#### Доставка:
| Шаг | Название | Описание |
|-----|----------|----------|
| 3 | Укажите адрес доставки и получателя | Имя, телефон, email, адрес (или добавьте новый в личном кабинете) |
| 4 | Выберите способ доставки | Почта России, Озон ПВЗ, Яндекс ПВЗ, 5Post, WB ПВЗ. Дождитесь расчёта стоимости доставки — мастер скорректирует цену |
| 5 | Оплатите заказ | Онлайн через ЮKassa (карты, СБП) |
| 6 | Получите заказ | Отслеживайте по трек-номеру |
#### Самовывоз:
| Шаг | Название | Описание |
|-----|----------|----------|
| 3 | Подтвердите заказ | Выберите оплату: онлайн через ЮKassa или при получении |
| 4 | Согласуйте время получения | Мастер свяжется с вами, чтобы договориться о времени |
| 5 | Получите заказ | Адрес: ул. Мира, 34 (ссылка на /about — карта) |
### Финальный шаг (после получения):
После получения заказа можно оставить отзыв.
---
## 2. Секция «Доставка» (DeliverySection)
Две карточки:
### Самовывоз
- Бесплатно
- Адрес: 618909, Россия, Пермский край, Лысьвенский муниципальный округ, Лысьва, улица Мира, 34
- Ссылка на /about (карта)
- Перед визитом согласуем время — чтобы заказ точно был готов к выдаче
### Доставка по России
Перечислить все 5 служб:
- Почта России
- Озон доставка (пункт выдачи)
- Яндекс доставка (пункт выдачи)
- 5Post (пункт выдачи)
- WB доставка (пункт выдачи)
- Каждому заказу присваивается трек-номер для отслеживания
- Стоимость рассчитывается по тарифу перевозчика, мастер скорректирует цену после оформления
---
## 3. Секция «Оплата» (PaymentSection)
Два способа:
| Способ | Описание |
|--------|----------|
| Онлайн-оплата через ЮKassa (карты, СБП) | Оплата после подтверждения заказа мастером. Перенаправление на защищённую платёжную страницу ЮKassa |
| Оплата при получении | Наличными или картой при самовывозе |
Примечание: оплата происходит после подтверждения заказа мастером.
---
## 4. Секция «Возврат» (ReturnsSection)
Без изменений — текущий контент остаётся.
---
## Файлы для изменения
| Файл | Что менять |
|------|-----------|
| `client/src/pages/info/ui/sections/HowToOrderSection.tsx` | Переписать stepper с ветвлением |
| `client/src/pages/info/ui/sections/DeliverySection.tsx` | Перечислить carriers, добавить ссылку на /about |
| `client/src/pages/info/ui/sections/PaymentSection.tsx` | Заменить «Банковская карта онлайн» на «ЮKassa (карты, СБП)» |
Binary file not shown.
@@ -10,10 +10,10 @@ export function renderOrderStatusChangedTg({ orderId, oldStatus, newStatus }) {
DRAFT: 'Черновик', DRAFT: 'Черновик',
PENDING_PAYMENT: 'Ожидает оплаты', PENDING_PAYMENT: 'Ожидает оплаты',
PAID: 'Оплачен', PAID: 'Оплачен',
IN_PROGRESS: 'В работе', IN_PROGRESS: 'Подготовка к отправке',
READY_FOR_PICKUP: 'Готов к выдаче', READY_FOR_PICKUP: 'Готов к выдаче',
SHIPPED: 'Отправлен', SHIPPED: 'Отправлен',
DONE: 'Выполнен', DONE: 'Завершён',
CANCELLED: 'Отменён', CANCELLED: 'Отменён',
} }
return `🔄 Заказ #${orderId.slice(0, 8)}\n${labels[oldStatus] || oldStatus} → <b>${labels[newStatus] || newStatus}</b>` return `🔄 Заказ #${orderId.slice(0, 8)}\n${labels[oldStatus] || oldStatus} → <b>${labels[newStatus] || newStatus}</b>`
+22 -22
View File
@@ -1,13 +1,13 @@
export const ORDER_STATUSES = Object.freeze([ export const ORDER_STATUSES = Object.freeze([
'DRAFT', "DRAFT",
'PENDING_PAYMENT', "PENDING_PAYMENT",
'PAID', "PAID",
'IN_PROGRESS', "IN_PROGRESS",
'SHIPPED', "SHIPPED",
'READY_FOR_PICKUP', "READY_FOR_PICKUP",
'DONE', "DONE",
'CANCELLED', "CANCELLED",
]) ]);
/** /**
* Допустимые переходы статусов, доступные админу. * Допустимые переходы статусов, доступные админу.
@@ -15,24 +15,24 @@ export const ORDER_STATUSES = Object.freeze([
* Для IN_PROGRESS: объект с ключами по deliveryType. * Для IN_PROGRESS: объект с ключами по deliveryType.
*/ */
export const ADMIN_ORDER_TRANSITIONS = Object.freeze({ export const ADMIN_ORDER_TRANSITIONS = Object.freeze({
DRAFT: ['PENDING_PAYMENT', 'CANCELLED'], DRAFT: ["PENDING_PAYMENT", "CANCELLED"],
PENDING_PAYMENT: ['PAID', 'CANCELLED'], PENDING_PAYMENT: ["PAID", "CANCELLED"],
PAID: ['IN_PROGRESS', 'CANCELLED'], PAID: ["IN_PROGRESS", "CANCELLED"],
IN_PROGRESS: Object.freeze({ IN_PROGRESS: Object.freeze({
delivery: ['SHIPPED', 'CANCELLED'], delivery: ["SHIPPED", "CANCELLED"],
pickup: ['READY_FOR_PICKUP', 'CANCELLED'], pickup: ["READY_FOR_PICKUP", "CANCELLED"],
}), }),
}) });
export function getNextAdminStatuses(from, deliveryType) { export function getNextAdminStatuses(from, deliveryType) {
const transition = ADMIN_ORDER_TRANSITIONS[from] const transition = ADMIN_ORDER_TRANSITIONS[from];
if (!transition) return [] if (!transition) return [];
if (Array.isArray(transition)) return [...transition] if (Array.isArray(transition)) return [...transition];
return transition[deliveryType] ? [...transition[deliveryType]] : [] return transition[deliveryType] ? [...transition[deliveryType]] : [];
} }
export function canTransitionAdminOrderStatus(order, next) { export function canTransitionAdminOrderStatus(order, next) {
const from = order.status const from = order.status;
if (from === next) return true if (from === next) return true;
return getNextAdminStatuses(from, order.deliveryType).includes(next) return getNextAdminStatuses(from, order.deliveryType).includes(next);
} }