feat: add change password tab to user profile settings
This commit is contained in:
parent
e64f6f8569
commit
6761d4571e
@ -4,8 +4,9 @@ import GlobalTopNavbar from 'App/TopNavbar';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getAccessToken } from 'shared/utils/accessToken';
|
||||
import Settings from 'shared/components/Settings';
|
||||
import { useMeQuery, useClearProfileAvatarMutation } from 'shared/generated/graphql';
|
||||
import { useMeQuery, useClearProfileAvatarMutation, useUpdateUserPasswordMutation } from 'shared/generated/graphql';
|
||||
import axios from 'axios';
|
||||
import { useCurrentUser } from 'App/context';
|
||||
|
||||
const MainContent = styled.div`
|
||||
padding: 0 0 50px 80px;
|
||||
@ -16,10 +17,16 @@ const MainContent = styled.div`
|
||||
const Projects = () => {
|
||||
const $fileUpload = useRef<HTMLInputElement>(null);
|
||||
const [clearProfileAvatar] = useClearProfileAvatarMutation();
|
||||
const { user } = useCurrentUser();
|
||||
const [updateUserPassword] = useUpdateUserPasswordMutation();
|
||||
const { loading, data, refetch } = useMeQuery();
|
||||
useEffect(() => {
|
||||
document.title = 'Profile | Taskcafé';
|
||||
}, []);
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
@ -57,6 +64,10 @@ const Projects = () => {
|
||||
$fileUpload.current.click();
|
||||
}
|
||||
}}
|
||||
onResetPassword={(password, done) => {
|
||||
updateUserPassword({ variables: { userID: user.id, password } });
|
||||
done();
|
||||
}}
|
||||
onProfileAvatarRemove={() => {
|
||||
clearProfileAvatar();
|
||||
}}
|
||||
|
@ -27,6 +27,7 @@ export const Default = () => {
|
||||
<BaseStyles />
|
||||
<Settings
|
||||
profile={profile}
|
||||
onResetPassword={action('reset password')}
|
||||
onProfileAvatarRemove={action('remove')}
|
||||
onProfileAvatarChange={action('profile avatar change')}
|
||||
/>
|
||||
|
@ -3,6 +3,17 @@ import styled from 'styled-components';
|
||||
import { User } from 'shared/icons';
|
||||
import Input from 'shared/components/Input';
|
||||
import Button from 'shared/components/Button';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
const PasswordInput = styled(Input)`
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
const FormError = styled.span`
|
||||
font-size: 12px;
|
||||
color: rgba(${props => props.theme.colors.warning});
|
||||
`;
|
||||
|
||||
const ProfileContainer = styled.div`
|
||||
display: flex;
|
||||
@ -218,6 +229,7 @@ const SettingActions = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
`;
|
||||
|
||||
const SaveButton = styled(Button)`
|
||||
@ -228,10 +240,73 @@ const SaveButton = styled(Button)`
|
||||
type SettingsProps = {
|
||||
onProfileAvatarChange: () => void;
|
||||
onProfileAvatarRemove: () => void;
|
||||
onResetPassword: (password: string, done: () => void) => void;
|
||||
profile: TaskUser;
|
||||
};
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({ onProfileAvatarRemove, onProfileAvatarChange, profile }) => {
|
||||
type TabProps = {
|
||||
tab: number;
|
||||
currentTab: number;
|
||||
};
|
||||
|
||||
const Tab: React.FC<TabProps> = ({ tab, currentTab, children }) => {
|
||||
if (tab !== currentTab) {
|
||||
return null;
|
||||
}
|
||||
return <TabContent>{children}</TabContent>;
|
||||
};
|
||||
|
||||
type ResetPasswordTabProps = {
|
||||
onResetPassword: (password: string, done: () => void) => void;
|
||||
};
|
||||
const ResetPasswordTab: React.FC<ResetPasswordTabProps> = ({ onResetPassword }) => {
|
||||
const [active, setActive] = useState(true);
|
||||
const { register, handleSubmit, errors, setError, reset } = useForm<{ password: string; password_confirm: string }>();
|
||||
const done = () => {
|
||||
reset();
|
||||
setActive(true);
|
||||
};
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(data => {
|
||||
console.log(`${data.password} !== ${data.password_confirm}`);
|
||||
if (data.password !== data.password_confirm) {
|
||||
setError('password', { message: 'Passwords must match!', type: 'error' });
|
||||
setError('password_confirm', { message: 'Passwords must match!', type: 'error' });
|
||||
} else {
|
||||
onResetPassword(data.password, done);
|
||||
}
|
||||
})}
|
||||
>
|
||||
<PasswordInput
|
||||
width="100%"
|
||||
ref={register({ required: 'Password is required' })}
|
||||
label="Password"
|
||||
name="password"
|
||||
/>
|
||||
{errors.password && <FormError>{errors.password.message}</FormError>}
|
||||
<PasswordInput
|
||||
width="100%"
|
||||
ref={register({ required: 'Password is required' })}
|
||||
label="Password (confirm)"
|
||||
name="password_confirm"
|
||||
/>
|
||||
{errors.password_confirm && <FormError>{errors.password_confirm.message}</FormError>}
|
||||
<SettingActions>
|
||||
<SaveButton disabled={!active} type="submit">
|
||||
Save Change
|
||||
</SaveButton>
|
||||
</SettingActions>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({
|
||||
onProfileAvatarRemove,
|
||||
onProfileAvatarChange,
|
||||
onResetPassword,
|
||||
profile,
|
||||
}) => {
|
||||
const [currentTab, setTab] = useState(0);
|
||||
const [currentTop, setTop] = useState(0);
|
||||
const $tabNav = useRef<HTMLDivElement>(null);
|
||||
@ -257,7 +332,7 @@ const Settings: React.FC<SettingsProps> = ({ onProfileAvatarRemove, onProfileAva
|
||||
</TabNavContent>
|
||||
</TabNav>
|
||||
<TabContentWrapper>
|
||||
<TabContent>
|
||||
<Tab tab={0} currentTab={currentTab}>
|
||||
<AvatarSettings
|
||||
onProfileAvatarRemove={onProfileAvatarRemove}
|
||||
onProfileAvatarChange={onProfileAvatarChange}
|
||||
@ -275,7 +350,10 @@ const Settings: React.FC<SettingsProps> = ({ onProfileAvatarRemove, onProfileAva
|
||||
<SettingActions>
|
||||
<SaveButton>Save Change</SaveButton>
|
||||
</SettingActions>
|
||||
</TabContent>
|
||||
</Tab>
|
||||
<Tab tab={1} currentTab={currentTab}>
|
||||
<ResetPasswordTab onResetPassword={onResetPassword} />
|
||||
</Tab>
|
||||
</TabContentWrapper>
|
||||
</Container>
|
||||
);
|
||||
|
@ -1935,6 +1935,20 @@ export type DeleteUserAccountMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateUserPasswordMutationVariables = {
|
||||
userID: Scalars['UUID'];
|
||||
password: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type UpdateUserPasswordMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateUserPassword: (
|
||||
{ __typename?: 'UpdateUserPasswordPayload' }
|
||||
& Pick<UpdateUserPasswordPayload, 'ok'>
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateUserRoleMutationVariables = {
|
||||
userID: Scalars['UUID'];
|
||||
roleCode: RoleCode;
|
||||
@ -3975,6 +3989,39 @@ export function useDeleteUserAccountMutation(baseOptions?: ApolloReactHooks.Muta
|
||||
export type DeleteUserAccountMutationHookResult = ReturnType<typeof useDeleteUserAccountMutation>;
|
||||
export type DeleteUserAccountMutationResult = ApolloReactCommon.MutationResult<DeleteUserAccountMutation>;
|
||||
export type DeleteUserAccountMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteUserAccountMutation, DeleteUserAccountMutationVariables>;
|
||||
export const UpdateUserPasswordDocument = gql`
|
||||
mutation updateUserPassword($userID: UUID!, $password: String!) {
|
||||
updateUserPassword(input: {userID: $userID, password: $password}) {
|
||||
ok
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UpdateUserPasswordMutationFn = ApolloReactCommon.MutationFunction<UpdateUserPasswordMutation, UpdateUserPasswordMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useUpdateUserPasswordMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateUserPasswordMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateUserPasswordMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [updateUserPasswordMutation, { data, loading, error }] = useUpdateUserPasswordMutation({
|
||||
* variables: {
|
||||
* userID: // value for 'userID'
|
||||
* password: // value for 'password'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateUserPasswordMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<UpdateUserPasswordMutation, UpdateUserPasswordMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<UpdateUserPasswordMutation, UpdateUserPasswordMutationVariables>(UpdateUserPasswordDocument, baseOptions);
|
||||
}
|
||||
export type UpdateUserPasswordMutationHookResult = ReturnType<typeof useUpdateUserPasswordMutation>;
|
||||
export type UpdateUserPasswordMutationResult = ApolloReactCommon.MutationResult<UpdateUserPasswordMutation>;
|
||||
export type UpdateUserPasswordMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateUserPasswordMutation, UpdateUserPasswordMutationVariables>;
|
||||
export const UpdateUserRoleDocument = gql`
|
||||
mutation updateUserRole($userID: UUID!, $roleCode: RoleCode!) {
|
||||
updateUserRole(input: {userID: $userID, roleCode: $roleCode}) {
|
||||
|
11
frontend/src/shared/graphql/user/updateUserPassword.ts
Normal file
11
frontend/src/shared/graphql/user/updateUserPassword.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const UPDATE_USER_PASSWORD_MUTATION = gql`
|
||||
mutation updateUserPassword($userID: UUID!, $password: String!) {
|
||||
updateUserPassword(input: { userID: $userID, password: $password }) {
|
||||
ok
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default UPDATE_USER_PASSWORD_MUTATION;
|
Loading…
Reference in New Issue
Block a user