#
vincentlu
2025-02-11 2d1b39fae6abed7cda7bd5722fcea23fefdb6e12
rsf-admin/src/page/tenant/TenantCreate.jsx
@@ -1,19 +1,7 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
import {
    Dialog,
@@ -23,92 +11,358 @@
    Stack,
    Grid,
    Box,
    Stepper,
    Step,
    StepLabel,
    StepContent,
    Button,
    TextField,
    InputAdornment,
    IconButton,
} from '@mui/material';
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
import { matchPath, useLocation } from 'react-router';
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import request from '@/utils/request';
const TenantCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const {
        control,
        handleSubmit,
        watch,
        setValue,
        getValues,
        reset,
        formState: {
            errors,
            isDirty,
        },
        trigger
    } = useForm();
    const passwordVal = watch('password');
    const [activeStep, setActiveStep] = useState(0);
    const [showPassword, setShowPassword] = useState(false);
    const validateCurrentStep = async () => {
        let fieldsToValidate = [];
        if (activeStep === 0) {
            fieldsToValidate = ['name', 'flag'];
        } else if (activeStep === 1) {
            fieldsToValidate = ['username', 'email', 'password', 'confirmPassword'];
        } else if (activeStep === 2) {
            fieldsToValidate = ['memo'];
        }
        return await trigger(fieldsToValidate);
    };
    const handleNext = async () => {
        const isValid = await validateCurrentStep();
        if (!isValid) {
            return;
        }
        setActiveStep(prev => prev + 1);
    };
    const handleBack = () => {
        setActiveStep(prev => prev - 1);
    };
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setActiveStep(0);
            setOpen(false);
            reset();
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    const onSubmit = (data) => {
        request.post('/tenant/init', data).then(res => {
            const { code, msg, data } = res.data;
            if (code === 200) {
                notify(msg, { type: 'success', messageArgs: { _: msg } });
                setOpen(false);
                reset();
            } else {
                notify(msg, { type: 'error', messageArgs: { _: msg } });
            }
        }).catch((error) => {
            notify(error.message, { type: 'error', messageArgs: { _: error.message } });
            console.error(error);
        })
    }
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            <Dialog
                open={open}
                onClose={handleClose}
                aria-labelledby="form-dialog-title"
                fullWidth
                disableRestoreFocus
                maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.tenant.name"
                                        source="name"
                                        parse={v => v}
                                        autoFocus
                                        validate={required()}
                                    />
                                </Grid>
                <DialogTitle id="form-dialog-title" sx={{
                    position: 'sticky',
                    top: 0,
                    backgroundColor: 'background.paper',
                    zIndex: 1000
                }}>
                    {translate('create.title')}
                    <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                        <DialogCloseButton onClose={handleClose} />
                    </Box>
                </DialogTitle>
                <DialogContent sx={{ mt: 2 }}>
                    {open && (
                        <form noValidate onSubmit={handleSubmit(onSubmit)} >
                            <Stepper activeStep={activeStep} orientation="vertical">
                                <Step>
                                    <StepLabel>{translate('page.tenant.create.title.basic')}</StepLabel>
                                    <StepContent>
                                        <Stack spacing={3} mt={2} direction='column' width={'50%'}>
                                            <Controller
                                                name="name"
                                                control={control}
                                                defaultValue=""
                                                rules={{ required: true }}
                                                parse={v => v}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('table.field.tenant.name')}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        error={!!error}
                                                        helperText={error ? translate('ra.validation.required') : ""}
                                                    />
                                                )}
                                            />
                                            <Controller
                                                name="flag"
                                                control={control}
                                                defaultValue=""
                                                rules={{
                                                    required: {
                                                        value: true,
                                                        message: translate('ra.validation.required')
                                                    },
                                                    pattern: {
                                                        value: /^[A-Za-z]{3,20}$/,
                                                        message: translate('page.tenant.create.tip.onlyEn'),
                                                    }
                                                }}
                                                parse={v => v}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('table.field.tenant.flag')}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        error={!!error}
                                                        helperText={error ? error.message : ""}
                                                    />
                                                )}
                                            />
                                        </Stack>
                                        <Box sx={{ mt: 3 }}>
                                            <Button onClick={handleNext} variant="contained">
                                                {translate('page.tenant.create.btn.next')}
                                            </Button>
                                            <Button disabled={activeStep === 0} onClick={handleBack}>
                                                {translate('page.tenant.create.btn.back')}
                                            </Button>
                                        </Box>
                                    </StepContent>
                                </Step>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
                                <Step>
                                    <StepLabel>{translate('page.tenant.create.title.root')}</StepLabel>
                                    <StepContent>
                                        <Stack spacing={3} mt={2} direction='column' width={'50%'}>
                                            <Controller
                                                name="username"
                                                control={control}
                                                defaultValue=""
                                                rules={{
                                                    required: {
                                                        value: true,
                                                        message: translate('ra.validation.required')
                                                    },
                                                    pattern: {
                                                        value: /^[A-Za-z0-9]{3,20}$/,
                                                        message: translate('page.settings.resetPwd.tip.usernameLimit'),
                                                    },
                                                }}
                                                parse={v => v}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('table.field.user.username')}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        error={!!error}
                                                        helperText={error ? error.message : ""}
                                                    />
                                                )}
                                            />
                                            <Controller
                                                name="email"
                                                control={control}
                                                defaultValue=""
                                                rules={{
                                                    required: false,
                                                    pattern: {
                                                        value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
                                                        message: translate("ra.validation.email"),
                                                    },
                                                }}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('table.field.user.email')}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        error={!!error}
                                                        helperText={error ? error.message : ""}
                                                    />
                                                )}
                                            />
                                            <Controller
                                                name="password"
                                                control={control}
                                                defaultValue=""
                                                rules={{
                                                    required: {
                                                        value: true,
                                                        message: translate('ra.validation.required'),
                                                    },
                                                    pattern: {
                                                        value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,13}$/,
                                                        message: translate('page.settings.resetPwd.tip.pwdInputLimit'),
                                                    },
                                                }}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('page.settings.resetPwd.newPwd')}
                                                        type={showPassword ? 'text' : 'password'}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        error={!!error}
                                                        helperText={error ? error.message : ""}
                                                        InputProps={{
                                                            endAdornment: (
                                                                <InputAdornment position="end">
                                                                    <IconButton
                                                                        aria-label="toggle password visibility"
                                                                        onClick={() => setShowPassword((show) => !show)}
                                                                        onMouseDown={(event) => { event.preventDefault() }}
                                                                        edge="end"
                                                                    >
                                                                        {showPassword ? <VisibilityOff /> : <Visibility />}
                                                                    </IconButton>
                                                                </InputAdornment>
                                                            ),
                                                        }}
                                                    />
                                                )}
                                            />
                                            <Controller
                                                name="confirmPassword"
                                                control={control}
                                                defaultValue=""
                                                rules={{
                                                    required: translate('ra.validation.required'),
                                                    validate: value =>
                                                        value === passwordVal || translate('page.settings.resetPwd.tip.pwdNotMatch'),
                                                }}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('page.settings.resetPwd.confirmNewPwd')}
                                                        type={showPassword ? 'text' : 'password'}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        error={!!error}
                                                        helperText={error?.message || ""}
                                                        InputProps={{
                                                            endAdornment: (
                                                                <InputAdornment position="end">
                                                                    <IconButton
                                                                        aria-label="toggle password visibility"
                                                                        onClick={() => setShowPassword((show) => !show)}
                                                                        onMouseDown={(event) => { event.preventDefault() }}
                                                                        edge="end"
                                                                    >
                                                                        {showPassword ? <VisibilityOff /> : <Visibility />}
                                                                    </IconButton>
                                                                </InputAdornment>
                                                            ),
                                                        }}
                                                    />
                                                )}
                                            />
                                        </Stack>
                                        <Box sx={{ mt: 3 }}>
                                            <Button onClick={handleNext} variant="contained">
                                                {translate('page.tenant.create.btn.next')}
                                            </Button>
                                            <Button onClick={handleBack}>
                                                {translate('page.tenant.create.btn.back')}
                                            </Button>
                                        </Box>
                                    </StepContent>
                                </Step>
                                <Step>
                                    <StepLabel>{translate('page.tenant.create.title.confirm')}</StepLabel>
                                    <StepContent>
                                        <Stack spacing={3} mt={2} direction='column' width={'50%'}>
                                            <Controller
                                                name="memo"
                                                control={control}
                                                defaultValue=""
                                                rules={{ required: false }}
                                                parse={v => v}
                                                render={({ field, fieldState: { error } }) => (
                                                    <TextField
                                                        {...field}
                                                        label={translate('common.field.memo')}
                                                        variant="outlined"
                                                        autoComplete="off"
                                                        fullWidth
                                                        multiline
                                                        minRows={2}
                                                        error={!!error}
                                                        helperText={error ? translate('ra.validation.required') : ""}
                                                    />
                                                )}
                                            />
                                        </Stack>
                                        <Box sx={{ mt: 3 }}>
                                            <Button type="submit" variant="contained">
                                                {translate('ra.action.save')}
                                            </Button>
                                            <Button onClick={handleBack}>
                                                {translate('page.tenant.create.btn.back')}
                                            </Button>
                                        </Box>
                                    </StepContent>
                                </Step>
                            </Stepper>
                        </form>
                    )}
                </DialogContent>
                <DialogActions sx={{ height: 10, position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                </DialogActions>
            </Dialog>
        </>
    )
}