add new props
for /carrier
route
This commit is contained in:
parent
230e7a9ada
commit
6db769f598
@ -12,10 +12,10 @@ interface CustomDataGridProps extends DataGridProps {
|
|||||||
resource?: string // Add this prop
|
resource?: string // Add this prop
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEV_FIELDS = ['id', 'code', 'country_code', 'city_id', 'carrier_id'] as const
|
const DEV_FIELDS = ['id', 'code', 'country_code', 'city_id', 'carrier_id', 'main_color', 'left_color', 'right_color', 'logo', 'slogan'] as const
|
||||||
|
|
||||||
export const CustomDataGrid = ({hasCoordinates = false, columns = [], resource, ...props}: CustomDataGridProps) => {
|
export const CustomDataGrid = ({hasCoordinates = false, columns = [], resource, ...props}: CustomDataGridProps) => {
|
||||||
const isDev = import.meta.env.DEV
|
// const isDev = import.meta.env.DEV
|
||||||
const {triggerExport, isLoading: exportLoading} = useExport({
|
const {triggerExport, isLoading: exportLoading} = useExport({
|
||||||
resource: resource ?? '',
|
resource: resource ?? '',
|
||||||
// pageSize: 100, #*
|
// pageSize: 100, #*
|
||||||
@ -23,10 +23,9 @@ export const CustomDataGrid = ({hasCoordinates = false, columns = [], resource,
|
|||||||
})
|
})
|
||||||
|
|
||||||
const initialShowCoordinates = Cookies.get('showCoordinates') === 'true'
|
const initialShowCoordinates = Cookies.get('showCoordinates') === 'true'
|
||||||
const initialShowDevData = Cookies.get('showDevData') === 'true'
|
const initialShowDevData = false // Default to false in both prod and dev
|
||||||
|
|
||||||
const [showCoordinates, setShowCoordinates] = useState(initialShowCoordinates)
|
const [showCoordinates, setShowCoordinates] = useState(initialShowCoordinates)
|
||||||
const [showDevData, setShowDevData] = useState(initialShowDevData)
|
const [showDevData, setShowDevData] = useState(Cookies.get('showDevData') === 'true')
|
||||||
|
|
||||||
const availableDevFields = useMemo(() => DEV_FIELDS.filter((field) => columns.some((column) => column.field === field)), [columns])
|
const availableDevFields = useMemo(() => DEV_FIELDS.filter((field) => columns.some((column) => column.field === field)), [columns])
|
||||||
|
|
||||||
@ -107,7 +106,7 @@ export const CustomDataGrid = ({hasCoordinates = false, columns = [], resource,
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDev && availableDevFields.length > 0 && (
|
{showDevData && availableDevFields.length > 0 && (
|
||||||
<Button variant="contained" onClick={toggleDevData}>
|
<Button variant="contained" onClick={toggleDevData}>
|
||||||
{showDevData ? 'Скрыть служебные данные' : 'Показать служебные данные'}
|
{showDevData ? 'Скрыть служебные данные' : 'Показать служебные данные'}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -23,6 +23,17 @@ export const CarrierCreate = () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const {autocompleteProps: mediaAutocompleteProps} = useAutocomplete({
|
||||||
|
resource: 'media',
|
||||||
|
onSearch: (value) => [
|
||||||
|
{
|
||||||
|
field: 'filename',
|
||||||
|
operator: 'contains',
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
||||||
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
||||||
@ -79,6 +90,111 @@ export const CarrierCreate = () => {
|
|||||||
label={'Короткое имя'}
|
label={'Короткое имя'}
|
||||||
name="short_name"
|
name="short_name"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...register('main_color', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.main_color}
|
||||||
|
helperText={(errors as any)?.main_color?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="color"
|
||||||
|
label={'Основной цвет'}
|
||||||
|
name="main_color"
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
height: '50px',
|
||||||
|
paddingBlock: '14px',
|
||||||
|
paddingInline: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...register('left_color', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.left_color}
|
||||||
|
helperText={(errors as any)?.left_color?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="color"
|
||||||
|
label={'Цвет левого виджета'}
|
||||||
|
name="left_color"
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
height: '50px',
|
||||||
|
paddingBlock: '14px',
|
||||||
|
paddingInline: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
{...register('right_color', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.right_color}
|
||||||
|
helperText={(errors as any)?.right_color?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="color"
|
||||||
|
label={'Цвет правого виджета'}
|
||||||
|
name="right_color"
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
height: '50px',
|
||||||
|
paddingBlock: '14px',
|
||||||
|
paddingInline: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...register('slogan', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.slogan}
|
||||||
|
helperText={(errors as any)?.slogan?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="text"
|
||||||
|
label={'Слоган'}
|
||||||
|
name="slogan"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="logo"
|
||||||
|
rules={{required: 'Это поле является обязательным'}}
|
||||||
|
defaultValue={null}
|
||||||
|
render={({field}) => (
|
||||||
|
<Autocomplete
|
||||||
|
{...mediaAutocompleteProps}
|
||||||
|
value={mediaAutocompleteProps.options.find((option) => option.id === field.value) || null}
|
||||||
|
onChange={(_, value) => {
|
||||||
|
field.onChange(value?.id || '')
|
||||||
|
}}
|
||||||
|
getOptionLabel={(item) => {
|
||||||
|
return item ? item.filename : ''
|
||||||
|
}}
|
||||||
|
isOptionEqualToValue={(option, value) => {
|
||||||
|
return option.id === value?.id
|
||||||
|
}}
|
||||||
|
filterOptions={(options, {inputValue}) => {
|
||||||
|
return options.filter((option) => option.filename.toLowerCase().includes(inputValue.toLowerCase()))
|
||||||
|
}}
|
||||||
|
renderInput={(params) => <TextField {...params} label="Выберите логотип" margin="normal" variant="outlined" error={!!errors.logo} helperText={(errors as any)?.logo?.message} required />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Create>
|
</Create>
|
||||||
)
|
)
|
||||||
|
@ -22,6 +22,17 @@ export const CarrierEdit = () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const {autocompleteProps: mediaAutocompleteProps} = useAutocomplete({
|
||||||
|
resource: 'media',
|
||||||
|
onSearch: (value) => [
|
||||||
|
{
|
||||||
|
field: 'filename',
|
||||||
|
operator: 'contains',
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Edit saveButtonProps={saveButtonProps}>
|
<Edit saveButtonProps={saveButtonProps}>
|
||||||
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
||||||
@ -78,6 +89,111 @@ export const CarrierEdit = () => {
|
|||||||
label={'Короткое имя'}
|
label={'Короткое имя'}
|
||||||
name="short_name"
|
name="short_name"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...register('main_color', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.main_color}
|
||||||
|
helperText={(errors as any)?.main_color?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="color"
|
||||||
|
label={'Основной цвет'}
|
||||||
|
name="main_color"
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
height: '50px',
|
||||||
|
paddingBlock: '14px',
|
||||||
|
paddingInline: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...register('left_color', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.left_color}
|
||||||
|
helperText={(errors as any)?.left_color?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="color"
|
||||||
|
label={'Цвет левого виджета'}
|
||||||
|
name="left_color"
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
height: '50px',
|
||||||
|
paddingBlock: '14px',
|
||||||
|
paddingInline: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
{...register('right_color', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.right_color}
|
||||||
|
helperText={(errors as any)?.right_color?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="color"
|
||||||
|
label={'Цвет правого виджета'}
|
||||||
|
name="right_color"
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
height: '50px',
|
||||||
|
paddingBlock: '14px',
|
||||||
|
paddingInline: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...register('slogan', {
|
||||||
|
required: 'Это поле является обязательным',
|
||||||
|
})}
|
||||||
|
error={!!(errors as any)?.slogan}
|
||||||
|
helperText={(errors as any)?.slogan?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{shrink: true}}
|
||||||
|
type="text"
|
||||||
|
label={'Слоган'}
|
||||||
|
name="slogan"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="logo"
|
||||||
|
rules={{required: 'Это поле является обязательным'}}
|
||||||
|
defaultValue={null}
|
||||||
|
render={({field}) => (
|
||||||
|
<Autocomplete
|
||||||
|
{...mediaAutocompleteProps}
|
||||||
|
value={mediaAutocompleteProps.options.find((option) => option.id === field.value) || null}
|
||||||
|
onChange={(_, value) => {
|
||||||
|
field.onChange(value?.id || '')
|
||||||
|
}}
|
||||||
|
getOptionLabel={(item) => {
|
||||||
|
return item ? item.filename : ''
|
||||||
|
}}
|
||||||
|
isOptionEqualToValue={(option, value) => {
|
||||||
|
return option.id === value?.id
|
||||||
|
}}
|
||||||
|
filterOptions={(options, {inputValue}) => {
|
||||||
|
return options.filter((option) => option.filename.toLowerCase().includes(inputValue.toLowerCase()))
|
||||||
|
}}
|
||||||
|
renderInput={(params) => <TextField {...params} label="Выберите логотип" margin="normal" variant="outlined" error={!!errors.logo} helperText={(errors as any)?.logo?.message} required />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Edit>
|
</Edit>
|
||||||
)
|
)
|
||||||
|
@ -34,17 +34,50 @@ export const CarrierList = () => {
|
|||||||
field: 'short_name',
|
field: 'short_name',
|
||||||
headerName: 'Короткое имя',
|
headerName: 'Короткое имя',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
minWidth: 150,
|
minWidth: 125,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'city',
|
field: 'city',
|
||||||
headerName: 'Город',
|
headerName: 'Город',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
minWidth: 150,
|
minWidth: 125,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'main_color',
|
||||||
|
headerName: 'Основной цвет',
|
||||||
|
type: 'string',
|
||||||
|
minWidth: 150,
|
||||||
|
renderCell: ({value}) => <div style={{display: 'grid', placeItems: 'center', width: '100%', height: '100%', backgroundColor: `${value}10`, borderRadius: 10}}>{value}</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'left_color',
|
||||||
|
headerName: 'Цвет левого виджета',
|
||||||
|
type: 'string',
|
||||||
|
minWidth: 150,
|
||||||
|
renderCell: ({value}) => <div style={{display: 'grid', placeItems: 'center', width: '100%', height: '100%', backgroundColor: `${value}10`, borderRadius: 10}}>{value}</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'right_color',
|
||||||
|
headerName: 'Цвет правого виджета',
|
||||||
|
type: 'string',
|
||||||
|
minWidth: 150,
|
||||||
|
renderCell: ({value}) => <div style={{display: 'grid', placeItems: 'center', width: '100%', height: '100%', backgroundColor: `${value}10`, borderRadius: 10}}>{value}</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'logo',
|
||||||
|
headerName: 'Лого',
|
||||||
|
type: 'string',
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'slogan',
|
||||||
|
headerName: 'Слоган',
|
||||||
|
type: 'string',
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
headerName: 'Действия',
|
headerName: 'Действия',
|
||||||
|
@ -1,30 +1,52 @@
|
|||||||
import {Stack, Typography} from '@mui/material'
|
import {Box, 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'
|
||||||
|
|
||||||
|
export type FieldType = {
|
||||||
|
label: string
|
||||||
|
data: any
|
||||||
|
render?: (value: any) => React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
export const CarrierShow = () => {
|
export const CarrierShow = () => {
|
||||||
const {query} = useShow({})
|
const {query} = useShow({})
|
||||||
const {data, isLoading} = query
|
const {data, isLoading} = query
|
||||||
|
|
||||||
const record = data?.data
|
const record = data?.data
|
||||||
|
|
||||||
const fields = [
|
const fields: FieldType[] = [
|
||||||
// {label: 'ID', data: 'id'},
|
|
||||||
// {label: 'ID города', data: 'city_id'},
|
|
||||||
{label: 'Полное имя', data: 'full_name'},
|
{label: 'Полное имя', data: 'full_name'},
|
||||||
{label: 'Короткое имя', data: 'short_name'},
|
{label: 'Короткое имя', data: 'short_name'},
|
||||||
{label: 'Город', data: 'city'},
|
{label: 'Город', data: 'city'},
|
||||||
|
{
|
||||||
|
label: 'Основной цвет',
|
||||||
|
data: 'main_color',
|
||||||
|
render: (value: string) => <Box sx={{display: 'grid', placeItems: 'center', width: 'fit-content', paddingInline: '6px', height: '100%', backgroundColor: `${value}20`, borderRadius: 1}}>{value}</Box>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Цвет левого виджета',
|
||||||
|
data: 'left_color',
|
||||||
|
render: (value: string) => <Box sx={{display: 'grid', placeItems: 'center', width: 'fit-content', paddingInline: '6px', height: '100%', backgroundColor: `${value}20`, borderRadius: 1}}>{value}</Box>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Цвет правого виджета',
|
||||||
|
data: 'right_color',
|
||||||
|
render: (value: string) => <Box sx={{display: 'grid', placeItems: 'center', width: 'fit-content', paddingInline: '6px', height: '100%', backgroundColor: `${value}20`, borderRadius: 1}}>{value}</Box>,
|
||||||
|
},
|
||||||
|
{label: 'Слоган', data: 'slogan'},
|
||||||
|
{label: 'Логотип', data: 'logo', render: (value: number) => <img src={`https://wn.krbl.ru/media/${value}/download`} alt={String(value)} style={{maxWidth: '10%', objectFit: 'contain', borderRadius: 8}} />},
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show isLoading={isLoading}>
|
<Show isLoading={isLoading}>
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
{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]} />
|
|
||||||
|
{render ? render(record?.[data]) : <TextField value={record?.[data]} />}
|
||||||
</Stack>
|
</Stack>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -12,6 +12,11 @@ export const RouteShow = () => {
|
|||||||
const fields = [
|
const fields = [
|
||||||
{label: 'Перевозчик', data: 'carrier'},
|
{label: 'Перевозчик', data: 'carrier'},
|
||||||
{label: 'Номер маршрута', data: 'route_number'},
|
{label: 'Номер маршрута', data: 'route_number'},
|
||||||
|
{
|
||||||
|
label: 'Направление маршрута',
|
||||||
|
data: 'route_direction',
|
||||||
|
render: (value: number[][]) => <Typography style={{color: value ? '#48989f' : '#7f6b58'}}>{value ? 'прямое' : 'обратное'}</Typography>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Координаты маршрута',
|
label: 'Координаты маршрута',
|
||||||
data: 'path',
|
data: 'path',
|
||||||
|
Loading…
Reference in New Issue
Block a user