2021-04-29 06:28:59 +00:00
|
|
|
import React, { useState } from "react";
|
2021-03-03 13:53:42 +00:00
|
|
|
import {
|
|
|
|
ActionGroup,
|
2021-04-20 12:22:36 +00:00
|
|
|
AlertVariant,
|
2021-03-03 13:53:42 +00:00
|
|
|
Button,
|
2021-04-16 20:15:02 +00:00
|
|
|
Chip,
|
|
|
|
ChipGroup,
|
2021-03-03 13:53:42 +00:00
|
|
|
FormGroup,
|
2021-04-16 20:15:02 +00:00
|
|
|
InputGroup,
|
2021-03-03 18:56:03 +00:00
|
|
|
Select,
|
|
|
|
SelectOption,
|
|
|
|
Switch,
|
2021-03-03 13:53:42 +00:00
|
|
|
TextInput,
|
|
|
|
} from "@patternfly/react-core";
|
|
|
|
import { useTranslation } from "react-i18next";
|
2021-03-03 18:56:03 +00:00
|
|
|
import { Controller, UseFormMethods } from "react-hook-form";
|
2021-03-11 20:23:08 +00:00
|
|
|
import { useHistory, useParams } from "react-router-dom";
|
2021-03-03 13:53:42 +00:00
|
|
|
import { FormAccess } from "../components/form-access/FormAccess";
|
|
|
|
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
2021-03-03 18:56:03 +00:00
|
|
|
import { HelpItem } from "../components/help-enabler/HelpItem";
|
|
|
|
import { useRealm } from "../context/realm-context/RealmContext";
|
2021-04-29 06:28:59 +00:00
|
|
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
2021-03-11 20:23:08 +00:00
|
|
|
import moment from "moment";
|
2021-04-16 20:15:02 +00:00
|
|
|
import { JoinGroupDialog } from "./JoinGroupDialog";
|
|
|
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
2021-04-20 12:22:36 +00:00
|
|
|
import { useAlerts } from "../components/alert/Alerts";
|
2021-05-03 20:00:12 +00:00
|
|
|
import { emailRegexPattern } from "../util";
|
2021-03-03 13:53:42 +00:00
|
|
|
|
|
|
|
export type UserFormProps = {
|
|
|
|
form: UseFormMethods<UserRepresentation>;
|
|
|
|
save: (user: UserRepresentation) => void;
|
2021-03-11 20:23:08 +00:00
|
|
|
editMode: boolean;
|
|
|
|
timestamp?: number;
|
2021-04-20 12:22:36 +00:00
|
|
|
onGroupsUpdate: (groups: GroupRepresentation[]) => void;
|
2021-03-03 13:53:42 +00:00
|
|
|
};
|
|
|
|
|
2021-03-03 21:17:01 +00:00
|
|
|
export const UserForm = ({
|
2021-03-11 20:23:08 +00:00
|
|
|
form: { handleSubmit, register, errors, watch, control, setValue, reset },
|
2021-03-03 21:17:01 +00:00
|
|
|
save,
|
2021-03-11 20:23:08 +00:00
|
|
|
editMode,
|
2021-04-20 12:22:36 +00:00
|
|
|
onGroupsUpdate,
|
2021-03-03 21:17:01 +00:00
|
|
|
}: UserFormProps) => {
|
2021-03-03 13:53:42 +00:00
|
|
|
const { t } = useTranslation("users");
|
2021-03-03 18:56:03 +00:00
|
|
|
const { realm } = useRealm();
|
|
|
|
|
|
|
|
const [
|
|
|
|
isRequiredUserActionsDropdownOpen,
|
|
|
|
setRequiredUserActionsDropdownOpen,
|
|
|
|
] = useState(false);
|
|
|
|
const history = useHistory();
|
2021-03-11 20:23:08 +00:00
|
|
|
const adminClient = useAdminClient();
|
|
|
|
const { id } = useParams<{ id: string }>();
|
2021-03-03 18:56:03 +00:00
|
|
|
|
2021-03-03 21:17:01 +00:00
|
|
|
const watchUsernameInput = watch("username");
|
2021-03-11 20:23:08 +00:00
|
|
|
const [timestamp, setTimestamp] = useState(null);
|
2021-04-16 20:15:02 +00:00
|
|
|
const [chips, setChips] = useState<(string | undefined)[]>([]);
|
2021-04-20 12:22:36 +00:00
|
|
|
const [selectedGroups, setSelectedGroups] = useState<GroupRepresentation[]>(
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
const { addAlert } = useAlerts();
|
2021-04-16 20:15:02 +00:00
|
|
|
|
|
|
|
const [open, setOpen] = useState(false);
|
2021-03-11 20:23:08 +00:00
|
|
|
|
2021-04-29 06:28:59 +00:00
|
|
|
useFetch(
|
|
|
|
async () => {
|
|
|
|
if (editMode) return await adminClient.users.findOne({ id: id });
|
|
|
|
},
|
|
|
|
(user) => {
|
|
|
|
if (user) setupForm(user);
|
|
|
|
},
|
|
|
|
[chips]
|
|
|
|
);
|
2021-03-11 20:23:08 +00:00
|
|
|
|
|
|
|
const setupForm = (user: UserRepresentation) => {
|
|
|
|
reset();
|
|
|
|
Object.entries(user).map((entry) => {
|
|
|
|
if (entry[0] == "createdTimestamp") {
|
|
|
|
setTimestamp(entry[1]);
|
|
|
|
} else {
|
|
|
|
setValue(entry[0], entry[1]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
2021-03-03 21:17:01 +00:00
|
|
|
|
2021-03-03 18:56:03 +00:00
|
|
|
const requiredUserActionsOptions = [
|
2021-03-11 20:23:08 +00:00
|
|
|
<SelectOption key={0} value="CONFIGURE_TOTP">
|
2021-03-03 18:56:03 +00:00
|
|
|
{t("configureOTP")}
|
|
|
|
</SelectOption>,
|
2021-03-11 20:23:08 +00:00
|
|
|
<SelectOption key={1} value="UPDATE_PASSWORD">
|
2021-03-03 18:56:03 +00:00
|
|
|
{t("updatePassword")}
|
|
|
|
</SelectOption>,
|
2021-03-11 20:23:08 +00:00
|
|
|
<SelectOption key={2} value="UPDATE_PROFILE">
|
2021-03-03 18:56:03 +00:00
|
|
|
{t("updateProfile")}
|
|
|
|
</SelectOption>,
|
2021-03-11 20:23:08 +00:00
|
|
|
<SelectOption key={3} value="VERIFY_EMAIL">
|
2021-03-03 18:56:03 +00:00
|
|
|
{t("verifyEmail")}
|
|
|
|
</SelectOption>,
|
|
|
|
];
|
|
|
|
|
|
|
|
const clearSelection = () => {
|
|
|
|
setRequiredUserActionsDropdownOpen(false);
|
|
|
|
};
|
|
|
|
|
2021-04-16 20:15:02 +00:00
|
|
|
const deleteItem = (id: string) => {
|
|
|
|
const copyOfChips = chips;
|
2021-04-20 12:22:36 +00:00
|
|
|
const copyOfGroups = selectedGroups;
|
2021-04-16 20:15:02 +00:00
|
|
|
|
|
|
|
setChips(copyOfChips.filter((item) => item !== id));
|
2021-04-20 12:22:36 +00:00
|
|
|
setSelectedGroups(copyOfGroups.filter((item) => item.name !== id));
|
|
|
|
onGroupsUpdate(selectedGroups);
|
2021-04-16 20:15:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const addChips = async (groups: GroupRepresentation[]): Promise<void> => {
|
2021-04-20 12:22:36 +00:00
|
|
|
const newSelectedGroups = groups;
|
2021-04-16 20:15:02 +00:00
|
|
|
|
2021-04-20 12:22:36 +00:00
|
|
|
const newGroupNames: (string | undefined)[] = newSelectedGroups!.map(
|
2021-04-16 20:15:02 +00:00
|
|
|
(item) => item.name
|
|
|
|
);
|
|
|
|
setChips([...chips!, ...newGroupNames]);
|
2021-04-20 12:22:36 +00:00
|
|
|
setSelectedGroups([...selectedGroups!, ...newSelectedGroups]);
|
2021-04-16 20:15:02 +00:00
|
|
|
};
|
|
|
|
|
2021-04-20 12:22:36 +00:00
|
|
|
onGroupsUpdate(selectedGroups);
|
2021-04-16 20:15:02 +00:00
|
|
|
|
|
|
|
const addGroups = async (groups: GroupRepresentation[]): Promise<void> => {
|
|
|
|
const newGroups = groups;
|
|
|
|
|
|
|
|
newGroups.forEach(async (group) => {
|
|
|
|
try {
|
|
|
|
await adminClient.users.addToGroup({
|
|
|
|
id: id,
|
|
|
|
groupId: group.id!,
|
|
|
|
});
|
2021-04-20 12:22:36 +00:00
|
|
|
addAlert(t("users:addedGroupMembership"), AlertVariant.success);
|
2021-04-16 20:15:02 +00:00
|
|
|
} catch (error) {
|
2021-04-20 12:22:36 +00:00
|
|
|
addAlert(
|
|
|
|
t("users:addedGroupMembershipError", { error }),
|
|
|
|
AlertVariant.danger
|
|
|
|
);
|
2021-04-16 20:15:02 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleModal = () => {
|
|
|
|
setOpen(!open);
|
|
|
|
};
|
|
|
|
|
2021-03-03 13:53:42 +00:00
|
|
|
return (
|
|
|
|
<FormAccess
|
|
|
|
isHorizontal
|
2021-03-03 21:17:01 +00:00
|
|
|
onSubmit={handleSubmit(save)}
|
2021-03-03 18:56:03 +00:00
|
|
|
role="manage-users"
|
2021-03-03 13:53:42 +00:00
|
|
|
className="pf-u-mt-lg"
|
|
|
|
>
|
2021-04-16 20:15:02 +00:00
|
|
|
{open && (
|
|
|
|
<JoinGroupDialog
|
|
|
|
open={open}
|
|
|
|
onClose={() => setOpen(!open)}
|
|
|
|
onConfirm={editMode ? addGroups : addChips}
|
|
|
|
toggleDialog={() => toggleModal()}
|
|
|
|
chips={chips}
|
|
|
|
/>
|
|
|
|
)}
|
2021-03-11 20:23:08 +00:00
|
|
|
{editMode ? (
|
|
|
|
<>
|
|
|
|
<FormGroup
|
2021-03-30 01:03:26 +00:00
|
|
|
label={t("common:id")}
|
2021-03-11 20:23:08 +00:00
|
|
|
fieldId="kc-id"
|
|
|
|
isRequired
|
|
|
|
validated={errors.id ? "error" : "default"}
|
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register({ required: !editMode })}
|
|
|
|
type="text"
|
|
|
|
id="kc-id"
|
|
|
|
name="id"
|
|
|
|
isReadOnly={editMode}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("createdAt")}
|
|
|
|
fieldId="kc-created-at"
|
|
|
|
isRequired
|
|
|
|
validated={errors.createdTimestamp ? "error" : "default"}
|
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
value={moment(timestamp).format("MM/DD/YY hh:MM:ss A")}
|
|
|
|
type="text"
|
|
|
|
id="kc-created-at"
|
|
|
|
name="createdTimestamp"
|
|
|
|
isReadOnly={editMode}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<FormGroup
|
|
|
|
label={t("username")}
|
|
|
|
fieldId="kc-username"
|
|
|
|
isRequired
|
|
|
|
validated={errors.username ? "error" : "default"}
|
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-username"
|
|
|
|
name="username"
|
|
|
|
isReadOnly={editMode}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
)}
|
2021-03-03 13:53:42 +00:00
|
|
|
<FormGroup
|
2021-03-03 18:56:03 +00:00
|
|
|
label={t("email")}
|
2021-03-03 13:53:42 +00:00
|
|
|
fieldId="kc-description"
|
2021-03-03 21:17:01 +00:00
|
|
|
validated={errors.email ? "error" : "default"}
|
2021-03-04 18:49:05 +00:00
|
|
|
helperTextInvalid={t("users:emailInvalid")}
|
2021-03-03 18:56:03 +00:00
|
|
|
>
|
|
|
|
<TextInput
|
2021-03-04 18:49:05 +00:00
|
|
|
ref={register({
|
|
|
|
pattern: emailRegexPattern,
|
|
|
|
})}
|
|
|
|
type="email"
|
2021-03-03 18:56:03 +00:00
|
|
|
id="kc-email"
|
|
|
|
name="email"
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid="email-input"
|
2021-03-03 19:36:21 +00:00
|
|
|
aria-label={t("emailInput")}
|
2021-03-03 18:56:03 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("emailVerified")}
|
|
|
|
fieldId="kc-email-verified"
|
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText={t("emailVerifiedHelpText")}
|
|
|
|
forLabel={t("emailVerified")}
|
|
|
|
forID="email-verified"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Controller
|
2021-03-11 20:23:08 +00:00
|
|
|
name="emailVerified"
|
2021-03-03 18:56:03 +00:00
|
|
|
defaultValue={false}
|
2021-03-03 21:17:01 +00:00
|
|
|
control={control}
|
2021-03-03 18:56:03 +00:00
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<Switch
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid="email-verified-switch"
|
2021-03-03 18:56:03 +00:00
|
|
|
id={"kc-user-email-verified"}
|
|
|
|
isDisabled={false}
|
2021-03-11 20:23:08 +00:00
|
|
|
onChange={(value) => onChange(value)}
|
|
|
|
isChecked={value}
|
2021-03-03 18:56:03 +00:00
|
|
|
label={t("common:on")}
|
|
|
|
labelOff={t("common:off")}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
></Controller>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("firstName")}
|
|
|
|
fieldId="kc-firstname"
|
2021-03-03 21:17:01 +00:00
|
|
|
validated={errors.firstName ? "error" : "default"}
|
2021-03-03 18:56:03 +00:00
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
>
|
|
|
|
<TextInput
|
2021-03-03 21:17:01 +00:00
|
|
|
ref={register()}
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid="firstName-input"
|
2021-03-03 18:56:03 +00:00
|
|
|
type="text"
|
|
|
|
id="kc-firstname"
|
2021-03-04 18:49:05 +00:00
|
|
|
name="firstName"
|
2021-03-03 18:56:03 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("lastName")}
|
|
|
|
fieldId="kc-name"
|
2021-03-03 21:17:01 +00:00
|
|
|
validated={errors.lastName ? "error" : "default"}
|
2021-03-03 13:53:42 +00:00
|
|
|
>
|
2021-03-03 18:56:03 +00:00
|
|
|
<TextInput
|
2021-03-03 21:17:01 +00:00
|
|
|
ref={register()}
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid="lastName-input"
|
2021-03-03 13:53:42 +00:00
|
|
|
type="text"
|
2021-03-03 18:56:03 +00:00
|
|
|
id="kc-lastname"
|
2021-03-04 18:49:05 +00:00
|
|
|
name="lastName"
|
2021-03-03 19:36:21 +00:00
|
|
|
aria-label={t("lastName")}
|
2021-03-03 13:53:42 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
2021-03-03 18:56:03 +00:00
|
|
|
<FormGroup
|
|
|
|
label={t("common:enabled")}
|
|
|
|
fieldId="kc-enabled"
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText={t("disabledHelpText")}
|
|
|
|
forLabel={t("enabled")}
|
|
|
|
forID="enabled-label"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Controller
|
2021-03-11 20:23:08 +00:00
|
|
|
name="enabled"
|
2021-03-03 18:56:03 +00:00
|
|
|
defaultValue={false}
|
2021-03-03 21:17:01 +00:00
|
|
|
control={control}
|
2021-03-03 18:56:03 +00:00
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<Switch
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid="user-enabled-switch"
|
2021-03-03 18:56:03 +00:00
|
|
|
id={"kc-user-enabled"}
|
|
|
|
isDisabled={false}
|
2021-03-11 20:23:08 +00:00
|
|
|
onChange={(value) => onChange(value)}
|
|
|
|
isChecked={value}
|
2021-03-03 18:56:03 +00:00
|
|
|
label={t("common:on")}
|
|
|
|
labelOff={t("common:off")}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
></Controller>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("requiredUserActions")}
|
|
|
|
fieldId="kc-required-user-actions"
|
2021-03-03 21:17:01 +00:00
|
|
|
validated={errors.requiredActions ? "error" : "default"}
|
2021-03-03 18:56:03 +00:00
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText={t("requiredUserActionsHelpText")}
|
|
|
|
forLabel={t("requiredUserActions")}
|
|
|
|
forID="required-user-actions-label"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Controller
|
2021-03-11 20:23:08 +00:00
|
|
|
name="requiredActions"
|
|
|
|
defaultValue={[]}
|
2021-03-03 18:56:03 +00:00
|
|
|
typeAheadAriaLabel="Select an action"
|
2021-03-03 21:17:01 +00:00
|
|
|
control={control}
|
2021-03-11 20:23:08 +00:00
|
|
|
render={({ onChange, value }) => (
|
2021-03-03 18:56:03 +00:00
|
|
|
<Select
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid="required-actions-select"
|
2021-03-03 18:56:03 +00:00
|
|
|
placeholderText="Select action"
|
|
|
|
toggleId="kc-required-user-actions"
|
|
|
|
onToggle={() =>
|
|
|
|
setRequiredUserActionsDropdownOpen(
|
|
|
|
!isRequiredUserActionsDropdownOpen
|
|
|
|
)
|
|
|
|
}
|
|
|
|
isOpen={isRequiredUserActionsDropdownOpen}
|
2021-03-11 20:23:08 +00:00
|
|
|
selections={value}
|
|
|
|
onSelect={(_, v) => {
|
|
|
|
const option = v as string;
|
|
|
|
if (value.includes(option)) {
|
|
|
|
onChange(value.filter((item: string) => item !== option));
|
2021-03-03 18:56:03 +00:00
|
|
|
} else {
|
2021-03-11 20:23:08 +00:00
|
|
|
onChange([...value, option]);
|
2021-03-03 18:56:03 +00:00
|
|
|
}
|
|
|
|
}}
|
|
|
|
onClear={clearSelection}
|
|
|
|
variant="typeaheadmulti"
|
|
|
|
>
|
|
|
|
{requiredUserActionsOptions}
|
|
|
|
</Select>
|
|
|
|
)}
|
2021-03-11 20:23:08 +00:00
|
|
|
/>
|
2021-03-03 18:56:03 +00:00
|
|
|
</FormGroup>
|
2021-04-16 20:15:02 +00:00
|
|
|
{!editMode && (
|
|
|
|
<FormGroup
|
|
|
|
label={t("common:groups")}
|
|
|
|
fieldId="kc-groups"
|
|
|
|
validated={errors.requiredActions ? "error" : "default"}
|
|
|
|
helperTextInvalid={t("common:required")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText={t("requiredUserActionsHelpText")}
|
|
|
|
forLabel={t("requiredUserActions")}
|
|
|
|
forID="required-user-actions-label"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Controller
|
|
|
|
name="groups"
|
|
|
|
defaultValue={[]}
|
|
|
|
typeAheadAriaLabel="Select an action"
|
|
|
|
control={control}
|
|
|
|
render={() => (
|
|
|
|
<>
|
|
|
|
<InputGroup>
|
2021-04-20 12:22:36 +00:00
|
|
|
<ChipGroup categoryName={" "}>
|
2021-04-16 20:15:02 +00:00
|
|
|
{chips.map((currentChip) => (
|
|
|
|
<Chip
|
|
|
|
key={currentChip}
|
|
|
|
onClick={() => deleteItem(currentChip!)}
|
|
|
|
>
|
|
|
|
{currentChip}
|
|
|
|
</Chip>
|
|
|
|
))}
|
|
|
|
</ChipGroup>
|
|
|
|
<Button
|
|
|
|
id="kc-join-groups-button"
|
|
|
|
onClick={toggleModal}
|
|
|
|
variant="secondary"
|
2021-04-20 12:22:36 +00:00
|
|
|
data-testid="join-groups-button"
|
2021-04-16 20:15:02 +00:00
|
|
|
>
|
|
|
|
{t("users:joinGroups")}
|
|
|
|
</Button>
|
|
|
|
</InputGroup>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
)}
|
|
|
|
|
2021-03-03 13:53:42 +00:00
|
|
|
<ActionGroup>
|
2021-03-03 21:17:01 +00:00
|
|
|
<Button
|
2021-03-11 20:23:08 +00:00
|
|
|
data-testid={!editMode ? "create-user" : "save-user"}
|
|
|
|
isDisabled={!editMode && !watchUsernameInput}
|
2021-03-03 21:17:01 +00:00
|
|
|
variant="primary"
|
|
|
|
type="submit"
|
|
|
|
>
|
2021-03-11 20:23:08 +00:00
|
|
|
{!editMode ? t("common:Create") : t("common:Save")}
|
2021-03-03 13:53:42 +00:00
|
|
|
</Button>
|
2021-03-03 19:12:16 +00:00
|
|
|
<Button
|
|
|
|
data-testid="cancel-create-user"
|
|
|
|
onClick={() => history.push(`/${realm}/users`)}
|
|
|
|
variant="link"
|
|
|
|
>
|
2021-03-03 18:56:03 +00:00
|
|
|
{t("common:cancel")}
|
2021-03-03 13:53:42 +00:00
|
|
|
</Button>
|
|
|
|
</ActionGroup>
|
|
|
|
</FormAccess>
|
|
|
|
);
|
|
|
|
};
|