fix /media route
				
					
				
			This commit is contained in:
		| @@ -23,12 +23,7 @@ type UseMediaFileUploadProps = { | |||||||
|   setValue: UseFormSetValue<any> |   setValue: UseFormSetValue<any> | ||||||
| } | } | ||||||
|  |  | ||||||
| export const useMediaFileUpload = ({ | export const useMediaFileUpload = ({selectedMediaType, setError, clearErrors, setValue}: UseMediaFileUploadProps) => { | ||||||
|   selectedMediaType, |  | ||||||
|   setError, |  | ||||||
|   clearErrors, |  | ||||||
|   setValue, |  | ||||||
| }: UseMediaFileUploadProps) => { |  | ||||||
|   const [selectedFile, setSelectedFile] = useState<File | null>(null) |   const [selectedFile, setSelectedFile] = useState<File | null>(null) | ||||||
|   const [previewUrl, setPreviewUrl] = useState<string | null>(null) |   const [previewUrl, setPreviewUrl] = useState<string | null>(null) | ||||||
|  |  | ||||||
| @@ -57,11 +52,10 @@ export const useMediaFileUpload = ({ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleMediaTypeChange = (event: any) => { |   const handleMediaTypeChange = (newMediaType: number | null) => { | ||||||
|     const newMediaType = event.target.value |     setValue('media_type', newMediaType || null) | ||||||
|     setValue('media_type', newMediaType) |  | ||||||
|  |  | ||||||
|     if (selectedFile) { |     if (selectedFile && newMediaType) { | ||||||
|       const error = validateFileType(selectedFile, newMediaType) |       const error = validateFileType(selectedFile, newMediaType) | ||||||
|       if (error) { |       if (error) { | ||||||
|         setError('file', {type: 'manual', message: error}) |         setError('file', {type: 'manual', message: error}) | ||||||
|   | |||||||
| @@ -1,22 +1,30 @@ | |||||||
| import {Box, TextField, Button, Typography, FormControl, InputLabel, Select, MenuItem} from '@mui/material' | import {Box, TextField, Button, Typography, Autocomplete} from '@mui/material' | ||||||
| import {Create} from '@refinedev/mui' | import {Create} from '@refinedev/mui' | ||||||
| import {useForm} from '@refinedev/react-hook-form' | import {useForm} from '@refinedev/react-hook-form' | ||||||
|  | import {Controller} from 'react-hook-form' | ||||||
|  |  | ||||||
| import {MEDIA_TYPES} from '../../lib/constants' | import {MEDIA_TYPES} from '../../lib/constants' | ||||||
| import {ALLOWED_IMAGE_TYPES, ALLOWED_VIDEO_TYPES, useMediaFileUpload} from '../../components/media/MediaFormUtils' | import {ALLOWED_IMAGE_TYPES, ALLOWED_VIDEO_TYPES, useMediaFileUpload} from '../../components/media/MediaFormUtils' | ||||||
|  |  | ||||||
|  | type MediaFormValues = { | ||||||
|  |   filename: string | ||||||
|  |   media_type: number | ||||||
|  |   file?: File | ||||||
|  | } | ||||||
|  |  | ||||||
| export const MediaCreate = () => { | export const MediaCreate = () => { | ||||||
|   const { |   const { | ||||||
|     saveButtonProps, |     saveButtonProps, | ||||||
|     refineCore: {formLoading, onFinish}, |     refineCore: {formLoading, onFinish}, | ||||||
|     register, |     register, | ||||||
|  |     control, | ||||||
|     formState: {errors}, |     formState: {errors}, | ||||||
|     setValue, |     setValue, | ||||||
|     handleSubmit, |     handleSubmit, | ||||||
|     watch, |     watch, | ||||||
|     setError, |     setError, | ||||||
|     clearErrors, |     clearErrors, | ||||||
|   } = useForm({}) |   } = useForm<MediaFormValues>({}) | ||||||
|  |  | ||||||
|   const selectedMediaType = watch('media_type') |   const selectedMediaType = watch('media_type') | ||||||
|  |  | ||||||
| @@ -44,6 +52,45 @@ export const MediaCreate = () => { | |||||||
|         }), |         }), | ||||||
|       }} |       }} | ||||||
|     > |     > | ||||||
|  |       <Controller | ||||||
|  |         control={control} | ||||||
|  |         name="media_type" | ||||||
|  |         rules={{ | ||||||
|  |           required: 'Это поле является обязательным', | ||||||
|  |         }} | ||||||
|  |         render={({field}) => ( | ||||||
|  |           <Autocomplete | ||||||
|  |             options={MEDIA_TYPES} | ||||||
|  |             value={MEDIA_TYPES.find((option) => option.value === field.value) || null} | ||||||
|  |             onChange={(_, value) => { | ||||||
|  |               field.onChange(value?.value || null) | ||||||
|  |               handleMediaTypeChange(value?.value || null) | ||||||
|  |             }} | ||||||
|  |             getOptionLabel={(item) => { | ||||||
|  |               return item ? item.label : '' | ||||||
|  |             }} | ||||||
|  |             isOptionEqualToValue={(option, value) => { | ||||||
|  |               return option.value === value?.value | ||||||
|  |             }} | ||||||
|  |             renderInput={(params) => <TextField {...params} label="Тип" margin="normal" variant="outlined" error={!!errors.media_type} helperText={(errors as any)?.media_type?.message} required />} | ||||||
|  |           /> | ||||||
|  |         )} | ||||||
|  |       /> | ||||||
|  |  | ||||||
|  |       <TextField | ||||||
|  |         {...register('filename', { | ||||||
|  |           required: 'Это поле является обязательным', | ||||||
|  |         })} | ||||||
|  |         error={!!(errors as any)?.filename} | ||||||
|  |         helperText={(errors as any)?.filename?.message} | ||||||
|  |         margin="normal" | ||||||
|  |         fullWidth | ||||||
|  |         InputLabelProps={{shrink: true}} | ||||||
|  |         type="text" | ||||||
|  |         label="Название" | ||||||
|  |         name="filename" | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       <Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off"> |       <Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off"> | ||||||
|         <Box display="flex" flexDirection="column-reverse" alignItems="center" gap={6}> |         <Box display="flex" flexDirection="column-reverse" alignItems="center" gap={6}> | ||||||
|           <Box display="flex" flexDirection="column" alignItems="center" gap={2}> |           <Box display="flex" flexDirection="column" alignItems="center" gap={2}> | ||||||
| @@ -71,44 +118,6 @@ export const MediaCreate = () => { | |||||||
|             </Box> |             </Box> | ||||||
|           )} |           )} | ||||||
|         </Box> |         </Box> | ||||||
|  |  | ||||||
|         <FormControl fullWidth margin="normal" error={!!errors.media_type}> |  | ||||||
|           <InputLabel id="media-type-label">Тип</InputLabel> |  | ||||||
|           <Select |  | ||||||
|             labelId="media-type-label" |  | ||||||
|             label="Тип" |  | ||||||
|             {...register('media_type', { |  | ||||||
|               required: 'Это поле является обязательным', |  | ||||||
|               valueAsNumber: true, |  | ||||||
|             })} |  | ||||||
|             onChange={handleMediaTypeChange} |  | ||||||
|           > |  | ||||||
|             {MEDIA_TYPES.map((type) => ( |  | ||||||
|               <MenuItem key={type.value} value={type.value}> |  | ||||||
|                 {type.label} |  | ||||||
|               </MenuItem> |  | ||||||
|             ))} |  | ||||||
|           </Select> |  | ||||||
|           {errors.media_type && ( |  | ||||||
|             <Typography variant="caption" color="error"> |  | ||||||
|               {(errors as any)?.media_type?.message} |  | ||||||
|             </Typography> |  | ||||||
|           )} |  | ||||||
|         </FormControl> |  | ||||||
|  |  | ||||||
|         <TextField |  | ||||||
|           {...register('filename', { |  | ||||||
|             required: 'Это поле является обязательным', |  | ||||||
|           })} |  | ||||||
|           error={!!(errors as any)?.filename} |  | ||||||
|           helperText={(errors as any)?.filename?.message} |  | ||||||
|           margin="normal" |  | ||||||
|           fullWidth |  | ||||||
|           InputLabelProps={{shrink: true}} |  | ||||||
|           type="text" |  | ||||||
|           label="Название" |  | ||||||
|           name="filename" |  | ||||||
|         /> |  | ||||||
|       </Box> |       </Box> | ||||||
|     </Create> |     </Create> | ||||||
|   ) |   ) | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| import {Box, TextField, Button, Typography, FormControl, InputLabel, Select, MenuItem} from '@mui/material' | import {Box, TextField, Button, Typography, Autocomplete} from '@mui/material' | ||||||
| import {Edit} from '@refinedev/mui' | import {Edit} from '@refinedev/mui' | ||||||
| import {useForm} from '@refinedev/react-hook-form' | import {useForm} from '@refinedev/react-hook-form' | ||||||
| import {useEffect} from 'react' | import {useEffect} from 'react' | ||||||
| import {useShow} from '@refinedev/core' | import {useShow} from '@refinedev/core' | ||||||
|  | import {Controller} from 'react-hook-form' | ||||||
|  |  | ||||||
| import {MEDIA_TYPES} from '../../lib/constants' | import {MEDIA_TYPES} from '../../lib/constants' | ||||||
| import {ALLOWED_IMAGE_TYPES, ALLOWED_VIDEO_TYPES, useMediaFileUpload} from '../../components/media/MediaFormUtils' | import {ALLOWED_IMAGE_TYPES, ALLOWED_VIDEO_TYPES, useMediaFileUpload} from '../../components/media/MediaFormUtils' | ||||||
| @@ -24,6 +25,7 @@ export const MediaEdit = () => { | |||||||
|     watch, |     watch, | ||||||
|     setError, |     setError, | ||||||
|     clearErrors, |     clearErrors, | ||||||
|  |     control, | ||||||
|   } = useForm<MediaFormValues>({ |   } = useForm<MediaFormValues>({ | ||||||
|     defaultValues: { |     defaultValues: { | ||||||
|       filename: '', |       filename: '', | ||||||
| @@ -68,7 +70,47 @@ export const MediaEdit = () => { | |||||||
|       }} |       }} | ||||||
|     > |     > | ||||||
|       <Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off"> |       <Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off"> | ||||||
|         <Box display="flex" flexDirection="column-reverse" alignItems="center" gap={6}> |         <Controller | ||||||
|  |           control={control} | ||||||
|  |           name="media_type" | ||||||
|  |           rules={{ | ||||||
|  |             required: 'Это поле является обязательным', | ||||||
|  |           }} | ||||||
|  |           defaultValue={null} | ||||||
|  |           render={({field}) => ( | ||||||
|  |             <Autocomplete | ||||||
|  |               options={MEDIA_TYPES} | ||||||
|  |               value={MEDIA_TYPES.find((option) => option.value === field.value) || null} | ||||||
|  |               onChange={(_, value) => { | ||||||
|  |                 field.onChange(value?.value || null) | ||||||
|  |                 handleMediaTypeChange(value?.value || null) | ||||||
|  |               }} | ||||||
|  |               getOptionLabel={(item) => { | ||||||
|  |                 return item ? item.label : '' | ||||||
|  |               }} | ||||||
|  |               isOptionEqualToValue={(option, value) => { | ||||||
|  |                 return option.value === value?.value | ||||||
|  |               }} | ||||||
|  |               renderInput={(params) => <TextField {...params} label="Тип" margin="normal" variant="outlined" error={!!errors.media_type} helperText={(errors as any)?.media_type?.message} required />} | ||||||
|  |             /> | ||||||
|  |           )} | ||||||
|  |         /> | ||||||
|  |  | ||||||
|  |         <TextField | ||||||
|  |           {...register('filename', { | ||||||
|  |             required: 'Это поле является обязательным', | ||||||
|  |           })} | ||||||
|  |           error={!!(errors as any)?.filename} | ||||||
|  |           helperText={(errors as any)?.filename?.message} | ||||||
|  |           margin="normal" | ||||||
|  |           fullWidth | ||||||
|  |           InputLabelProps={{shrink: true}} | ||||||
|  |           type="text" | ||||||
|  |           label="Название" | ||||||
|  |           name="filename" | ||||||
|  |         /> | ||||||
|  |  | ||||||
|  |         <Box display="flex" flexDirection="column-reverse" alignItems="center" gap={4}> | ||||||
|           <Box display="flex" flexDirection="column" alignItems="center" gap={2}> |           <Box display="flex" flexDirection="column" alignItems="center" gap={2}> | ||||||
|             <Button variant="contained" component="label" disabled={!selectedMediaType}> |             <Button variant="contained" component="label" disabled={!selectedMediaType}> | ||||||
|               {selectedFile ? 'Изменить файл' : 'Загрузить файл'} |               {selectedFile ? 'Изменить файл' : 'Загрузить файл'} | ||||||
| @@ -90,48 +132,10 @@ export const MediaEdit = () => { | |||||||
|  |  | ||||||
|           {previewUrl && ( |           {previewUrl && ( | ||||||
|             <Box mt={2} display="flex" justifyContent="center"> |             <Box mt={2} display="flex" justifyContent="center"> | ||||||
|               <img src={previewUrl} alt="Preview" style={{maxWidth: '200px', borderRadius: 8}} /> |               <img src={previewUrl} alt="Preview" style={{maxWidth: '300px', objectFit: 'contain'}} /> | ||||||
|             </Box> |             </Box> | ||||||
|           )} |           )} | ||||||
|         </Box> |         </Box> | ||||||
|  |  | ||||||
|         <FormControl fullWidth margin="normal" error={!!errors.media_type}> |  | ||||||
|           <InputLabel id="media-type-label">Тип</InputLabel> |  | ||||||
|           <Select |  | ||||||
|             labelId="media-type-label" |  | ||||||
|             label="Тип" |  | ||||||
|             {...register('media_type', { |  | ||||||
|               required: 'Это поле является обязательным', |  | ||||||
|               valueAsNumber: true, |  | ||||||
|             })} |  | ||||||
|             onChange={handleMediaTypeChange} |  | ||||||
|           > |  | ||||||
|             {MEDIA_TYPES.map((type) => ( |  | ||||||
|               <MenuItem key={type.value} value={type.value}> |  | ||||||
|                 {type.label} |  | ||||||
|               </MenuItem> |  | ||||||
|             ))} |  | ||||||
|           </Select> |  | ||||||
|           {errors.media_type && ( |  | ||||||
|             <Typography variant="caption" color="error"> |  | ||||||
|               {(errors as any)?.media_type?.message} |  | ||||||
|             </Typography> |  | ||||||
|           )} |  | ||||||
|         </FormControl> |  | ||||||
|  |  | ||||||
|         <TextField |  | ||||||
|           {...register('filename', { |  | ||||||
|             required: 'Это поле является обязательным', |  | ||||||
|           })} |  | ||||||
|           error={!!(errors as any)?.filename} |  | ||||||
|           helperText={(errors as any)?.filename?.message} |  | ||||||
|           margin="normal" |  | ||||||
|           fullWidth |  | ||||||
|           InputLabelProps={{shrink: true}} |  | ||||||
|           type="text" |  | ||||||
|           label="Название" |  | ||||||
|           name="filename" |  | ||||||
|         /> |  | ||||||
|       </Box> |       </Box> | ||||||
|     </Edit> |     </Edit> | ||||||
|   ) |   ) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import {Stack, Typography} from '@mui/material' | import {Stack, Typography} from '@mui/material' | ||||||
| import {useShow} from '@refinedev/core' | import {useShow} from '@refinedev/core' | ||||||
| import {Show, TextFieldComponent as TextField} from '@refinedev/mui' | import {Show, TextFieldComponent as TextField} from '@refinedev/mui' | ||||||
|  | import {MEDIA_TYPES} from '../../lib/constants' | ||||||
|  |  | ||||||
| export const MediaShow = () => { | export const MediaShow = () => { | ||||||
|   const {query} = useShow({}) |   const {query} = useShow({}) | ||||||
| @@ -10,8 +11,12 @@ export const MediaShow = () => { | |||||||
|  |  | ||||||
|   const fields = [ |   const fields = [ | ||||||
|     {label: 'Название', data: 'filename'}, |     {label: 'Название', data: 'filename'}, | ||||||
|     {label: 'Тип', data: 'media_type'}, |     { | ||||||
|     {label: 'ID', data: 'id'}, |       label: 'Тип', | ||||||
|  |       data: 'media_type', | ||||||
|  |       render: (value: number) => MEDIA_TYPES.find((type) => type.value === value)?.label || value, | ||||||
|  |     }, | ||||||
|  |     // {label: 'ID', data: 'id'}, | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
| @@ -19,12 +24,12 @@ export const MediaShow = () => { | |||||||
|       <Stack gap={4}> |       <Stack gap={4}> | ||||||
|         {record && <img src={`https://wn.krbl.ru/media/${record?.id}/download`} alt={record?.filename} style={{maxWidth: '100%', height: '40vh', objectFit: 'contain', borderRadius: 8}} />} |         {record && <img src={`https://wn.krbl.ru/media/${record?.id}/download`} alt={record?.filename} style={{maxWidth: '100%', height: '40vh', objectFit: 'contain', borderRadius: 8}} />} | ||||||
|  |  | ||||||
|         {fields.map(({label, data}) => ( |         {fields.map(({label, data, render}) => ( | ||||||
|           <Stack key={data} gap={1}> |           <Stack key={data} gap={1}> | ||||||
|             <Typography variant="body1" fontWeight="bold"> |             <Typography variant="body1" fontWeight="bold"> | ||||||
|               {label} |               {label} | ||||||
|             </Typography> |             </Typography> | ||||||
|             <TextField value={record?.[data]} /> |             <TextField value={render ? render(record?.[data]) : record?.[data]} /> | ||||||
|           </Stack> |           </Stack> | ||||||
|         ))} |         ))} | ||||||
|       </Stack> |       </Stack> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user