Merge pull request #289 from mirumee/add/staff-password-reset
Add ability to reset own password
This commit is contained in:
commit
7090148f52
13 changed files with 380 additions and 9 deletions
|
@ -19,6 +19,7 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- Fix dropdown clickable areas - #281 by @dominik-zeglen
|
||||
- Use eslint - #285 by @dominik-zeglen
|
||||
- Enforce using "name" property in style hooks - #288 by @dominik-zeglen
|
||||
- Add ability to reset own password - #289 by @dominik-zeglen
|
||||
|
||||
## 2.0.0
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2019-11-26T14:34:48.426Z\n"
|
||||
"POT-Creation-Date: 2019-12-04T14:17:34.264Z\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -1855,6 +1855,14 @@ msgctxt "description"
|
|||
msgid "Category name"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPasswordResetDialog/StaffPasswordResetDialog.json
|
||||
#. [src.staff.components.StaffPasswordResetDialog.2521568990] - dialog header
|
||||
#. defaultMessage is:
|
||||
#. Change Password
|
||||
msgctxt "dialog header"
|
||||
msgid "Change Password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffProperties/StaffProperties.json
|
||||
#. [src.staff.components.StaffProperties.2771097267] - button
|
||||
#. defaultMessage is:
|
||||
|
@ -1863,6 +1871,14 @@ msgctxt "button"
|
|||
msgid "Change photo"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPassword/StaffPassword.json
|
||||
#. [src.staff.components.StaffPassword.1434811103] - button
|
||||
#. defaultMessage is:
|
||||
#. Change your password
|
||||
msgctxt "button"
|
||||
msgid "Change your password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductPricing/ProductPricing.json
|
||||
#. [src.products.components.ProductPricing.3015886868]
|
||||
#. defaultMessage is:
|
||||
|
@ -4911,6 +4927,14 @@ msgctxt "description"
|
|||
msgid "New Password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPasswordResetDialog/StaffPasswordResetDialog.json
|
||||
#. [src.staff.components.StaffPasswordResetDialog.1254879564] - input label
|
||||
#. defaultMessage is:
|
||||
#. New Password
|
||||
msgctxt "input label"
|
||||
msgid "New Password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/views/ProductCreate.json
|
||||
#. [src.products.views.1591632382] - page header
|
||||
#. defaultMessage is:
|
||||
|
@ -4927,6 +4951,14 @@ msgctxt "variant name"
|
|||
msgid "New Variant"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPasswordResetDialog/StaffPasswordResetDialog.json
|
||||
#. [src.staff.components.StaffPasswordResetDialog.1651415182]
|
||||
#. defaultMessage is:
|
||||
#. New password must be at least 8 characters long
|
||||
msgctxt "description"
|
||||
msgid "New password must be at least 8 characters long"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/taxes/components/CountryTaxesPage/CountryTaxesPage.json
|
||||
#. [src.taxes.components.CountryTaxesPage.1451721797] - tax rate
|
||||
#. defaultMessage is:
|
||||
|
@ -5947,6 +5979,14 @@ msgctxt "description"
|
|||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPassword/StaffPassword.json
|
||||
#. [src.staff.components.StaffPassword.2237029987] - header
|
||||
#. defaultMessage is:
|
||||
#. Password
|
||||
msgctxt "header"
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/auth/components/NewPasswordPage/NewPasswordPage.json
|
||||
#. [src.auth.components.NewPasswordPage.4253911811]
|
||||
#. defaultMessage is:
|
||||
|
@ -6215,6 +6255,14 @@ msgctxt "previous step, button"
|
|||
msgid "Previous"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPasswordResetDialog/StaffPasswordResetDialog.json
|
||||
#. [src.staff.components.StaffPasswordResetDialog.53359254] - input label
|
||||
#. defaultMessage is:
|
||||
#. Previous Password
|
||||
msgctxt "input label"
|
||||
msgid "Previous Password"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryProductList/CategoryProductList.json
|
||||
#. [src.categories.components.CategoryProductList.1134347598] - product price
|
||||
#. defaultMessage is:
|
||||
|
@ -9667,6 +9715,14 @@ msgctxt "description"
|
|||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/components/StaffPassword/StaffPassword.json
|
||||
#. [src.staff.components.StaffPassword.1274006906]
|
||||
#. defaultMessage is:
|
||||
#. You should change your password every month to avoid security issues.
|
||||
msgctxt "description"
|
||||
msgid "You should change your password every month to avoid security issues."
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductVariantCreateDialog/ProductVariantCreateSummary.json
|
||||
#. [src.products.components.ProductVariantCreateDialog.1009678918] - header
|
||||
#. defaultMessage is:
|
||||
|
|
|
@ -19,6 +19,7 @@ import { PermissionEnum } from "../../../types/globalTypes";
|
|||
import { StaffMemberDetails_user } from "../../types/StaffMemberDetails";
|
||||
import StaffPreferences from "../StaffPreferences";
|
||||
import StaffProperties from "../StaffProperties/StaffProperties";
|
||||
import StaffPassword from "../StaffPassword/StaffPassword";
|
||||
|
||||
interface FormData {
|
||||
hasFullAccess: boolean;
|
||||
|
@ -39,6 +40,7 @@ export interface StaffDetailsPageProps {
|
|||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
staffMember: StaffMemberDetails_user;
|
||||
onBack: () => void;
|
||||
onChangePassword: () => void;
|
||||
onDelete: () => void;
|
||||
onImageDelete: () => void;
|
||||
onSubmit: (data: FormData) => void;
|
||||
|
@ -55,6 +57,7 @@ const StaffDetailsPage: React.FC<StaffDetailsPageProps> = ({
|
|||
saveButtonBarState,
|
||||
staffMember,
|
||||
onBack,
|
||||
onChangePassword,
|
||||
onDelete,
|
||||
onImageDelete,
|
||||
onImageUpload,
|
||||
|
@ -100,6 +103,12 @@ const StaffDetailsPage: React.FC<StaffDetailsPageProps> = ({
|
|||
onImageUpload={onImageUpload}
|
||||
onImageDelete={onImageDelete}
|
||||
/>
|
||||
{canEditPreferences && (
|
||||
<>
|
||||
<CardSpacer />
|
||||
<StaffPassword onChangePassword={onChangePassword} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{canEditPreferences && (
|
||||
|
|
43
src/staff/components/StaffPassword/StaffPassword.tsx
Normal file
43
src/staff/components/StaffPassword/StaffPassword.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
|
||||
interface StaffPasswordProps {
|
||||
onChangePassword: () => void;
|
||||
}
|
||||
|
||||
const StaffPassword: React.FC<StaffPasswordProps> = ({ onChangePassword }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Password",
|
||||
description: "header"
|
||||
})}
|
||||
toolbar={
|
||||
<Button color="primary" onClick={onChangePassword}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Change your password"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography>
|
||||
<FormattedMessage defaultMessage="You should change your password every month to avoid security issues." />
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
StaffPassword.displayName = "StaffPassword";
|
||||
export default StaffPassword;
|
2
src/staff/components/StaffPassword/index.ts
Normal file
2
src/staff/components/StaffPassword/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./StaffPassword";
|
||||
export * from "./StaffPassword";
|
|
@ -0,0 +1,111 @@
|
|||
import React from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { DialogProps, UserError } from "@saleor/types";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import Form from "@saleor/components/Form";
|
||||
import ConfirmButton, {
|
||||
ConfirmButtonTransitionState
|
||||
} from "@saleor/components/ConfirmButton";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
|
||||
|
||||
interface StaffPasswordResetDialogFormData {
|
||||
newPassword: string;
|
||||
oldPassword: string;
|
||||
}
|
||||
export interface StaffPasswordResetDialogProps extends DialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
errors: UserError[];
|
||||
onSubmit: (data: StaffPasswordResetDialogFormData) => void;
|
||||
}
|
||||
|
||||
const initialForm: StaffPasswordResetDialogFormData = {
|
||||
newPassword: "",
|
||||
oldPassword: ""
|
||||
};
|
||||
|
||||
const StaffPasswordResetDialog: React.FC<StaffPasswordResetDialogProps> = ({
|
||||
confirmButtonState,
|
||||
errors: apiErrors,
|
||||
open,
|
||||
onClose,
|
||||
onSubmit
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const dialogErrors = useModalDialogErrors(apiErrors, open);
|
||||
|
||||
return (
|
||||
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
|
||||
<DialogTitle>
|
||||
<FormattedMessage
|
||||
defaultMessage="Change Password"
|
||||
description="dialog header"
|
||||
/>
|
||||
</DialogTitle>
|
||||
<Form errors={dialogErrors} initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ change, data, errors, submit }) => (
|
||||
<>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
error={!!errors.oldPassword}
|
||||
fullWidth
|
||||
helperText={errors.oldPassword}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Previous Password",
|
||||
description: "input label"
|
||||
})}
|
||||
name="oldPassword"
|
||||
type="password"
|
||||
onChange={change}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<TextField
|
||||
error={!!errors.newPassword}
|
||||
fullWidth
|
||||
helperText={
|
||||
errors.newPassword ||
|
||||
intl.formatMessage({
|
||||
defaultMessage:
|
||||
"New password must be at least 8 characters long"
|
||||
})
|
||||
}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "New Password",
|
||||
description: "input label"
|
||||
})}
|
||||
name="newPassword"
|
||||
type="password"
|
||||
onChange={change}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose}>
|
||||
<FormattedMessage {...buttonMessages.back} />
|
||||
</Button>
|
||||
<ConfirmButton
|
||||
disabled={data.newPassword.length < 8}
|
||||
transitionState={confirmButtonState}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
onClick={submit}
|
||||
>
|
||||
<FormattedMessage {...buttonMessages.save} />
|
||||
</ConfirmButton>
|
||||
</DialogActions>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
StaffPasswordResetDialog.displayName = "StaffPasswordResetDialog";
|
||||
export default StaffPasswordResetDialog;
|
2
src/staff/components/StaffPasswordResetDialog/index.ts
Normal file
2
src/staff/components/StaffPasswordResetDialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./StaffPasswordResetDialog";
|
||||
export * from "./StaffPasswordResetDialog";
|
|
@ -1,5 +1,6 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeMutation from "@saleor/hooks/makeMutation";
|
||||
import { TypedMutation } from "../mutations";
|
||||
import { staffMemberDetailsFragment } from "./queries";
|
||||
import { StaffAvatarDelete } from "./types/StaffAvatarDelete";
|
||||
|
@ -19,6 +20,10 @@ import {
|
|||
StaffMemberUpdate,
|
||||
StaffMemberUpdateVariables
|
||||
} from "./types/StaffMemberUpdate";
|
||||
import {
|
||||
ChangeStaffPassword,
|
||||
ChangeStaffPasswordVariables
|
||||
} from "./types/ChangeStaffPassword";
|
||||
|
||||
const staffMemberAddMutation = gql`
|
||||
${staffMemberDetailsFragment}
|
||||
|
@ -114,3 +119,18 @@ export const TypedStaffAvatarDeleteMutation = TypedMutation<
|
|||
StaffAvatarDelete,
|
||||
StaffMemberDeleteVariables
|
||||
>(staffAvatarDeleteMutation);
|
||||
|
||||
const changeStaffPassword = gql`
|
||||
mutation ChangeStaffPassword($newPassword: String!, $oldPassword: String!) {
|
||||
passwordChange(newPassword: $newPassword, oldPassword: $oldPassword) {
|
||||
errors {
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const useChangeStaffPassword = makeMutation<
|
||||
ChangeStaffPassword,
|
||||
ChangeStaffPasswordVariables
|
||||
>(changeStaffPassword);
|
||||
|
|
27
src/staff/types/ChangeStaffPassword.ts
Normal file
27
src/staff/types/ChangeStaffPassword.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: ChangeStaffPassword
|
||||
// ====================================================
|
||||
|
||||
export interface ChangeStaffPassword_passwordChange_errors {
|
||||
__typename: "Error";
|
||||
field: string | null;
|
||||
message: string | null;
|
||||
}
|
||||
|
||||
export interface ChangeStaffPassword_passwordChange {
|
||||
__typename: "PasswordChange";
|
||||
errors: ChangeStaffPassword_passwordChange_errors[] | null;
|
||||
}
|
||||
|
||||
export interface ChangeStaffPassword {
|
||||
passwordChange: ChangeStaffPassword_passwordChange | null;
|
||||
}
|
||||
|
||||
export interface ChangeStaffPasswordVariables {
|
||||
newPassword: string;
|
||||
oldPassword: string;
|
||||
}
|
|
@ -27,7 +27,10 @@ export const staffListUrl = (params?: StaffListUrlQueryParams) =>
|
|||
staffListPath + "?" + stringifyQs(params);
|
||||
|
||||
export const staffMemberDetailsPath = (id: string) => urlJoin(staffSection, id);
|
||||
export type StaffMemberDetailsUrlDialog = "remove" | "remove-avatar";
|
||||
export type StaffMemberDetailsUrlDialog =
|
||||
| "change-password"
|
||||
| "remove"
|
||||
| "remove-avatar";
|
||||
export type StaffMemberDetailsUrlQueryParams = Dialog<
|
||||
StaffMemberDetailsUrlDialog
|
||||
>;
|
||||
|
|
|
@ -15,7 +15,8 @@ import {
|
|||
TypedStaffAvatarDeleteMutation,
|
||||
TypedStaffAvatarUpdateMutation,
|
||||
TypedStaffMemberDeleteMutation,
|
||||
TypedStaffMemberUpdateMutation
|
||||
TypedStaffMemberUpdateMutation,
|
||||
useChangeStaffPassword
|
||||
} from "../mutations";
|
||||
import { TypedStaffMemberDetailsQuery } from "../queries";
|
||||
import { StaffAvatarDelete } from "../types/StaffAvatarDelete";
|
||||
|
@ -27,6 +28,8 @@ import {
|
|||
staffMemberDetailsUrl,
|
||||
StaffMemberDetailsUrlQueryParams
|
||||
} from "../urls";
|
||||
import StaffPasswordResetDialog from "../components/StaffPasswordResetDialog";
|
||||
import { ChangeStaffPassword } from "../types/ChangeStaffPassword";
|
||||
|
||||
interface OrderListProps {
|
||||
id: string;
|
||||
|
@ -40,6 +43,32 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
const intl = useIntl();
|
||||
const shop = useShop();
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
staffMemberDetailsUrl(id, {
|
||||
...params,
|
||||
action: undefined
|
||||
})
|
||||
);
|
||||
|
||||
const handleChangePassword = (data: ChangeStaffPassword) => {
|
||||
if (data.passwordChange.errors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
const [changePassword, changePasswordOpts] = useChangeStaffPassword({
|
||||
onCompleted: handleChangePassword
|
||||
});
|
||||
|
||||
const changePasswordTransitionState = getMutationState(
|
||||
changePasswordOpts.called,
|
||||
changePasswordOpts.loading,
|
||||
maybe(() => changePasswordOpts.data.passwordChange.errors)
|
||||
);
|
||||
|
||||
return (
|
||||
<TypedStaffMemberDetailsQuery
|
||||
displayLoader
|
||||
|
@ -128,6 +157,13 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
canRemove={!isUserSameAsViewer}
|
||||
disabled={loading}
|
||||
onBack={() => navigate(staffListUrl())}
|
||||
onChangePassword={() =>
|
||||
navigate(
|
||||
staffMemberDetailsUrl(id, {
|
||||
action: "change-password"
|
||||
})
|
||||
)
|
||||
}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
staffMemberDetailsUrl(id, {
|
||||
|
@ -175,9 +211,7 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
})}
|
||||
confirmButtonState={deleteTransitionState}
|
||||
variant="delete"
|
||||
onClose={() =>
|
||||
navigate(staffMemberDetailsUrl(id))
|
||||
}
|
||||
onClose={closeModal}
|
||||
onConfirm={deleteStaffMember}
|
||||
>
|
||||
<DialogContentText>
|
||||
|
@ -197,9 +231,7 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
})}
|
||||
confirmButtonState={deleteAvatarTransitionState}
|
||||
variant="delete"
|
||||
onClose={() =>
|
||||
navigate(staffMemberDetailsUrl(id))
|
||||
}
|
||||
onClose={closeModal}
|
||||
onConfirm={deleteStaffAvatar}
|
||||
>
|
||||
<DialogContentText>
|
||||
|
@ -215,6 +247,24 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<StaffPasswordResetDialog
|
||||
confirmButtonState={
|
||||
changePasswordTransitionState
|
||||
}
|
||||
errors={maybe(
|
||||
() =>
|
||||
changePasswordOpts.data.passwordChange
|
||||
.errors,
|
||||
[]
|
||||
)}
|
||||
open={params.action === "change-password"}
|
||||
onClose={closeModal}
|
||||
onSubmit={data =>
|
||||
changePassword({
|
||||
variables: data
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
|
|
@ -121349,6 +121349,52 @@ exports[`Storyshots Views / Staff / Staff member details himself 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiPaper-root-id MuiPaper-elevation0-id MuiCard-root-id MuiPaper-rounded-id"
|
||||
>
|
||||
<div
|
||||
class="CardTitle-root-id"
|
||||
>
|
||||
<span
|
||||
class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id"
|
||||
>
|
||||
Password
|
||||
</span>
|
||||
<div
|
||||
class="CardTitle-toolbar-id"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-text-id MuiButton-textPrimary-id"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiButton-label-id"
|
||||
>
|
||||
Change your password
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="CardTitle-children-id"
|
||||
/>
|
||||
<hr
|
||||
class="CardTitle-hr-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiCardContent-root-id"
|
||||
>
|
||||
<div
|
||||
class="MuiTypography-root-id MuiTypography-body1-id"
|
||||
>
|
||||
You should change your password every month to avoid security issues.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
|
|
|
@ -16,6 +16,7 @@ const props: Omit<StaffDetailsPageProps, "classes"> = {
|
|||
canRemove: true,
|
||||
disabled: false,
|
||||
onBack: () => undefined,
|
||||
onChangePassword: () => undefined,
|
||||
onDelete: () => undefined,
|
||||
onImageDelete: () => undefined,
|
||||
onImageUpload: () => undefined,
|
||||
|
|
Loading…
Reference in a new issue