deploy
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { Category, Product } from '@/entities/product/model/types'
|
||||
import { apiClient } from '@/shared/api/client'
|
||||
import { apiBaseURL } from '@/shared/config'
|
||||
import { ADMIN_UPLOAD_IMAGE_MAX_BYTES, formatAdminImageMaxSizeHint } from '@/shared/constants/upload-limits'
|
||||
|
||||
export type PublicProductsResponse = {
|
||||
items: Product[]
|
||||
@@ -119,8 +120,16 @@ export async function deleteAdminCategory(id: string): Promise<void> {
|
||||
|
||||
/** FormData: не задавать Content-Type вручную (boundary задаёт браузер). */
|
||||
export async function uploadAdminProductImages(files: FileList | readonly File[]): Promise<string[]> {
|
||||
const list = Array.from(files)
|
||||
for (const f of list) {
|
||||
if (f.size > ADMIN_UPLOAD_IMAGE_MAX_BYTES) {
|
||||
throw new Error(
|
||||
`Файл «${f.name}» слишком большой (максимум ${formatAdminImageMaxSizeHint()} на одно изображение).`,
|
||||
)
|
||||
}
|
||||
}
|
||||
const fd = new FormData()
|
||||
for (const f of Array.from(files)) {
|
||||
for (const f of list) {
|
||||
fd.append('files', f, f.name)
|
||||
}
|
||||
const token = localStorage.getItem('craftshop_auth_token')
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { fetchAdminCatalogSlider } from '@/entities/catalog-slider/api/catalog-slider-api'
|
||||
import { deleteGalleryImage, fetchAdminGallery } from '@/entities/gallery/api/gallery-api'
|
||||
import { uploadAdminProductImages } from '@/entities/product/api/product-api'
|
||||
import { formatAdminImageMaxSizeHint } from '@/shared/constants/upload-limits'
|
||||
import { invalidateQueryKeys } from '@/shared/lib/invalidate-query-keys'
|
||||
import { GallerySliderSection } from './GallerySliderSection'
|
||||
|
||||
@@ -80,6 +81,10 @@ export function AdminGalleryPage() {
|
||||
|
||||
<Divider sx={{ mb: 3 }} />
|
||||
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
|
||||
Форматы: PNG, JPEG, WebP. На один файл — до {formatAdminImageMaxSizeHint()}.
|
||||
</Typography>
|
||||
|
||||
<Stack direction="row" spacing={2} sx={{ mb: 3, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Button variant="contained" component="label" disabled={uploadMut.isPending}>
|
||||
Загрузить файлы
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
uploadAdminProductImages,
|
||||
} from '@/entities/product/api/product-api'
|
||||
import type { Category, Product } from '@/entities/product/model/types'
|
||||
import { formatAdminImageMaxSizeHint } from '@/shared/constants/upload-limits'
|
||||
import { formatPriceRub } from '@/shared/lib/format-price'
|
||||
import { getErrorMessage } from '@/shared/lib/get-error-message'
|
||||
import { invalidateQueryKeys } from '@/shared/lib/invalidate-query-keys'
|
||||
@@ -535,7 +536,8 @@ export function AdminPage() {
|
||||
Фото (загрузка)
|
||||
</Typography>
|
||||
<FormHelperText sx={{ mt: 0, mb: 1 }}>
|
||||
Крестик на превью убирает фото только из карточки; файл остаётся на сервере и в галерее.
|
||||
PNG, JPEG или WebP, до {formatAdminImageMaxSizeHint()} на файл. Крестик на превью убирает фото только из
|
||||
карточки; файл остаётся на сервере и в галерее.
|
||||
</FormHelperText>
|
||||
<Box
|
||||
sx={{
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/** Должно совпадать с `getProductImageMaxFileBytes()` на сервере (по умолчанию 20 МБ). */
|
||||
export const ADMIN_UPLOAD_IMAGE_MAX_BYTES = 20 * 1024 * 1024
|
||||
|
||||
export function formatAdminImageMaxSizeHint(): string {
|
||||
return `${Math.round(ADMIN_UPLOAD_IMAGE_MAX_BYTES / (1024 * 1024))} МБ`
|
||||
}
|
||||
Reference in New Issue
Block a user