init /media
route
This commit is contained in:
parent
deaf5d68bb
commit
a19d0f9478
18
src/App.tsx
18
src/App.tsx
@ -22,6 +22,7 @@ import {authProvider} from './authProvider'
|
|||||||
import {CountryList, CountryCreate, CountryEdit} from './pages/country'
|
import {CountryList, CountryCreate, CountryEdit} from './pages/country'
|
||||||
import {CityList, CityCreate, CityEdit} from './pages/city'
|
import {CityList, CityCreate, CityEdit} from './pages/city'
|
||||||
import {CarrierList, CarrierCreate, CarrierEdit} from './pages/carrier'
|
import {CarrierList, CarrierCreate, CarrierEdit} from './pages/carrier'
|
||||||
|
import {MediaList, MediaCreate, MediaEdit, MediaShow} from './pages/media'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@ -86,6 +87,16 @@ function App() {
|
|||||||
canDelete: true,
|
canDelete: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'media',
|
||||||
|
list: '/media',
|
||||||
|
create: '/media/create',
|
||||||
|
edit: '/media/edit/:id',
|
||||||
|
show: '/media/show/:id',
|
||||||
|
meta: {
|
||||||
|
canDelete: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
options={{
|
options={{
|
||||||
syncWithLocation: true,
|
syncWithLocation: true,
|
||||||
@ -136,6 +147,13 @@ function App() {
|
|||||||
<Route path="edit/:id" element={<CarrierEdit />} />
|
<Route path="edit/:id" element={<CarrierEdit />} />
|
||||||
</Route>
|
</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 path="*" element={<ErrorComponent />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
|
108
src/pages/media/create.tsx
Normal file
108
src/pages/media/create.tsx
Normal 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
96
src/pages/media/edit.tsx
Normal 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
4
src/pages/media/index.ts
Normal 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
64
src/pages/media/list.tsx
Normal 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
39
src/pages/media/show.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user