init /media route

This commit is contained in:
maxim 2025-02-03 18:20:53 +03:00
parent deaf5d68bb
commit a19d0f9478
6 changed files with 329 additions and 0 deletions

View File

@ -22,6 +22,7 @@ import {authProvider} from './authProvider'
import {CountryList, CountryCreate, CountryEdit} from './pages/country'
import {CityList, CityCreate, CityEdit} from './pages/city'
import {CarrierList, CarrierCreate, CarrierEdit} from './pages/carrier'
import {MediaList, MediaCreate, MediaEdit, MediaShow} from './pages/media'
function App() {
return (
@ -86,6 +87,16 @@ function App() {
canDelete: true,
},
},
{
name: 'media',
list: '/media',
create: '/media/create',
edit: '/media/edit/:id',
show: '/media/show/:id',
meta: {
canDelete: true,
},
},
]}
options={{
syncWithLocation: true,
@ -136,6 +147,13 @@ function App() {
<Route path="edit/:id" element={<CarrierEdit />} />
</Route>
<Route path="/media">
<Route index element={<MediaList />} />
<Route path="create" element={<MediaCreate />} />
<Route path="edit/:id" element={<MediaEdit />} />
<Route path="show/:id" element={<MediaShow />} />
</Route>
<Route path="*" element={<ErrorComponent />} />
</Route>
<Route

108
src/pages/media/create.tsx Normal file
View File

@ -0,0 +1,108 @@
import {Box, TextField, Button, Typography} from '@mui/material'
import {Create} from '@refinedev/mui'
import {useForm} from '@refinedev/react-hook-form'
import {useState} from 'react'
export const MediaCreate = () => {
const {
saveButtonProps,
refineCore: {formLoading, onFinish},
register,
formState: {errors},
setValue,
handleSubmit,
watch,
} = useForm({})
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [previewUrl, setPreviewUrl] = useState<string | null>(null)
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
setValue('file', file)
setSelectedFile(file)
if (file.type.startsWith('image/')) {
const url = URL.createObjectURL(file)
setPreviewUrl(url)
} else {
setPreviewUrl(null)
}
}
}
return (
<Create
isLoading={formLoading}
saveButtonProps={{
...saveButtonProps,
onClick: handleSubmit((data) => {
const formData = new FormData()
formData.append('filename', data.filename)
formData.append('type', String(data.media_type))
const file = watch('file')
if (file) {
formData.append('file', file)
}
onFinish(formData)
}),
}}
>
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
<Box display="flex" flexDirection="column-reverse" alignItems="center" gap={6}>
<Box display="flex" alignItems="center" gap={2}>
<Button variant="contained" component="label">
{selectedFile ? 'Change File' : 'Upload File'}
<input type="file" hidden onChange={handleFileChange} />
</Button>
{selectedFile && (
<Typography variant="body2" color="text.secondary">
{selectedFile.name}
</Typography>
)}
</Box>
{previewUrl && (
<Box mt={2} display="flex" justifyContent="center">
<img src={previewUrl} alt="Preview" style={{maxWidth: '200px', borderRadius: 8}} />
</Box>
)}
</Box>
<TextField
{...register('filename', {
required: 'This field is required',
})}
error={!!(errors as any)?.filename}
helperText={(errors as any)?.filename?.message}
margin="normal"
fullWidth
InputLabelProps={{shrink: true}}
type="text"
label="File Name"
name="filename"
/>
<TextField
{...register('media_type', {
required: 'This field is required',
valueAsNumber: true,
})}
error={!!(errors as any)?.media_type}
helperText={(errors as any)?.media_type?.message}
margin="normal"
fullWidth
InputLabelProps={{shrink: true}}
type="number"
label="Media Type"
name="media_type"
/>
</Box>
</Create>
)
}

96
src/pages/media/edit.tsx Normal file
View File

@ -0,0 +1,96 @@
import {Box, TextField, Button, Typography} from '@mui/material'
import {Edit} from '@refinedev/mui'
import {useForm} from '@refinedev/react-hook-form'
import {useState, useEffect} from 'react'
import {useShow} from '@refinedev/core'
export const MediaEdit = () => {
const {
saveButtonProps,
register,
formState: {errors},
setValue,
} = useForm({})
const {query} = useShow()
const {data} = query
const record = data?.data
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [previewUrl, setPreviewUrl] = useState<string | null>(null)
useEffect(() => {
if (record?.id) {
setPreviewUrl(`https://wn.krbl.ru/media/${record.id}/download`)
}
}, [record])
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
setValue('file', file)
setSelectedFile(file)
if (file.type.startsWith('image/')) {
const url = URL.createObjectURL(file)
setPreviewUrl(url)
}
}
}
return (
<Edit saveButtonProps={saveButtonProps}>
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
<Box display="flex" flexDirection="column-reverse" alignItems="center" gap={6}>
<Box display="flex" alignItems="center" gap={2}>
<Button variant="contained" component="label">
{selectedFile ? 'Change File' : 'Upload File'}
<input type="file" hidden onChange={handleFileChange} />
</Button>
{selectedFile && (
<Typography variant="body2" color="text.secondary">
{selectedFile.name}
</Typography>
)}
</Box>
{previewUrl && (
<Box mt={2} display="flex" justifyContent="center">
<img src={previewUrl} alt="Preview" style={{maxWidth: '200px', borderRadius: 8}} />
</Box>
)}
</Box>
<TextField
{...register('filename', {
required: 'This field is required',
})}
error={!!(errors as any)?.filename}
helperText={(errors as any)?.filename?.message}
margin="normal"
fullWidth
InputLabelProps={{shrink: true}}
type="text"
label="File Name"
name="filename"
/>
<TextField
{...register('media_type', {
required: 'This field is required',
valueAsNumber: true,
})}
error={!!(errors as any)?.media_type}
helperText={(errors as any)?.media_type?.message}
margin="normal"
fullWidth
InputLabelProps={{shrink: true}}
type="number"
label="Media Type"
name="media_type"
/>
</Box>
</Edit>
)
}

4
src/pages/media/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from './create'
export * from './edit'
export * from './list'
export * from './show'

64
src/pages/media/list.tsx Normal file
View File

@ -0,0 +1,64 @@
import {DataGrid, type GridColDef} from '@mui/x-data-grid'
import {DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui'
import React from 'react'
export const MediaList = () => {
const {dataGridProps} = useDataGrid({})
const columns = React.useMemo<GridColDef[]>(
() => [
{
field: 'filename',
headerName: 'File name',
type: 'string',
minWidth: 250,
display: 'flex',
align: 'left',
headerAlign: 'left',
},
{
field: 'media_type',
headerName: 'Media Type',
type: 'number',
minWidth: 150,
display: 'flex',
align: 'left',
headerAlign: 'left',
},
{
field: 'id',
headerName: 'ID',
type: 'number',
display: 'flex',
align: 'left',
headerAlign: 'left',
flex: 1,
},
{
field: 'actions',
headerName: 'Actions',
align: 'right',
headerAlign: 'center',
minWidth: 130,
sortable: false,
display: 'flex',
renderCell: function render({row}) {
return (
<>
<EditButton hideText recordItemId={row.id} />
<ShowButton hideText recordItemId={row.id} />
<DeleteButton hideText recordItemId={row.id} />
</>
)
},
},
],
[],
)
return (
<List>
<DataGrid {...dataGridProps} columns={columns} getRowId={(row: any) => row.id} />
</List>
)
}

39
src/pages/media/show.tsx Normal file
View File

@ -0,0 +1,39 @@
import {Stack, Typography} from '@mui/material'
import {useShow} from '@refinedev/core'
import {Show, TextFieldComponent as TextField} from '@refinedev/mui'
export const MediaShow = () => {
const {query} = useShow({})
const {data, isLoading} = query
const record = data?.data
return (
<Show isLoading={isLoading}>
<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}} />}
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
{'File name'}
</Typography>
<TextField value={record?.filename} />
</Stack>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
{'Media Type'}
</Typography>
<TextField value={record?.media_type} />
</Stack>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
{'ID'}
</Typography>
<TextField value={record?.id} />
</Stack>
</Stack>
</Show>
)
}