mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
refactor(frontend): update permissions dialogs
This commit is contained in:
parent
14dbe5075a
commit
58c96417b5
251
frontend/src/components/roles/PermissionsBody.tsx
Normal file
251
frontend/src/components/roles/PermissionsBody.tsx
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2024 Hexastack. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||||
|
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||||
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import DeleteOutlinedIcon from "@mui/icons-material/DeleteOutlined";
|
||||||
|
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionSummary,
|
||||||
|
Divider,
|
||||||
|
Grid,
|
||||||
|
MenuItem,
|
||||||
|
Paper,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { FC, Fragment, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { IconButton } from "@/app-components/buttons/IconButton";
|
||||||
|
import { Input } from "@/app-components/inputs/Input";
|
||||||
|
import { useCreate } from "@/hooks/crud/useCreate";
|
||||||
|
import { useDelete } from "@/hooks/crud/useDelete";
|
||||||
|
import { useFind } from "@/hooks/crud/useFind";
|
||||||
|
import { useGetFromCache } from "@/hooks/crud/useGet";
|
||||||
|
import { useToast } from "@/hooks/useToast";
|
||||||
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
import { EntityType, Format } from "@/services/types";
|
||||||
|
import { ComponentFormProps } from "@/types/common/dialogs.types";
|
||||||
|
import { IPermission, IPermissionAttributes } from "@/types/permission.types";
|
||||||
|
import { IRole } from "@/types/role.types";
|
||||||
|
|
||||||
|
const DEFAULT_PAYLOAD: IPermissionAttributes = {
|
||||||
|
action: "",
|
||||||
|
model: "",
|
||||||
|
relation: "",
|
||||||
|
role: "",
|
||||||
|
};
|
||||||
|
const AccordionModelHead = () => (
|
||||||
|
<Grid container direction="row" minHeight="6rem" alignContent="center" mb={1}>
|
||||||
|
<Grid item width="6rem" />
|
||||||
|
<Grid item xs textAlign="left">
|
||||||
|
<Typography fontWeight={700} fontSize="body2.fontSize">
|
||||||
|
Action
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs textAlign="left">
|
||||||
|
<Typography fontWeight={700} fontSize="body2.fontSize">
|
||||||
|
Relation
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const PermissionsBody: FC<ComponentFormProps<IRole>> = ({
|
||||||
|
data: role,
|
||||||
|
Wrapper = Fragment,
|
||||||
|
WrapperProps,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslate();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const { data: models, refetch: modelRefetch } = useFind(
|
||||||
|
{ entity: EntityType.MODEL, format: Format.FULL },
|
||||||
|
{
|
||||||
|
hasCount: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const getPermissionFromCache = useGetFromCache(EntityType.PERMISSION);
|
||||||
|
const options = {
|
||||||
|
onError: (error: Error) => {
|
||||||
|
toast.error(error.message || t("message.internal_server_error"));
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
modelRefetch();
|
||||||
|
toast.success(t("message.item_delete_success"));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { mutate: createPermission } = useCreate(EntityType.PERMISSION, {
|
||||||
|
...options,
|
||||||
|
onError: (error: Error & { statusCode?: number }) => {
|
||||||
|
rest.onError?.();
|
||||||
|
if (error.statusCode === 409) {
|
||||||
|
toast.error(t("message.permission_already_exists"));
|
||||||
|
} else {
|
||||||
|
toast.error(t("message.internal_server_error"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { mutate: deletePermission } = useDelete(
|
||||||
|
EntityType.PERMISSION,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
const [expanded, setExpanded] = useState<string | false>(false);
|
||||||
|
const [payload, setPayload] =
|
||||||
|
useState<IPermissionAttributes>(DEFAULT_PAYLOAD);
|
||||||
|
const reset = () => setPayload(DEFAULT_PAYLOAD);
|
||||||
|
const handleChange =
|
||||||
|
(panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => {
|
||||||
|
setExpanded(isExpanded ? panel : false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (expanded === false && models?.[0]?.id) setExpanded(models[0].id);
|
||||||
|
}, [models]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper open={!!WrapperProps?.open} onSubmit={() => {}} {...WrapperProps}>
|
||||||
|
<Typography fontWeight={700} sx={{ marginBottom: 2 }}>
|
||||||
|
{role?.name}
|
||||||
|
</Typography>
|
||||||
|
{models?.map((model) => (
|
||||||
|
<Accordion
|
||||||
|
key={model.id}
|
||||||
|
expanded={expanded === model.id}
|
||||||
|
onChange={handleChange(model.id)}
|
||||||
|
sx={{
|
||||||
|
marginTop: 1,
|
||||||
|
boxShadow: "none",
|
||||||
|
"&:before": {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<KeyboardArrowUpIcon />}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "background.default",
|
||||||
|
borderRadius: 1,
|
||||||
|
fontFamily: "inherit",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>{model.name}</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails sx={{ p: 0, m: 0 }}>
|
||||||
|
<Paper
|
||||||
|
sx={{
|
||||||
|
padding: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AccordionModelHead />
|
||||||
|
{model.permissions
|
||||||
|
?.map((p) => getPermissionFromCache(p))
|
||||||
|
?.filter(
|
||||||
|
(permission) => permission && permission.role === role?.id,
|
||||||
|
)
|
||||||
|
.map((p) => p as IPermission)
|
||||||
|
.map(({ id, action, relation }, index) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{index > 0 && <Divider />}
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
key={id}
|
||||||
|
sx={{
|
||||||
|
borderRadius: 0.8,
|
||||||
|
padding: 1,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "background.default",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Grid item width="6rem">
|
||||||
|
<IconButton
|
||||||
|
variant="text"
|
||||||
|
color="error"
|
||||||
|
onClick={() => deletePermission(id)}
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<DeleteOutlinedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs>
|
||||||
|
<Typography>{action}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs>
|
||||||
|
<Typography>{relation}</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<Grid container minHeight="2.5rem" padding={1}>
|
||||||
|
<Grid item width="6rem" alignContent="center">
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => {
|
||||||
|
if (role?.id)
|
||||||
|
createPermission({
|
||||||
|
...payload,
|
||||||
|
role: role.id,
|
||||||
|
model: model.id,
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AddIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs alignContent="center">
|
||||||
|
<Input
|
||||||
|
select
|
||||||
|
sx={{ width: "6.875rem" }}
|
||||||
|
label="Action"
|
||||||
|
value={payload.action}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.value)
|
||||||
|
setPayload((currentPayload) => ({
|
||||||
|
...currentPayload,
|
||||||
|
action: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value="create">{t("label.create")}</MenuItem>
|
||||||
|
<MenuItem value="read">{t("label.read")}</MenuItem>
|
||||||
|
<MenuItem value="update">{t("label.update")}</MenuItem>
|
||||||
|
<MenuItem value="delete">{t("label.delete")}</MenuItem>
|
||||||
|
</Input>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs alignContent="center">
|
||||||
|
<Input
|
||||||
|
select
|
||||||
|
sx={{ width: "6.875rem" }}
|
||||||
|
label={t("label.relation")}
|
||||||
|
value={payload.relation}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.value)
|
||||||
|
setPayload((currentPayload) => ({
|
||||||
|
...currentPayload,
|
||||||
|
relation: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value="role">{t("label.role")}</MenuItem>
|
||||||
|
</Input>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
))}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
23
frontend/src/components/roles/PermissionsBodyDialog.tsx
Normal file
23
frontend/src/components/roles/PermissionsBodyDialog.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2025 Hexastack. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||||
|
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||||
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { GenericFormDialog } from "@/app-components/dialogs";
|
||||||
|
import { ComponentFormDialogProps } from "@/types/common/dialogs.types";
|
||||||
|
import { IRole } from "@/types/role.types";
|
||||||
|
|
||||||
|
import { PermissionsBody } from "./PermissionsBody";
|
||||||
|
|
||||||
|
export const PermissionBodyDialog = <T extends IRole = IRole>(
|
||||||
|
props: ComponentFormDialogProps<T>,
|
||||||
|
) => (
|
||||||
|
<GenericFormDialog<T>
|
||||||
|
Form={PermissionsBody}
|
||||||
|
editText="title.manage_permissions"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2024 Hexastack. All rights reserved.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
|
||||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
|
||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
|
||||||
*/
|
|
||||||
|
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
|
||||||
import DeleteOutlinedIcon from "@mui/icons-material/DeleteOutlined";
|
|
||||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionDetails,
|
|
||||||
AccordionSummary,
|
|
||||||
Dialog,
|
|
||||||
Grid,
|
|
||||||
MenuItem,
|
|
||||||
Paper,
|
|
||||||
Typography,
|
|
||||||
DialogContent,
|
|
||||||
DialogActions,
|
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { useState, FC, useEffect } from "react";
|
|
||||||
|
|
||||||
import { IconButton } from "@/app-components/buttons/IconButton";
|
|
||||||
import { DialogTitle } from "@/app-components/dialogs/DialogTitle";
|
|
||||||
import { Input } from "@/app-components/inputs/Input";
|
|
||||||
import { useCreate } from "@/hooks/crud/useCreate";
|
|
||||||
import { useDelete } from "@/hooks/crud/useDelete";
|
|
||||||
import { useFind } from "@/hooks/crud/useFind";
|
|
||||||
import { useGetFromCache } from "@/hooks/crud/useGet";
|
|
||||||
import { DialogControlProps } from "@/hooks/useDialog";
|
|
||||||
import { useToast } from "@/hooks/useToast";
|
|
||||||
import { useTranslate } from "@/hooks/useTranslate";
|
|
||||||
import { EntityType, Format } from "@/services/types";
|
|
||||||
import { IPermission, IPermissionAttributes } from "@/types/permission.types";
|
|
||||||
import { IRole } from "@/types/role.types";
|
|
||||||
|
|
||||||
export type PermissionsDialogProps = DialogControlProps<{
|
|
||||||
role: IRole;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const DEFAULT_PAYLOAD: IPermissionAttributes = {
|
|
||||||
action: "",
|
|
||||||
model: "",
|
|
||||||
relation: "",
|
|
||||||
role: "",
|
|
||||||
};
|
|
||||||
const AccordionModelHead = () => (
|
|
||||||
<Grid container direction="row" minHeight="35px" alignContent="center" mb={1}>
|
|
||||||
<Grid item width="96px" />
|
|
||||||
<Grid item xs textAlign="left">
|
|
||||||
<Typography fontWeight={700} fontSize="body2.fontSize">
|
|
||||||
Action
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs textAlign="left">
|
|
||||||
<Typography fontWeight={700} fontSize="body2.fontSize">
|
|
||||||
Relation
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const PermissionsDialog: FC<PermissionsDialogProps> = ({
|
|
||||||
open,
|
|
||||||
data,
|
|
||||||
closeDialog: closeFunction,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslate();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const { data: models, refetch: modelRefetch } = useFind(
|
|
||||||
{ entity: EntityType.MODEL, format: Format.FULL },
|
|
||||||
{
|
|
||||||
hasCount: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const getPermisionFromCache = useGetFromCache(EntityType.PERMISSION);
|
|
||||||
const { mutateAsync: createPermission } = useCreate(EntityType.PERMISSION, {
|
|
||||||
onError: (error: Error & { statusCode?: number }) => {
|
|
||||||
if (error.statusCode === 409) {
|
|
||||||
toast.error(t("message.permission_already_exists"));
|
|
||||||
} else {
|
|
||||||
toast.error(t("message.internal_server_error"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
modelRefetch();
|
|
||||||
toast.success(t("message.success_save"));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { mutateAsync: deletePermission } = useDelete(EntityType.PERMISSION, {
|
|
||||||
onError: () => {
|
|
||||||
toast.error(t("message.internal_server_error"));
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
modelRefetch();
|
|
||||||
toast.success(t("message.item_delete_success"));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [expanded, setExpanded] = useState<string | false>(false);
|
|
||||||
const [payload, setPayload] =
|
|
||||||
useState<IPermissionAttributes>(DEFAULT_PAYLOAD);
|
|
||||||
const reset = () => setPayload(DEFAULT_PAYLOAD);
|
|
||||||
const handleChange =
|
|
||||||
(panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => {
|
|
||||||
setExpanded(isExpanded ? panel : false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (expanded === false && models?.[0]?.id) setExpanded(models[0].id);
|
|
||||||
}, [models]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
open={open}
|
|
||||||
fullWidth
|
|
||||||
onClose={closeFunction}
|
|
||||||
sx={{ maxWidth: "850px", margin: "auto" }}
|
|
||||||
maxWidth="md"
|
|
||||||
>
|
|
||||||
<DialogTitle onClose={closeFunction}>
|
|
||||||
{t("title.manage_permissions")}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Typography fontWeight={700} sx={{ marginBottom: 2 }}>
|
|
||||||
{data?.role.name}
|
|
||||||
</Typography>
|
|
||||||
{models?.map((model) => {
|
|
||||||
return (
|
|
||||||
<Accordion
|
|
||||||
key={model.id}
|
|
||||||
expanded={expanded === model.id}
|
|
||||||
onChange={handleChange(model.id)}
|
|
||||||
sx={{
|
|
||||||
marginTop: 1,
|
|
||||||
boxShadow: "none",
|
|
||||||
"&:before": {
|
|
||||||
display: "none",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<KeyboardArrowUpIcon />}
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "background.default",
|
|
||||||
borderRadius: 1,
|
|
||||||
fontFamily: "inherit",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography>{model.name}</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails sx={{ p: 0, m: 0 }}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
padding: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AccordionModelHead />
|
|
||||||
{model.permissions
|
|
||||||
?.map((p) => getPermisionFromCache(p))
|
|
||||||
?.filter(
|
|
||||||
(permission) =>
|
|
||||||
permission && permission.role === data?.role.id,
|
|
||||||
)
|
|
||||||
.map((p) => p as IPermission)
|
|
||||||
.map(({ id, action, relation }, index) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{index > 0 && <Divider />}
|
|
||||||
<Grid
|
|
||||||
container
|
|
||||||
key={id}
|
|
||||||
sx={{
|
|
||||||
borderRadius: 0.8,
|
|
||||||
padding: 1,
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "background.default",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Grid item width="96px">
|
|
||||||
<IconButton
|
|
||||||
variant="text"
|
|
||||||
color="error"
|
|
||||||
onClick={() => {
|
|
||||||
deletePermission(id);
|
|
||||||
}}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<DeleteOutlinedIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs>
|
|
||||||
<Typography>{action}</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs>
|
|
||||||
<Typography>{relation}</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<Grid container minHeight="40px" padding={1}>
|
|
||||||
<Grid item width="96px" alignContent="center">
|
|
||||||
<IconButton
|
|
||||||
size="small"
|
|
||||||
color="primary"
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
if (data?.role.id)
|
|
||||||
createPermission({
|
|
||||||
...payload,
|
|
||||||
model: model.id,
|
|
||||||
role: data.role.id,
|
|
||||||
});
|
|
||||||
reset();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AddIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs alignContent="center">
|
|
||||||
<Input
|
|
||||||
select
|
|
||||||
sx={{ width: "110px" }}
|
|
||||||
label="Action"
|
|
||||||
value={payload.action}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value)
|
|
||||||
setPayload((currentPayload) => ({
|
|
||||||
...currentPayload,
|
|
||||||
action: e.target.value,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem value="create">{t("label.create")}</MenuItem>
|
|
||||||
<MenuItem value="read">{t("label.read")}</MenuItem>
|
|
||||||
<MenuItem value="update">{t("label.update")}</MenuItem>
|
|
||||||
<MenuItem value="delete">{t("label.delete")}</MenuItem>
|
|
||||||
</Input>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs alignContent="center">
|
|
||||||
<Input
|
|
||||||
select
|
|
||||||
sx={{ width: "110px" }}
|
|
||||||
label={t("label.relation")}
|
|
||||||
value={payload.relation}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value)
|
|
||||||
setPayload((currentPayload) => ({
|
|
||||||
...currentPayload,
|
|
||||||
relation: e.target.value,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem value="role">{t("label.role")}</MenuItem>
|
|
||||||
</Input>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Paper>
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button variant="outlined" onClick={closeFunction}>
|
|
||||||
{t("button.close")}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue
Block a user