import React, { useMemo } from 'react';
|
import { useInput, useTranslate } from 'react-admin';
|
import {
|
Box,
|
FormControl,
|
InputLabel,
|
MenuItem,
|
Select,
|
Stack,
|
TextField,
|
ToggleButton,
|
ToggleButtonGroup,
|
Typography,
|
Chip,
|
Skeleton,
|
} 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', waitForValue = false, ...rest }) => {
|
const translate = useTranslate();
|
const inputConfig = waitForValue
|
? { source, ...rest }
|
: { source, defaultValue: DEFAULT_CRON_EXPRESSION, ...rest };
|
const { field } = useInput(inputConfig);
|
const hasFieldValue = typeof field.value === 'string' && field.value.length > 0;
|
const parsedState = useMemo(() => {
|
if (!hasFieldValue) {
|
return waitForValue ? null : parseCronExpression(DEFAULT_CRON_EXPRESSION);
|
}
|
return parseCronExpression(field.value);
|
}, [field.value, hasFieldValue, waitForValue]);
|
|
const state = parsedState || DEFAULT_SCHEDULE_STATE;
|
|
const handleUpdate = (nextState) => {
|
const cron = buildCronExpression(nextState);
|
field.onChange(cron);
|
};
|
|
const handleModeChange = (event) => {
|
const mode = event.target.value;
|
handleUpdate({
|
...state,
|
mode,
|
weekDays:
|
mode === SCHEDULE_MODES.WEEKLY
|
? state.weekDays || DEFAULT_SCHEDULE_STATE.weekDays
|
: state.weekDays,
|
monthDays:
|
mode === SCHEDULE_MODES.MONTHLY
|
? state.monthDays || DEFAULT_SCHEDULE_STATE.monthDays
|
: state.monthDays,
|
});
|
};
|
|
const handleTimeChange = (event) => {
|
handleUpdate({
|
...state,
|
time: event.target.value,
|
});
|
};
|
|
const handleWeekDaysChange = (_, newDays) => {
|
if (!newDays.length) {
|
return;
|
}
|
handleUpdate({
|
...state,
|
weekDays: newDays,
|
});
|
};
|
|
const handleMonthDaysChange = (_, newDays) => {
|
handleUpdate({
|
...state,
|
monthDays: (newDays.length ? newDays : DEFAULT_SCHEDULE_STATE.monthDays),
|
});
|
};
|
|
const cronExpression = useMemo(() => buildCronExpression(state), [state]);
|
const description = useMemo(
|
() => describeCronExpression(cronExpression, translate),
|
[cronExpression, translate]
|
);
|
|
if (waitForValue && !hasFieldValue) {
|
return <Skeleton variant="rectangular" height={180} sx={{ borderRadius: 1 }} />;
|
}
|
|
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}
|
getOptionLabel={(option) => option?.toString?.() || `${option}`}
|
isOptionEqualToValue={(option, value) => Number(option) === Number(value)}
|
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;
|