Merge remote-tracking branch 'upstream/main' into AUT-1115
This commit is contained in:
@@ -10,7 +10,7 @@ import CardActionArea from '@mui/material/CardActionArea';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import ConnectionContextMenu from 'components/AppConnectionContextMenu';
|
||||
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
|
||||
@@ -35,6 +35,7 @@ function AppConnectionRow(props) {
|
||||
const [verificationVisible, setVerificationVisible] = React.useState(false);
|
||||
const contextButtonRef = React.useRef(null);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [deleteConnection] = useMutation(DELETE_CONNECTION);
|
||||
|
||||
@@ -75,6 +76,9 @@ function AppConnectionRow(props) {
|
||||
},
|
||||
});
|
||||
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ['apps', key, 'connections'],
|
||||
});
|
||||
enqueueSnackbar(formatMessage('connection.deletedMessage'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
@@ -86,7 +90,7 @@ function AppConnectionRow(props) {
|
||||
testConnection({ variables: { id } });
|
||||
}
|
||||
},
|
||||
[deleteConnection, id, testConnection, formatMessage, enqueueSnackbar],
|
||||
[deleteConnection, id, queryClient, key, enqueueSnackbar, formatMessage, testConnection],
|
||||
);
|
||||
|
||||
const relativeCreatedAt = DateTime.fromMillis(
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
@@ -7,16 +6,14 @@ import { useQueryClient } from '@tanstack/react-query';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import ConfirmationDialog from 'components/ConfirmationDialog';
|
||||
import { DELETE_USER } from 'graphql/mutations/delete-user.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useAdminUserDelete from 'hooks/useAdminUserDelete';
|
||||
|
||||
function DeleteUserButton(props) {
|
||||
const { userId } = props;
|
||||
const [showConfirmation, setShowConfirmation] = React.useState(false);
|
||||
const [deleteUser] = useMutation(DELETE_USER, {
|
||||
variables: { input: { id: userId } },
|
||||
refetchQueries: ['GetUsers'],
|
||||
});
|
||||
const { mutateAsync: deleteUser } = useAdminUserDelete(userId);
|
||||
|
||||
const formatMessage = useFormatMessage();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -33,7 +30,12 @@ function DeleteUserButton(props) {
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while deleting!');
|
||||
enqueueSnackbar(
|
||||
error?.message || formatMessage('deleteUserButton.deleteError'),
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [deleteUser]);
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useNavigate, Link as RouterLink } from 'react-router-dom';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Link from '@mui/material/Link';
|
||||
import Typography from '@mui/material/Typography';
|
||||
@@ -8,17 +7,20 @@ import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import useAuthentication from 'hooks/useAuthentication';
|
||||
import useCloud from 'hooks/useCloud';
|
||||
import * as URLS from 'config/urls';
|
||||
import { LOGIN } from 'graphql/mutations/login';
|
||||
import Form from 'components/Form';
|
||||
import TextField from 'components/TextField';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useCreateAccessToken from 'hooks/useCreateAccessToken';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
|
||||
function LoginForm() {
|
||||
const isCloud = useCloud();
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const authentication = useAuthentication();
|
||||
const [login, { loading }] = useMutation(LOGIN);
|
||||
const { mutateAsync: createAccessToken, isPending: loading } =
|
||||
useCreateAccessToken();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (authentication.isAuthenticated) {
|
||||
@@ -27,13 +29,19 @@ function LoginForm() {
|
||||
}, [authentication.isAuthenticated]);
|
||||
|
||||
const handleSubmit = async (values) => {
|
||||
const { data } = await login({
|
||||
variables: {
|
||||
input: values,
|
||||
},
|
||||
});
|
||||
const { token } = data.login;
|
||||
authentication.updateToken(token);
|
||||
try {
|
||||
const { email, password } = values;
|
||||
const { data } = await createAccessToken({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
const { token } = data;
|
||||
authentication.updateToken(token);
|
||||
} catch (error) {
|
||||
enqueueSnackbar(error?.message || formatMessage('loginForm.error'), {
|
||||
variant: 'error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import Paper from '@mui/material/Paper';
|
||||
@@ -7,11 +6,12 @@ import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import Form from 'components/Form';
|
||||
import TextField from 'components/TextField';
|
||||
import * as URLS from 'config/urls';
|
||||
import { RESET_PASSWORD } from 'graphql/mutations/reset-password.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useResetPassword from 'hooks/useResetPassword';
|
||||
|
||||
const validationSchema = yup.object().shape({
|
||||
password: yup.string().required('resetPasswordForm.mandatoryInput'),
|
||||
@@ -26,25 +26,35 @@ export default function ResetPasswordForm() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [resetPassword, { data, loading }] = useMutation(RESET_PASSWORD);
|
||||
const {
|
||||
mutateAsync: resetPassword,
|
||||
isPending,
|
||||
isSuccess,
|
||||
} = useResetPassword();
|
||||
const token = searchParams.get('token');
|
||||
|
||||
const handleSubmit = async (values) => {
|
||||
await resetPassword({
|
||||
variables: {
|
||||
input: {
|
||||
password: values.password,
|
||||
token,
|
||||
const { password } = values;
|
||||
try {
|
||||
await resetPassword({
|
||||
password,
|
||||
token,
|
||||
});
|
||||
enqueueSnackbar(formatMessage('resetPasswordForm.passwordUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-reset-password-success',
|
||||
},
|
||||
},
|
||||
});
|
||||
enqueueSnackbar(formatMessage('resetPasswordForm.passwordUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-reset-password-success',
|
||||
},
|
||||
});
|
||||
navigate(URLS.LOGIN);
|
||||
});
|
||||
navigate(URLS.LOGIN);
|
||||
} catch (error) {
|
||||
enqueueSnackbar(
|
||||
error?.message || formatMessage('resetPasswordForm.error'),
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -113,8 +123,8 @@ export default function ResetPasswordForm() {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
sx={{ boxShadow: 2, my: 3 }}
|
||||
loading={loading}
|
||||
disabled={data || !token}
|
||||
loading={isPending}
|
||||
disabled={isSuccess || !token}
|
||||
fullWidth
|
||||
>
|
||||
{formatMessage('resetPasswordForm.submit')}
|
||||
|
@@ -11,8 +11,10 @@ import * as URLS from 'config/urls';
|
||||
import { REGISTER_USER } from 'graphql/mutations/register-user.ee';
|
||||
import Form from 'components/Form';
|
||||
import TextField from 'components/TextField';
|
||||
import { LOGIN } from 'graphql/mutations/login';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useCreateAccessToken from 'hooks/useCreateAccessToken';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
|
||||
const validationSchema = yup.object().shape({
|
||||
fullName: yup.string().trim().required('signupForm.mandatoryInput'),
|
||||
email: yup
|
||||
@@ -26,39 +28,57 @@ const validationSchema = yup.object().shape({
|
||||
.required('signupForm.mandatoryInput')
|
||||
.oneOf([yup.ref('password')], 'signupForm.passwordsMustMatch'),
|
||||
});
|
||||
|
||||
const initialValues = {
|
||||
fullName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
};
|
||||
|
||||
function SignUpForm() {
|
||||
const navigate = useNavigate();
|
||||
const authentication = useAuthentication();
|
||||
const formatMessage = useFormatMessage();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const [registerUser, { loading: registerUserLoading }] =
|
||||
useMutation(REGISTER_USER);
|
||||
const [login, { loading: loginLoading }] = useMutation(LOGIN);
|
||||
const { mutateAsync: createAccessToken, isPending: loginLoading } =
|
||||
useCreateAccessToken();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (authentication.isAuthenticated) {
|
||||
navigate(URLS.DASHBOARD);
|
||||
}
|
||||
}, [authentication.isAuthenticated]);
|
||||
|
||||
const handleSubmit = async (values) => {
|
||||
const { fullName, email, password } = values;
|
||||
|
||||
await registerUser({
|
||||
variables: {
|
||||
input: { fullName, email, password },
|
||||
input: {
|
||||
fullName,
|
||||
email,
|
||||
password,
|
||||
},
|
||||
},
|
||||
});
|
||||
const { data } = await login({
|
||||
variables: {
|
||||
input: { email, password },
|
||||
},
|
||||
});
|
||||
const { token } = data.login;
|
||||
authentication.updateToken(token);
|
||||
|
||||
try {
|
||||
const { data } = await createAccessToken({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
const { token } = data;
|
||||
authentication.updateToken(token);
|
||||
} catch (error) {
|
||||
enqueueSnackbar(error?.message || formatMessage('signupForm.error'), {
|
||||
variant: 'error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper sx={{ px: 2, py: 4 }}>
|
||||
<Typography
|
||||
@@ -168,4 +188,5 @@ function SignUpForm() {
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignUpForm;
|
||||
|
@@ -1,6 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
export const DELETE_USER = gql`
|
||||
mutation DeleteUser($input: DeleteUserInput) {
|
||||
deleteUser(input: $input)
|
||||
}
|
||||
`;
|
@@ -1,12 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
export const LOGIN = gql`
|
||||
mutation Login($input: LoginInput) {
|
||||
login(input: $input) {
|
||||
token
|
||||
user {
|
||||
id
|
||||
email
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@@ -1,6 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
export const RESET_PASSWORD = gql`
|
||||
mutation ResetPassword($input: ResetPasswordInput) {
|
||||
resetPassword(input: $input)
|
||||
}
|
||||
`;
|
15
packages/web/src/hooks/useAdminUserDelete.js
Normal file
15
packages/web/src/hooks/useAdminUserDelete.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useAdminUserDelete(userId) {
|
||||
const mutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const { data } = await api.delete(`/v1/admin/users/${userId}`);
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return mutation;
|
||||
}
|
15
packages/web/src/hooks/useCreateAccessToken.js
Normal file
15
packages/web/src/hooks/useCreateAccessToken.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useCreateAccessToken() {
|
||||
const query = useMutation({
|
||||
mutationFn: async ({ email, password }) => {
|
||||
const { data } = await api.post('/v1/access-tokens', { email, password });
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
15
packages/web/src/hooks/useResetPassword.js
Normal file
15
packages/web/src/hooks/useResetPassword.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useResetPassword() {
|
||||
const mutation = useMutation({
|
||||
mutationFn: async (payload) => {
|
||||
const { data } = await api.post('/v1/users/reset-password', payload);
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
return mutation;
|
||||
}
|
@@ -144,6 +144,7 @@
|
||||
"signupForm.validateEmail": "Email must be valid.",
|
||||
"signupForm.passwordsMustMatch": "Passwords must match.",
|
||||
"signupForm.mandatoryInput": "{inputName} is required.",
|
||||
"signupForm.error": "Something went wrong. Please try again.",
|
||||
"loginForm.title": "Login",
|
||||
"loginForm.emailFieldLabel": "Email",
|
||||
"loginForm.passwordFieldLabel": "Password",
|
||||
@@ -152,6 +153,7 @@
|
||||
"loginForm.noAccount": "Don't have an Automatisch account yet?",
|
||||
"loginForm.signUp": "Sign up",
|
||||
"loginPage.divider": "OR",
|
||||
"loginForm.error": "Something went wrong. Please try again.",
|
||||
"ssoProviders.loginWithProvider": "Login with {providerName}",
|
||||
"forgotPasswordForm.title": "Forgot password",
|
||||
"forgotPasswordForm.submit": "Send reset instructions",
|
||||
@@ -165,6 +167,7 @@
|
||||
"resetPasswordForm.passwordFieldLabel": "Password",
|
||||
"resetPasswordForm.confirmPasswordFieldLabel": "Confirm password",
|
||||
"resetPasswordForm.passwordUpdated": "The password has been updated. Now, you can login.",
|
||||
"resetPasswordForm.error": "Something went wrong. Please try again.",
|
||||
"acceptInvitationForm.passwordsMustMatch": "Passwords must match.",
|
||||
"acceptInvitationForm.mandatoryInput": "{inputName} is required.",
|
||||
"acceptInvitationForm.title": "Accept invitation",
|
||||
@@ -210,6 +213,7 @@
|
||||
"deleteUserButton.cancel": "Cancel",
|
||||
"deleteUserButton.confirm": "Delete",
|
||||
"deleteUserButton.successfullyDeleted": "The user has been deleted.",
|
||||
"deleteUserButton.deleteError": "Failed while deleting!",
|
||||
"createUserPage.title": "Create user",
|
||||
"userForm.fullName": "Full name",
|
||||
"userForm.email": "Email",
|
||||
|
@@ -32,11 +32,11 @@ import useAuthentication from 'hooks/useAuthentication';
|
||||
import Installation from 'pages/Installation';
|
||||
|
||||
function Routes() {
|
||||
const { data: configData } = useAutomatischConfig();
|
||||
const { data: configData, isSuccess } = useAutomatischConfig();
|
||||
const { isAuthenticated } = useAuthentication();
|
||||
const config = configData?.data;
|
||||
|
||||
const installed = configData?.data?.['installation.completed'] === true;
|
||||
const installed = isSuccess ? config?.['installation.completed'] === true : true;
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
|
Reference in New Issue
Block a user