feat: remove old manual payment dialog and api method
This commit is contained in:
@@ -73,20 +73,6 @@ export async function postOrderMessage(id: string, text: string): Promise<void>
|
|||||||
await apiClient.post(`me/orders/${id}/messages`, { text })
|
await apiClient.post(`me/orders/${id}/messages`, { text })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Подтверждение оплаты переводом: multipart detail + необязательный файл receipt (хотя бы одно нужно на сервере). */
|
|
||||||
export async function submitOrderPayment(
|
|
||||||
orderId: string,
|
|
||||||
payload: { detail: string; receiptFile: File | null },
|
|
||||||
): Promise<{ ok: boolean; status: string }> {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('detail', payload.detail)
|
|
||||||
if (payload.receiptFile) {
|
|
||||||
formData.append('receipt', payload.receiptFile)
|
|
||||||
}
|
|
||||||
const { data } = await apiClient.post<{ ok: boolean; status: string }>(`me/orders/${orderId}/pay`, formData)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function confirmOrderReceived(id: string): Promise<{ ok: boolean; status: string }> {
|
export async function confirmOrderReceived(id: string): Promise<{ ok: boolean; status: string }> {
|
||||||
const { data } = await apiClient.post<{ ok: boolean; status: string }>(`me/orders/${id}/confirm-received`)
|
const { data } = await apiClient.post<{ ok: boolean; status: string }>(`me/orders/${id}/confirm-received`)
|
||||||
return data
|
return data
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
export { OrderPaymentSection } from './ui/OrderPaymentSection'
|
export { OrderPaymentSection } from './ui/OrderPaymentSection'
|
||||||
export { PaymentDialog } from './ui/PaymentDialog'
|
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
|
||||||
import Alert from '@mui/material/Alert'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import Dialog from '@mui/material/Dialog'
|
|
||||||
import DialogActions from '@mui/material/DialogActions'
|
|
||||||
import DialogContent from '@mui/material/DialogContent'
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle'
|
|
||||||
import Stack from '@mui/material/Stack'
|
|
||||||
import TextField from '@mui/material/TextField'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import axios from 'axios'
|
|
||||||
import { PAYMENT_TRANSFER_INSTRUCTIONS_PLAIN } from '@/shared/constants/payment-instructions'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
open: boolean
|
|
||||||
isPending: boolean
|
|
||||||
error: unknown
|
|
||||||
onClose: () => void
|
|
||||||
onSubmit: (params: { detail: string; receiptFile: File | null }) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function paySubmitErrorMessage(err: unknown): string {
|
|
||||||
if (axios.isAxiosError(err)) {
|
|
||||||
const raw = err.response?.data
|
|
||||||
const apiMsg =
|
|
||||||
raw && typeof raw === 'object' && 'error' in raw && typeof (raw as { error: unknown }).error === 'string'
|
|
||||||
? (raw as { error: string }).error
|
|
||||||
: null
|
|
||||||
return apiMsg || err.message || 'Не удалось отправить данные оплаты'
|
|
||||||
}
|
|
||||||
if (err instanceof Error) return err.message
|
|
||||||
return 'Не удалось отправить данные оплаты'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PaymentDialog({ open, isPending, error, onClose, onSubmit }: Props) {
|
|
||||||
const [detail, setDetail] = useState('')
|
|
||||||
const [receiptFile, setReceiptFile] = useState<File | null>(null)
|
|
||||||
const [clientError, setClientError] = useState<string | null>(null)
|
|
||||||
|
|
||||||
const receiptPreviewUrl = useMemo(() => {
|
|
||||||
if (!receiptFile) return null
|
|
||||||
return URL.createObjectURL(receiptFile)
|
|
||||||
}, [receiptFile])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!receiptPreviewUrl) return
|
|
||||||
return () => URL.revokeObjectURL(receiptPreviewUrl)
|
|
||||||
}, [receiptPreviewUrl])
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
setDetail('')
|
|
||||||
setReceiptFile(null)
|
|
||||||
setClientError(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
if (isPending) return
|
|
||||||
reset()
|
|
||||||
onClose()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
const hasText = detail.trim().length > 0
|
|
||||||
const hasFile = Boolean(receiptFile)
|
|
||||||
if (!hasText && !hasFile) {
|
|
||||||
setClientError('Укажите комментарий и/или прикрепите чек.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setClientError(null)
|
|
||||||
onSubmit({ detail: detail.trim(), receiptFile })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
|
|
||||||
<DialogTitle>Подтверждение оплаты</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap', mb: 2 }}>
|
|
||||||
{PAYMENT_TRANSFER_INSTRUCTIONS_PLAIN}
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
label="Комментарий об оплате (сумма, время перевода и т.д.)"
|
|
||||||
value={detail}
|
|
||||||
onChange={(e) => {
|
|
||||||
setDetail(e.target.value)
|
|
||||||
setClientError(null)
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
multiline
|
|
||||||
minRows={3}
|
|
||||||
sx={{ mb: 2 }}
|
|
||||||
/>
|
|
||||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} sx={{ mb: 1, alignItems: { sm: 'center' } }}>
|
|
||||||
<Button component="label" variant="outlined">
|
|
||||||
Прикрепить чек (png, jpg, webp)
|
|
||||||
<input
|
|
||||||
hidden
|
|
||||||
type="file"
|
|
||||||
accept="image/png,image/jpeg,image/webp"
|
|
||||||
onChange={(e) => {
|
|
||||||
const file = e.target.files?.[0]
|
|
||||||
setReceiptFile(file ?? null)
|
|
||||||
setClientError(null)
|
|
||||||
e.currentTarget.value = ''
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
{receiptFile && (
|
|
||||||
<Button color="error" variant="text" onClick={() => setReceiptFile(null)}>
|
|
||||||
Убрать файл
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
|
||||||
Нужен текст комментария и/или изображение чека.
|
|
||||||
</Typography>
|
|
||||||
{receiptPreviewUrl && (
|
|
||||||
<Box
|
|
||||||
component="img"
|
|
||||||
src={receiptPreviewUrl}
|
|
||||||
alt="Предпросмотр чека"
|
|
||||||
sx={{ maxWidth: '100%', maxHeight: 200, borderRadius: 1, border: 1, borderColor: 'divider', mb: 1 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{clientError && (
|
|
||||||
<Alert severity="warning" sx={{ mb: 1 }}>
|
|
||||||
{clientError}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{error ? (
|
|
||||||
<Alert severity="error" sx={{ mt: 1 }}>
|
|
||||||
{paySubmitErrorMessage(error)}
|
|
||||||
</Alert>
|
|
||||||
) : null}
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleClose} disabled={isPending}>
|
|
||||||
Отмена
|
|
||||||
</Button>
|
|
||||||
<Button variant="contained" disabled={isPending} onClick={handleSubmit}>
|
|
||||||
Подтвердить оплату
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/** Текст модалки оплаты (можно переопределить через VITE_PAYMENT_INSTRUCTIONS — многострочная строка \n). */
|
|
||||||
const fromEnv =
|
|
||||||
typeof import.meta.env.VITE_PAYMENT_INSTRUCTIONS === 'string' ? import.meta.env.VITE_PAYMENT_INSTRUCTIONS.trim() : ''
|
|
||||||
|
|
||||||
export const PAYMENT_TRANSFER_INSTRUCTIONS_PLAIN =
|
|
||||||
fromEnv ||
|
|
||||||
[
|
|
||||||
'Временно оплата доступна только переводом на ВТБ / Сбербанк.',
|
|
||||||
'',
|
|
||||||
'По номеру +79524181624',
|
|
||||||
'Получатель: Лариса К',
|
|
||||||
].join('\n')
|
|
||||||
Reference in New Issue
Block a user