| | |
| | | }, |
| | | }, |
| | | }, |
| | | guarantee: { |
| | | scope: { |
| | | helper: 'Select the scope for this plan; defaults to global coverage', |
| | | global: 'Global', |
| | | model: 'By model', |
| | | area: 'By area', |
| | | biz: 'By business type', |
| | | agv: 'Specific vehicles', |
| | | globalHelper: 'Applies to all vehicles', |
| | | globalTip: 'Select a scope type to limit the plan', |
| | | }, |
| | | schedule: { |
| | | label: 'Capacity window', |
| | | type: 'Frequency', |
| | | daily: 'Daily', |
| | | weekly: 'Weekly', |
| | | monthly: 'Monthly', |
| | | time: 'Execution time', |
| | | weeklyLabel: 'Weekdays', |
| | | monthlyLabel: 'Days of month', |
| | | monthHelper: 'Select at least one day', |
| | | monthDay: 'Day %{day}', |
| | | preview: 'Cron expression', |
| | | descriptionPrefix: 'Runs as %{desc}', |
| | | description: { |
| | | daily: 'every day at %{time}', |
| | | weekly: 'every week on %{days} at %{time}', |
| | | monthly: 'every month on %{days} at %{time}', |
| | | }, |
| | | }, |
| | | week: { |
| | | mon: 'Mon', |
| | | tue: 'Tue', |
| | | wed: 'Wed', |
| | | thu: 'Thu', |
| | | fri: 'Fri', |
| | | sat: 'Sat', |
| | | sun: 'Sun', |
| | | }, |
| | | }, |
| | | sta: { |
| | | depthHint: 'Set depth to 0 to keep the default value.', |
| | | enums: { |
| | |
| | | }, |
| | | }, |
| | | }, |
| | | guarantee: { |
| | | scope: { |
| | | helper: '选择作用范围,默认为全局生效', |
| | | global: '全局', |
| | | model: '按车型', |
| | | area: '按区域', |
| | | biz: '按业务类型', |
| | | agv: '指定车辆', |
| | | globalHelper: '全局适用', |
| | | globalTip: '如需限定,请先选择作用范围', |
| | | }, |
| | | schedule: { |
| | | label: '保障时间配置', |
| | | type: '执行频率', |
| | | daily: '每天', |
| | | weekly: '每周', |
| | | monthly: '每月', |
| | | time: '执行时间', |
| | | weeklyLabel: '执行星期', |
| | | monthlyLabel: '执行日', |
| | | monthHelper: '至少选择一个日期', |
| | | monthDay: '%{day} 日', |
| | | preview: 'Cron 表达式', |
| | | descriptionPrefix: '系统将在 %{desc} 触发', |
| | | description: { |
| | | daily: '每天 %{time}', |
| | | weekly: '每周 %{days} %{time}', |
| | | monthly: '每月 %{days} %{time}', |
| | | }, |
| | | }, |
| | | week: { |
| | | mon: '周一', |
| | | tue: '周二', |
| | | wed: '周三', |
| | | thu: '周四', |
| | | fri: '周五', |
| | | sat: '周六', |
| | | sun: '周日', |
| | | }, |
| | | }, |
| | | sta: { |
| | | depthHint: '深度设置为 0 表示沿用默认值。', |
| | | enums: { |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.action"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.actionSts"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.actionType"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.agv"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.agvDetail"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.agvModel"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.agvSts"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.area"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.bus"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.busSts"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.code"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.codeGap"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.config"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.funcSta"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| New file |
| | |
| | | import React, { useEffect, useMemo, useState } from 'react'; |
| | | import { useInput, useTranslate } from 'react-admin'; |
| | | import { |
| | | Box, |
| | | FormControl, |
| | | InputLabel, |
| | | MenuItem, |
| | | Select, |
| | | Stack, |
| | | TextField, |
| | | ToggleButton, |
| | | ToggleButtonGroup, |
| | | Typography, |
| | | Chip, |
| | | } from '@mui/material'; |
| | | import Autocomplete from '@mui/material/Autocomplete'; |
| | | import { |
| | | DEFAULT_CRON_EXPRESSION, |
| | | DEFAULT_SCHEDULE_STATE, |
| | | MONTH_DAY_OPTIONS, |
| | | SCHEDULE_MODES, |
| | | WEEK_DAYS, |
| | | buildCronExpression, |
| | | describeCronExpression, |
| | | getWeekdayLabel, |
| | | parseCronExpression, |
| | | } from './cronUtils'; |
| | | |
| | | const CronField = ({ source = 'cronExpr', ...rest }) => { |
| | | const translate = useTranslate(); |
| | | const { field } = useInput({ source, defaultValue: DEFAULT_CRON_EXPRESSION, ...rest }); |
| | | const [state, setState] = useState(() => parseCronExpression(field.value || DEFAULT_CRON_EXPRESSION)); |
| | | |
| | | useEffect(() => { |
| | | setState(parseCronExpression(field.value || DEFAULT_CRON_EXPRESSION)); |
| | | }, [field.value]); |
| | | |
| | | useEffect(() => { |
| | | const cron = buildCronExpression(state); |
| | | if (cron !== field.value) { |
| | | field.onChange(cron); |
| | | } |
| | | }, [state, field]); |
| | | |
| | | const handleModeChange = (event) => { |
| | | const mode = event.target.value; |
| | | setState((prev) => ({ |
| | | ...prev, |
| | | mode, |
| | | weekDays: mode === SCHEDULE_MODES.WEEKLY ? prev.weekDays || DEFAULT_SCHEDULE_STATE.weekDays : prev.weekDays, |
| | | monthDays: mode === SCHEDULE_MODES.MONTHLY ? prev.monthDays || DEFAULT_SCHEDULE_STATE.monthDays : prev.monthDays, |
| | | })); |
| | | }; |
| | | |
| | | const handleTimeChange = (event) => { |
| | | const newTime = event.target.value; |
| | | setState((prev) => ({ ...prev, time: newTime })); |
| | | }; |
| | | |
| | | const handleWeekDaysChange = (_, newDays) => { |
| | | if (!newDays.length) { |
| | | return; |
| | | } |
| | | setState((prev) => ({ ...prev, weekDays: newDays })); |
| | | }; |
| | | |
| | | const handleMonthDaysChange = (_, newDays) => { |
| | | if (!newDays.length) { |
| | | newDays = DEFAULT_SCHEDULE_STATE.monthDays; |
| | | } |
| | | setState((prev) => ({ ...prev, monthDays: newDays })); |
| | | }; |
| | | |
| | | const cronExpression = useMemo(() => buildCronExpression(state), [state]); |
| | | const description = useMemo(() => describeCronExpression(cronExpression, translate), [cronExpression, translate]); |
| | | |
| | | return ( |
| | | <Stack spacing={2} sx={{ width: '100%' }}> |
| | | <Typography variant="subtitle1">{translate('page.guarantee.schedule.label')}</Typography> |
| | | <FormControl fullWidth> |
| | | <InputLabel>{translate('page.guarantee.schedule.type')}</InputLabel> |
| | | <Select |
| | | label={translate('page.guarantee.schedule.type')} |
| | | value={state.mode || SCHEDULE_MODES.DAILY} |
| | | onChange={handleModeChange} |
| | | > |
| | | <MenuItem value={SCHEDULE_MODES.DAILY}>{translate('page.guarantee.schedule.daily')}</MenuItem> |
| | | <MenuItem value={SCHEDULE_MODES.WEEKLY}>{translate('page.guarantee.schedule.weekly')}</MenuItem> |
| | | <MenuItem value={SCHEDULE_MODES.MONTHLY}>{translate('page.guarantee.schedule.monthly')}</MenuItem> |
| | | </Select> |
| | | </FormControl> |
| | | <TextField |
| | | label={translate('page.guarantee.schedule.time')} |
| | | type="time" |
| | | value={state.time || DEFAULT_SCHEDULE_STATE.time} |
| | | onChange={handleTimeChange} |
| | | inputProps={{ step: 300 }} |
| | | /> |
| | | {state.mode === SCHEDULE_MODES.WEEKLY && ( |
| | | <Box> |
| | | <Typography variant="body2" gutterBottom> |
| | | {translate('page.guarantee.schedule.weeklyLabel')} |
| | | </Typography> |
| | | <ToggleButtonGroup value={state.weekDays || []} onChange={handleWeekDaysChange} size="small"> |
| | | {WEEK_DAYS.map((day) => ( |
| | | <ToggleButton key={day} value={day} aria-label={day} sx={{ textTransform: 'none' }}> |
| | | {getWeekdayLabel(day, translate)} |
| | | </ToggleButton> |
| | | ))} |
| | | </ToggleButtonGroup> |
| | | </Box> |
| | | )} |
| | | {state.mode === SCHEDULE_MODES.MONTHLY && ( |
| | | <Autocomplete |
| | | multiple |
| | | options={MONTH_DAY_OPTIONS} |
| | | value={state.monthDays || []} |
| | | onChange={handleMonthDaysChange} |
| | | renderTags={(value, getTagProps) => |
| | | value.map((option, index) => ( |
| | | <Chip |
| | | variant="outlined" |
| | | label={translate('page.guarantee.schedule.monthDay', { day: option })} |
| | | {...getTagProps({ index })} |
| | | key={`month-day-${option}`} |
| | | /> |
| | | )) |
| | | } |
| | | renderInput={(params) => ( |
| | | <TextField |
| | | {...params} |
| | | label={translate('page.guarantee.schedule.monthlyLabel')} |
| | | helperText={translate('page.guarantee.schedule.monthHelper')} |
| | | /> |
| | | )} |
| | | /> |
| | | )} |
| | | <TextField |
| | | label={translate('page.guarantee.schedule.preview')} |
| | | value={cronExpression} |
| | | InputProps={{ readOnly: true }} |
| | | /> |
| | | <Typography variant="caption" color="text.secondary"> |
| | | {translate('page.guarantee.schedule.descriptionPrefix', { desc: description })} |
| | | </Typography> |
| | | </Stack> |
| | | ); |
| | | }; |
| | | |
| | | export default CronField; |
| | |
| | | useTranslate, |
| | | TextInput, |
| | | NumberInput, |
| | | BooleanInput, |
| | | DateInput, |
| | | SaveButton, |
| | | SelectInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | Toolbar, |
| | | required, |
| | | useDataProvider, |
| | | useNotify, |
| | | Form, |
| | | useCreateController, |
| | | } from 'react-admin'; |
| | | import { |
| | | Dialog, |
| | |
| | | import DialogCloseButton from "../components/DialogCloseButton"; |
| | | import StatusSelectInput from "../components/StatusSelectInput"; |
| | | import MemoInput from "../components/MemoInput"; |
| | | import ScopeField from "./ScopeField"; |
| | | import CronField from "./CronField"; |
| | | import { DEFAULT_CRON_EXPRESSION } from "./cronUtils"; |
| | | import { DEFAULT_SCOPE_TYPE } from "./guaranteeConstants"; |
| | | |
| | | const GuaranteeCreate = (props) => { |
| | | const { open, setOpen } = props; |
| | | |
| | | const translate = useTranslate(); |
| | | const notify = useNotify(); |
| | | |
| | | const defaultRecord = { |
| | | scopeType: DEFAULT_SCOPE_TYPE, |
| | | cronExpr: DEFAULT_CRON_EXPRESSION, |
| | | requiredCount: 10, |
| | | minSoc: 50, |
| | | leadTime: 60, |
| | | status: 1, |
| | | }; |
| | | |
| | | const handleClose = (event, reason) => { |
| | | if (reason !== "backdropClick") { |
| | |
| | | return ( |
| | | <> |
| | | <CreateBase |
| | | record={{}} |
| | | record={defaultRecord} |
| | | transform={(data) => { |
| | | return data; |
| | | return { |
| | | ...data, |
| | | cronExpr: (data.cronExpr || '').trim(), |
| | | scopeValue: data.scopeType === DEFAULT_SCOPE_TYPE ? null : data.scopeValue, |
| | | }; |
| | | }} |
| | | mutationOptions={{ onSuccess: handleSuccess, onError: handleError }} |
| | | > |
| | |
| | | label="table.field.guarantee.name" |
| | | source="name" |
| | | parse={v => v} |
| | | validate={[required()]} |
| | | fullWidth |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.guarantee.scopeType" |
| | | source="scopeType" |
| | | parse={v => v} |
| | | /> |
| | | <Grid item xs={12}> |
| | | <ScopeField /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.guarantee.scopeValue" |
| | | source="scopeValue" |
| | | parse={v => v} |
| | | /> |
| | | <Grid item xs={12}> |
| | | <CronField /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | | label="table.field.guarantee.cronExpr" |
| | | source="cronExpr" |
| | | parse={v => v} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <Grid item xs={4} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.guarantee.requiredCount" |
| | | source="requiredCount" |
| | | validate={[required()]} |
| | | min={1} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <Grid item xs={4} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.guarantee.minSoc" |
| | | source="minSoc" |
| | | validate={[required()]} |
| | | min={1} |
| | | max={100} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <Grid item xs={4} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.guarantee.leadTime" |
| | | source="leadTime" |
| | | validate={[required()]} |
| | | min={1} |
| | | /> |
| | | </Grid> |
| | | |
| | |
| | | import { |
| | | Edit, |
| | | SimpleForm, |
| | | FormDataConsumer, |
| | | useTranslate, |
| | | TextInput, |
| | | NumberInput, |
| | | BooleanInput, |
| | | DateInput, |
| | | SelectInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | SaveButton, |
| | | Toolbar, |
| | | Labeled, |
| | | NumberField, |
| | | required, |
| | | useRecordContext, |
| | | DeleteButton, |
| | | } from 'react-admin'; |
| | | import { useWatch, useFormContext } from "react-hook-form"; |
| | | import { Stack, Grid, Box, Typography } from '@mui/material'; |
| | | import * as Common from '@/utils/common'; |
| | | import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting'; |
| | | import { EDIT_MODE } from '@/config/setting'; |
| | | import EditBaseAside from "../components/EditBaseAside"; |
| | | import CustomerTopToolBar from "../components/EditTopToolBar"; |
| | | import MemoInput from "../components/MemoInput"; |
| | | import StatusSelectInput from "../components/StatusSelectInput"; |
| | | import ScopeField from "./ScopeField"; |
| | | import CronField from "./CronField"; |
| | | import { DEFAULT_SCOPE_TYPE } from "./guaranteeConstants"; |
| | | |
| | | const FormToolbar = () => { |
| | | const { getValues } = useFormContext(); |
| | | |
| | | return ( |
| | | <Toolbar sx={{ justifyContent: 'space-between' }}> |
| | | <SaveButton /> |
| | |
| | | label="table.field.guarantee.name" |
| | | source="name" |
| | | parse={v => v} |
| | | validate={[required()]} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.guarantee.scopeType" |
| | | source="scopeType" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.guarantee.scopeValue" |
| | | source="scopeValue" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.guarantee.cronExpr" |
| | | source="cronExpr" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> |
| | | <ScopeField /> |
| | | <Box mt={2}> |
| | | <CronField /> |
| | | </Box> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.guarantee.requiredCount" |
| | | source="requiredCount" |
| | | validate={[required()]} |
| | | min={1} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.guarantee.minSoc" |
| | | source="minSoc" |
| | | validate={[required()]} |
| | | min={1} |
| | | max={100} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.guarantee.leadTime" |
| | | source="leadTime" |
| | | validate={[required()]} |
| | | min={1} |
| | | /> |
| | | </Stack> |
| | | |
| | |
| | | ) |
| | | } |
| | | |
| | | export default GuaranteeEdit; |
| | | const GuaranteeEditWrapper = () => ( |
| | | <Edit |
| | | redirect="list" |
| | | mutationMode={EDIT_MODE} |
| | | actions={<CustomerTopToolBar />} |
| | | aside={<EditBaseAside />} |
| | | transform={(data) => ({ |
| | | ...data, |
| | | cronExpr: (data.cronExpr || '').trim(), |
| | | scopeValue: data.scopeType === DEFAULT_SCOPE_TYPE ? null : data.scopeValue, |
| | | })} |
| | | > |
| | | <GuaranteeEdit /> |
| | | </Edit> |
| | | ); |
| | | |
| | | export default GuaranteeEditWrapper; |
| | |
| | | import React, { useState, useRef, useEffect, useMemo, useCallback } from "react"; |
| | | import { useNavigate } from 'react-router-dom'; |
| | | import React, { useState, useCallback } from "react"; |
| | | import { |
| | | List, |
| | | DatagridConfigurable, |
| | | SearchInput, |
| | | TopToolbar, |
| | | SelectColumnsButton, |
| | | EditButton, |
| | | FilterButton, |
| | | CreateButton, |
| | | ExportButton, |
| | | BulkDeleteButton, |
| | | WrapperField, |
| | | useRecordContext, |
| | | useTranslate, |
| | | useNotify, |
| | | useListContext, |
| | | FunctionField, |
| | | TextField, |
| | | NumberField, |
| | |
| | | DateInput, |
| | | SelectInput, |
| | | NumberInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | DeleteButton, |
| | | EditButton, |
| | | } from 'react-admin'; |
| | | import { Box, Typography, Card, Stack } from '@mui/material'; |
| | | import { styled } from '@mui/material/styles'; |
| | |
| | | import MyCreateButton from "../components/MyCreateButton"; |
| | | import MyExportButton from '../components/MyExportButton'; |
| | | import PageDrawer from "../components/PageDrawer"; |
| | | import MyField from "../components/MyField"; |
| | | import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting'; |
| | | import * as Common from '@/utils/common'; |
| | | import { describeCronExpression } from "./cronUtils"; |
| | | import { DEFAULT_SCOPE_TYPE, SCOPE_FILTER_CHOICES, getScopeLabel } from "./guaranteeConstants"; |
| | | |
| | | const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({ |
| | | '& .css-1vooibu-MuiSvgIcon-root': { |
| | |
| | | |
| | | <TextInput source="uuid" label="table.field.guarantee.uuid" />, |
| | | <TextInput source="name" label="table.field.guarantee.name" />, |
| | | <TextInput source="scopeType" label="table.field.guarantee.scopeType" />, |
| | | <SelectInput |
| | | source="scopeType" |
| | | label="table.field.guarantee.scopeType" |
| | | choices={SCOPE_FILTER_CHOICES} |
| | | translateChoice |
| | | allowEmpty |
| | | resettable |
| | | />, |
| | | <TextInput source="scopeValue" label="table.field.guarantee.scopeValue" />, |
| | | <TextInput source="cronExpr" label="table.field.guarantee.cronExpr" />, |
| | | <NumberInput source="requiredCount" label="table.field.guarantee.requiredCount" />, |
| | |
| | | const [createDialog, setCreateDialog] = useState(false); |
| | | const [drawerVal, setDrawerVal] = useState(false); |
| | | |
| | | const renderScopeType = useCallback((record) => { |
| | | if (!record) { |
| | | return ''; |
| | | } |
| | | return getScopeLabel(record.scopeType, translate); |
| | | }, [translate]); |
| | | |
| | | const renderScopeValue = useCallback((record) => { |
| | | if (!record) { |
| | | return ''; |
| | | } |
| | | if (!record.scopeValue) { |
| | | return record.scopeType === DEFAULT_SCOPE_TYPE |
| | | ? translate('page.guarantee.scope.globalHelper') |
| | | : translate('common.enums.na'); |
| | | } |
| | | return record.scopeValue; |
| | | }, [translate]); |
| | | |
| | | const renderCron = useCallback((record) => { |
| | | if (!record || !record.cronExpr) { |
| | | return translate('common.enums.na'); |
| | | } |
| | | return describeCronExpression(record.cronExpr, translate); |
| | | }, [translate]); |
| | | |
| | | return ( |
| | | <Box display="flex"> |
| | | <List |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.guarantee"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | <NumberField source="id" /> |
| | | <TextField source="uuid" label="table.field.guarantee.uuid" /> |
| | | <TextField source="name" label="table.field.guarantee.name" /> |
| | | <TextField source="scopeType" label="table.field.guarantee.scopeType" /> |
| | | <TextField source="scopeValue" label="table.field.guarantee.scopeValue" /> |
| | | <TextField source="cronExpr" label="table.field.guarantee.cronExpr" /> |
| | | <FunctionField label="table.field.guarantee.scopeType" render={renderScopeType} /> |
| | | <FunctionField label="table.field.guarantee.scopeValue" render={renderScopeValue} /> |
| | | <FunctionField label="table.field.guarantee.cronExpr" render={renderCron} /> |
| | | <NumberField source="requiredCount" label="table.field.guarantee.requiredCount" /> |
| | | <NumberField source="minSoc" label="table.field.guarantee.minSoc" /> |
| | | <NumberField source="leadTime" label="table.field.guarantee.leadTime" /> |
| | |
| | | } from 'react-admin'; |
| | | import PanelTypography from "../components/PanelTypography"; |
| | | import * as Common from '@/utils/common' |
| | | import { describeCronExpression } from "./cronUtils"; |
| | | import { DEFAULT_SCOPE_TYPE, getScopeLabel } from "./guaranteeConstants"; |
| | | |
| | | const GuaranteePanel = () => { |
| | | const record = useRecordContext(); |
| | | if (!record) return null; |
| | | const translate = useTranslate(); |
| | | const scopeTypeLabel = getScopeLabel(record.scopeType, translate); |
| | | const scopeValueLabel = record.scopeValue |
| | | ? record.scopeValue |
| | | : record.scopeType === DEFAULT_SCOPE_TYPE |
| | | ? translate('page.guarantee.scope.globalHelper') |
| | | : translate('common.enums.na'); |
| | | const cronDesc = record.cronExpr |
| | | ? `${describeCronExpression(record.cronExpr, translate)} (${record.cronExpr})` |
| | | : translate('common.enums.na'); |
| | | |
| | | return ( |
| | | <> |
| | | <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}> |
| | |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.guarantee.scopeType" |
| | | property={record.scopeType} |
| | | property={scopeTypeLabel} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.guarantee.scopeValue" |
| | | property={record.scopeValue} |
| | | property={scopeValueLabel} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.guarantee.cronExpr" |
| | | property={record.cronExpr} |
| | | property={cronDesc} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| New file |
| | |
| | | import React from 'react'; |
| | | import { Stack, TextField } from '@mui/material'; |
| | | import { |
| | | AutocompleteInput, |
| | | FormDataConsumer, |
| | | ReferenceInput, |
| | | SelectInput, |
| | | required, |
| | | useTranslate, |
| | | } from 'react-admin'; |
| | | import { REFERENCE_INPUT_PAGESIZE } from '@/config/setting'; |
| | | import { DEFAULT_SCOPE_TYPE, SCOPE_OPTIONS, getScopeConfig } from './guaranteeConstants'; |
| | | |
| | | const ScopeField = () => { |
| | | const translate = useTranslate(); |
| | | |
| | | return ( |
| | | <Stack spacing={2} sx={{ width: '100%' }}> |
| | | <SelectInput |
| | | label="table.field.guarantee.scopeType" |
| | | source="scopeType" |
| | | choices={SCOPE_OPTIONS} |
| | | optionText={(choice) => translate(choice.labelKey)} |
| | | optionValue="id" |
| | | translateChoice={false} |
| | | defaultValue={DEFAULT_SCOPE_TYPE} |
| | | helperText={translate('page.guarantee.scope.helper')} |
| | | /> |
| | | <FormDataConsumer> |
| | | {({ formData, ...rest }) => { |
| | | const currentType = formData.scopeType || DEFAULT_SCOPE_TYPE; |
| | | const config = getScopeConfig(currentType); |
| | | |
| | | if (config.valueType === 'reference') { |
| | | return ( |
| | | <ReferenceInput |
| | | key={currentType} |
| | | source="scopeValue" |
| | | reference={config.reference} |
| | | perPage={REFERENCE_INPUT_PAGESIZE} |
| | | {...rest} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.guarantee.scopeValue" |
| | | optionText={config.optionText} |
| | | optionValue={config.optionValue || 'id'} |
| | | filterToQuery={config.filterToQuery} |
| | | validate={[required()]} |
| | | fullWidth |
| | | /> |
| | | </ReferenceInput> |
| | | ); |
| | | } |
| | | |
| | | return ( |
| | | <TextField |
| | | label={translate('table.field.guarantee.scopeValue')} |
| | | value={translate('page.guarantee.scope.globalHelper')} |
| | | helperText={translate('page.guarantee.scope.globalTip')} |
| | | disabled |
| | | fullWidth |
| | | /> |
| | | ); |
| | | }} |
| | | </FormDataConsumer> |
| | | </Stack> |
| | | ); |
| | | }; |
| | | |
| | | export default ScopeField; |
| New file |
| | |
| | | export const SCHEDULE_MODES = { |
| | | DAILY: 'DAILY', |
| | | WEEKLY: 'WEEKLY', |
| | | MONTHLY: 'MONTHLY', |
| | | }; |
| | | |
| | | export const WEEK_DAYS = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']; |
| | | export const MONTH_DAY_OPTIONS = Array.from({ length: 31 }, (_, idx) => idx + 1); |
| | | |
| | | const DEFAULT_TIME = '10:00'; |
| | | const pad = (value) => value.toString().padStart(2, '0'); |
| | | |
| | | const sanitizeWeekDays = (days) => { |
| | | if (!Array.isArray(days) || days.length === 0) { |
| | | return ['MON']; |
| | | } |
| | | const unique = [...new Set(days.filter((day) => WEEK_DAYS.includes(day)))]; |
| | | return unique.length ? unique : ['MON']; |
| | | }; |
| | | |
| | | const sanitizeMonthDays = (days) => { |
| | | if (!Array.isArray(days) || days.length === 0) { |
| | | return [1]; |
| | | } |
| | | const unique = [...new Set(days.map((day) => Number(day)).filter((day) => day >= 1 && day <= 31))]; |
| | | return unique.length ? unique : [1]; |
| | | }; |
| | | |
| | | const sanitizeTime = (time = DEFAULT_TIME) => { |
| | | if (typeof time !== 'string') { |
| | | return DEFAULT_TIME; |
| | | } |
| | | const [hour = '10', minute = '00'] = time.split(':'); |
| | | const parsedHour = Math.min(23, Math.max(0, parseInt(hour, 10) || 0)); |
| | | const parsedMinute = Math.min(59, Math.max(0, parseInt(minute, 10) || 0)); |
| | | return `${pad(parsedHour)}:${pad(parsedMinute)}`; |
| | | }; |
| | | |
| | | export const DEFAULT_SCHEDULE_STATE = { |
| | | mode: SCHEDULE_MODES.DAILY, |
| | | time: DEFAULT_TIME, |
| | | weekDays: ['MON'], |
| | | monthDays: [1], |
| | | }; |
| | | |
| | | export const buildCronExpression = (state = DEFAULT_SCHEDULE_STATE) => { |
| | | const mode = state?.mode || SCHEDULE_MODES.DAILY; |
| | | const time = sanitizeTime(state?.time); |
| | | const [hour, minute] = time.split(':'); |
| | | |
| | | if (mode === SCHEDULE_MODES.WEEKLY) { |
| | | const days = sanitizeWeekDays(state?.weekDays).join(','); |
| | | return `0 ${minute} ${hour} ? * ${days}`; |
| | | } |
| | | |
| | | if (mode === SCHEDULE_MODES.MONTHLY) { |
| | | const days = sanitizeMonthDays(state?.monthDays).sort((a, b) => a - b).join(','); |
| | | return `0 ${minute} ${hour} ${days} * ?`; |
| | | } |
| | | |
| | | return `0 ${minute} ${hour} * * ?`; |
| | | }; |
| | | |
| | | export const DEFAULT_CRON_EXPRESSION = buildCronExpression(DEFAULT_SCHEDULE_STATE); |
| | | |
| | | const createParsedState = () => ({ |
| | | ...DEFAULT_SCHEDULE_STATE, |
| | | valid: false, |
| | | }); |
| | | |
| | | export const parseCronExpression = (expr) => { |
| | | const state = createParsedState(); |
| | | if (!expr || typeof expr !== 'string') { |
| | | return state; |
| | | } |
| | | const tokens = expr.trim().split(/\s+/); |
| | | if (tokens.length < 6) { |
| | | return state; |
| | | } |
| | | const [, minute, hour, dayOfMonth, , dayOfWeek] = tokens; |
| | | const time = sanitizeTime(`${hour}:${minute}`); |
| | | |
| | | if (dayOfMonth === '*' && dayOfWeek === '?') { |
| | | return { |
| | | ...state, |
| | | valid: true, |
| | | mode: SCHEDULE_MODES.DAILY, |
| | | time, |
| | | }; |
| | | } |
| | | |
| | | if (dayOfMonth === '?' && dayOfWeek && dayOfWeek !== '?') { |
| | | const weekDays = dayOfWeek.split(',').filter((day) => WEEK_DAYS.includes(day)); |
| | | return { |
| | | ...state, |
| | | valid: true, |
| | | mode: SCHEDULE_MODES.WEEKLY, |
| | | time, |
| | | weekDays: sanitizeWeekDays(weekDays), |
| | | }; |
| | | } |
| | | |
| | | if (dayOfMonth && dayOfMonth !== '*' && dayOfWeek === '?') { |
| | | const monthDays = dayOfMonth |
| | | .split(',') |
| | | .map((day) => parseInt(day, 10)) |
| | | .filter((day) => !Number.isNaN(day)); |
| | | return { |
| | | ...state, |
| | | valid: true, |
| | | mode: SCHEDULE_MODES.MONTHLY, |
| | | time, |
| | | monthDays: sanitizeMonthDays(monthDays), |
| | | }; |
| | | } |
| | | |
| | | return state; |
| | | }; |
| | | |
| | | export const getWeekdayLabel = (day, translate) => { |
| | | const key = `page.guarantee.week.${day?.toLowerCase?.()}`; |
| | | return translate ? translate(key) : day; |
| | | }; |
| | | |
| | | const formatWeekdays = (days, translate) => { |
| | | const normalized = sanitizeWeekDays(days); |
| | | return normalized.map((day) => getWeekdayLabel(day, translate)).join(' / '); |
| | | }; |
| | | |
| | | const formatMonthDays = (days, translate) => { |
| | | const normalized = sanitizeMonthDays(days).sort((a, b) => a - b); |
| | | return normalized |
| | | .map((day) => (translate ? translate('page.guarantee.schedule.monthDay', { day }) : `${day}`)) |
| | | .join(' / '); |
| | | }; |
| | | |
| | | export const describeCronExpression = (expr, translate) => { |
| | | if (!expr) { |
| | | return ''; |
| | | } |
| | | const parsed = parseCronExpression(expr); |
| | | const time = parsed.time || DEFAULT_TIME; |
| | | |
| | | if (!parsed.valid) { |
| | | return expr; |
| | | } |
| | | |
| | | switch (parsed.mode) { |
| | | case SCHEDULE_MODES.WEEKLY: |
| | | return translate |
| | | ? translate('page.guarantee.schedule.description.weekly', { |
| | | days: formatWeekdays(parsed.weekDays, translate), |
| | | time, |
| | | }) |
| | | : `Weekly ${formatWeekdays(parsed.weekDays)} ${time}`; |
| | | case SCHEDULE_MODES.MONTHLY: |
| | | return translate |
| | | ? translate('page.guarantee.schedule.description.monthly', { |
| | | days: formatMonthDays(parsed.monthDays, translate), |
| | | time, |
| | | }) |
| | | : `Monthly ${formatMonthDays(parsed.monthDays)} ${time}`; |
| | | default: |
| | | return translate |
| | | ? translate('page.guarantee.schedule.description.daily', { time }) |
| | | : `Daily ${time}`; |
| | | } |
| | | }; |
| New file |
| | |
| | | const buildFilter = (field) => (value) => ({ [field]: value }); |
| | | |
| | | export const SCOPE_CONFIGS = { |
| | | GLOBAL: { |
| | | id: 'GLOBAL', |
| | | labelKey: 'page.guarantee.scope.global', |
| | | valueType: 'none', |
| | | }, |
| | | MODEL: { |
| | | id: 'MODEL', |
| | | labelKey: 'page.guarantee.scope.model', |
| | | valueType: 'reference', |
| | | reference: 'agvModel', |
| | | optionText: 'name', |
| | | optionValue: 'id', |
| | | filterToQuery: buildFilter('name'), |
| | | }, |
| | | AREA: { |
| | | id: 'AREA', |
| | | labelKey: 'page.guarantee.scope.area', |
| | | valueType: 'reference', |
| | | reference: 'area', |
| | | optionText: 'name', |
| | | optionValue: 'id', |
| | | filterToQuery: buildFilter('name'), |
| | | }, |
| | | BIZ: { |
| | | id: 'BIZ', |
| | | labelKey: 'page.guarantee.scope.biz', |
| | | valueType: 'reference', |
| | | reference: 'taskType', |
| | | optionText: 'name', |
| | | optionValue: 'id', |
| | | filterToQuery: buildFilter('name'), |
| | | }, |
| | | AGV: { |
| | | id: 'AGV', |
| | | labelKey: 'page.guarantee.scope.agv', |
| | | valueType: 'reference', |
| | | reference: 'agv', |
| | | optionText: 'uuid', |
| | | optionValue: 'id', |
| | | filterToQuery: buildFilter('uuid'), |
| | | }, |
| | | }; |
| | | |
| | | export const DEFAULT_SCOPE_TYPE = SCOPE_CONFIGS.GLOBAL.id; |
| | | |
| | | export const SCOPE_OPTIONS = Object.values(SCOPE_CONFIGS); |
| | | |
| | | export const SCOPE_FILTER_CHOICES = SCOPE_OPTIONS.map(({ id, labelKey }) => ({ |
| | | id, |
| | | name: labelKey, |
| | | })); |
| | | |
| | | export const getScopeConfig = (scopeType) => SCOPE_CONFIGS[scopeType] || SCOPE_CONFIGS.GLOBAL; |
| | | |
| | | export const getScopeLabelKey = (scopeType) => getScopeConfig(scopeType).labelKey; |
| | | |
| | | export const getScopeLabel = (scopeType, translate) => { |
| | | const key = getScopeLabelKey(scopeType); |
| | | return translate ? translate(key) : key; |
| | | }; |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.host"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.integrationRecord"} |
| | | empty={<EmptyDataLoader />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.jam"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.lane"} |
| | | empty={<EmptyDataLoader />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.loc"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.locSts"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.locType"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | }} |
| | | > |
| | | <DialogContent sx={{ padding: 0 }}> |
| | | {!!id ? ( |
| | | {id ? ( |
| | | <ShowBase id={id}> |
| | | <MissionResendContent handleClose={handleClose} /> |
| | | </ShowBase> |
| | |
| | | }; |
| | | |
| | | const resendSubmit = () => { |
| | | if (!!selected?.length) { |
| | | if (selected?.length) { |
| | | request.post("/mission/resend", selected.map(id => ({ id }))).then(res => { |
| | | const { code, msg, data } = res.data; |
| | | if (code === 200) { |
| | |
| | | }} |
| | | > |
| | | <DialogContent sx={{ padding: 0 }}> |
| | | {!!id ? ( |
| | | {id ? ( |
| | | <ShowBase id={id}> |
| | | <MissionShowContent handleClose={handleClose} /> |
| | | </ShowBase> |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.operation"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.role"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | <PageDrawer |
| | | drawerVal={drawerVal} |
| | | setDrawerVal={setDrawerVal} |
| | | title={!!drawerVal ? `Scope by ${drawerVal.code || drawerVal.name}` : 'Role Detail'} |
| | | title={drawerVal ? `Scope by ${drawerVal.code || drawerVal.name}` : 'Role Detail'} |
| | | closeCallback={() => { |
| | | setMenuIds([]); |
| | | }} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.route"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.segment"} |
| | | empty={<EmptyData />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.sta"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.staReserve"} |
| | | empty={<EmptyDataLoader />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.staSts"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.staType"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.task"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.taskSts"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.taskType"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.tenant"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.travel"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.user"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.userLogin"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.vehFaultRec"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | |
| | | theme.transitions.create(['all'], { |
| | | duration: theme.transitions.duration.enteringScreen, |
| | | }), |
| | | marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0, |
| | | }} |
| | | title={"menu.zone"} |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |