@apollo/client#useMutation TypeScript Examples

The following examples show how to use @apollo/client#useMutation. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: entityRestore.ts    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
export function useEntityRestore(restoreEntitesVariables: RestoreEntitiesVariables, onCompleteCallback?: () => Promise<any>) {
  const intl = useIntl();
  const {className, ids} = restoreEntitesVariables;
  const [restoreEntities] = useMutation<RestoreData, RestoreEntitiesVariables>(
    gqlRestoreEntity,
    {
      fetchPolicy: "no-cache"
    }
  );

  const restore = () => {
    restoreEntities({
      variables: {
        className, 
        ids
      }
    }).then(({data}) => {
      if(data != null) {
        restoreEntitesHandler(data, intl, onCompleteCallback);
      }
    }).catch(({message}: ApolloError) => {
      restoreEntitesErrorHandler(message, intl);
    })
  };

  return restore;
}
Example #2
Source File: useUpdateThreadPoint.ts    From Full-Stack-React-TypeScript-and-Node with MIT License 6 votes vote down vote up
useUpdateThreadPoint = (
  refreshThread?: () => void,
  threadId?: string
) => {
  const [execUpdateThreadPoint] = useMutation(UpdateThreadPoint);

  const onClickIncThreadPoint = async (
    e: React.MouseEvent<SVGSVGElement, MouseEvent>
  ) => {
    e.preventDefault();

    await execUpdateThreadPoint({
      variables: {
        threadId,
        increment: true,
      },
    });
    refreshThread && refreshThread();
  };
  const onClickDecThreadPoint = async (
    e: React.MouseEvent<SVGSVGElement, MouseEvent>
  ) => {
    e.preventDefault();

    await execUpdateThreadPoint({
      variables: {
        threadId,
        increment: false,
      },
    });
    refreshThread && refreshThread();
  };

  return {
    onClickIncThreadPoint,
    onClickDecThreadPoint,
  };
}
Example #3
Source File: AuthAppWithGitCallback.tsx    From amplication with Apache License 2.0 6 votes vote down vote up
AuthAppWithGitCallback = () => {
  const { trackEvent } = useTracking();
  const [completeAuthWithGit] = useMutation<Boolean>(CREATE_GIT_ORGANIZATION, {
    onCompleted: (data) => {
      window.opener.postMessage({ completed: true });
      // close the popup
      window.close();
    },
  });

  useEffect(() => {
    // get the URL parameters with the code and state values
    const queryString = window.location.search;
    const urlParams = new URLSearchParams(queryString);
    const installationId = urlParams.get("installation_id");
    if (window.opener) {
      trackEvent({
        eventName: "completeAuthAppWithGitHub",
      });
      completeAuthWithGit({
        variables: {
          installationId,
          gitProvider: "Github",
        },
      }).catch(console.error);
    }
  }, [completeAuthWithGit, trackEvent]);

  /**@todo: show formatted layout and optional error message */
  return <p>Please wait...</p>;
}
Example #4
Source File: Apollo.ts    From graphql-ts-client with MIT License 6 votes vote down vote up
export function useTypedMutation<
    TData extends object,
    TVariables extends object,
    TContext = DefaultContext, 
	TCache extends ApolloCache<any> = ApolloCache<any>
>(
    fetcher: Fetcher<"Mutation", TData, TVariables>,
    options?: MutationHookOptions<TData, TVariables, TContext> & {
        readonly operationName?: string
	}
): MutationTuple<
    TData, 
    TVariables, 
    TContext, 
    TCache
> {
    const body = requestBody(fetcher);

    const request = useMemo<DocumentNode>(() => {
		const operationName = options?.operationName ?? `mutation_${util.toMd5(body)}`;
		return gql`mutation ${operationName}${body}`;
	}, [body, options?.operationName]);

	const response = useMutation<
		TData, 
		TVariables, 
		TContext, 
		TCache
	>(request, options);
	const responseData = response[1].data;
	const newResponseData = useMemo(() => util.exceptNullValues(responseData), [responseData]);
	return newResponseData === responseData ? response : [
        response[0],
        { ...response[1], data: newResponseData }
    ];
}
Example #5
Source File: TerminateFlow.tsx    From glific-frontend with GNU Affero General Public License v3.0 6 votes vote down vote up
TerminateFlow: React.FC<TerminateFlowProps> = ({
  contactId,
  setDialog,
}: TerminateFlowProps) => {
  const [terminateFlow] = useMutation(TERMINATE_FLOW, {
    onCompleted: ({ terminateContactFlows }) => {
      if (terminateContactFlows.success) {
        setNotification('Flow terminated successfully');
      } else if (terminateContactFlows.errors) {
        setNotification(terminateContactFlows.errors[0].message, 'warning');
      }
    },
  });

  const handleTerminateFlow = () => {
    terminateFlow({ variables: { contactId } });
    setDialog(false);
  };

  return (
    <DialogBox
      title="Terminate flows!"
      alignButtons="center"
      buttonOk="YES, TERMINATE"
      buttonCancel="NO, CANCEL"
      additionalTitleStyles={styles.Title}
      colorOk="secondary"
      handleOk={handleTerminateFlow}
      handleCancel={() => {
        setDialog(false);
      }}
    >
      <div className={styles.Dialog}>
        All active flows for the contact will be stopped. They can initiate a flow via keyword or
        you will need to do it manually.
      </div>
    </DialogBox>
  );
}
Example #6
Source File: MainRoomComponent.tsx    From dh-web with GNU General Public License v3.0 6 votes vote down vote up
RoomComponent: FC = () => {

    const router = useRouter();

    const [joinRoom, { error, loading }] = useMutation<RoomJoinQuery>(ROOM_JOIN_QUERY, { errorPolicy: "all" });

    useEffect(() => {
        if (router.query["roomId"]) {
            joinRoom({ variables: { room_id: router.query["roomId"] || "" } });
        }
    }, [joinRoom, router.query]);

    if (error) return (<p>{error.message}</p>);
    if (loading) return (<p>...Loading</p>);


    return (
        <MainRoomComponent room_id={router.query["roomId"].toString() || ""} />
    );
}
Example #7
Source File: form.create.mutation.ts    From ui with GNU Affero General Public License v3.0 6 votes vote down vote up
useFormCreateMutation = (
  options: MutationHookOptions<Data, Variables> = {}
): MutationTuple<Data, Variables> => {
  const oldUpdate = options.update

  options.update = (cache, result, options) => {
    cache.evict({
      fieldName: 'listForms',
    })
    cache.gc()

    if (oldUpdate) {
      oldUpdate(cache, result, options)
    }
  }

  return useMutation<Data, Variables>(MUTATION, options)
}
Example #8
Source File: useSignup.ts    From magento_react_native_graphql with MIT License 6 votes vote down vote up
useSignup = (): Result<SignupForm> => {
  const [createCustomer, { loading, data, error }] = useMutation<
    CreateCustomerDataType,
    CreateCustomerVars
  >(CREATE_CUSTOMER);
  const { values, handleChange, handleSubmit } = useForm<SignupForm>({
    initialValues: {
      firstName: '',
      lastName: '',
      email: '',
      password: '',
      secureTextEntry: true,
    },
    onSubmit: async _values => {
      try {
        await createCustomer({
          variables: {
            firstName: _values.firstName,
            lastName: _values.lastName,
            email: _values.email,
            password: _values.password,
          },
        });
      } catch {}
    },
  });

  return {
    values,
    data,
    error,
    loading,
    handleChange,
    handleSubmit,
  };
}
Example #9
Source File: Logout.tsx    From Full-Stack-React-TypeScript-and-Node with MIT License 5 votes vote down vote up
Logout: FC<ModalProps> = ({ isOpen, onClickToggle }) => {
  const user = useSelector((state: AppState) => state.user);
  const [execLogout] = useMutation(LogoutMutation, {
    refetchQueries: [
      {
        query: Me,
      },
    ],
  });
  const { deleteMe } = useRefreshReduxMe();

  const onClickLogin = async (
    e: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    e.preventDefault();
    onClickToggle(e);
    await execLogout({
      variables: {
        userName: user?.userName ?? "",
      },
    });
    deleteMe();
  };

  const onClickCancel = (
    e: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    onClickToggle(e);
  };

  return (
    <ReactModal
      className="modal-menu"
      isOpen={isOpen}
      onRequestClose={onClickToggle}
      shouldCloseOnOverlayClick={true}
    >
      <form>
        <div className="logout-inputs">Are you sure you want to logout?</div>
        <div className="form-buttons form-buttons-sm">
          <div className="form-btn-left">
            <button
              style={{ marginLeft: ".5em" }}
              className="action-btn"
              onClick={onClickLogin}
            >
              Login
            </button>
            <button
              style={{ marginLeft: ".5em" }}
              className="cancel-btn"
              onClick={onClickCancel}
            >
              Close
            </button>
          </div>
        </div>
      </form>
    </ReactModal>
  );
}
Example #10
Source File: GithubSyncDetails.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
function GithubSyncDetails({ gitRepositoryWithOrganization }: Props) {
  const gitRepositoryFullName = `${gitRepositoryWithOrganization.gitOrganization.name}/${gitRepositoryWithOrganization.name}`;
  const [deleteGitRepository, { error: errorUpdate }] = useMutation(
    DELETE_GIT_REPOSITORY,
    {
      variables: { gitRepositoryId: gitRepositoryWithOrganization.id },
    }
  );

  const handleDeleteGitRepository = useCallback(() => {
    deleteGitRepository({
      variables: { gitRepositoryId: gitRepositoryWithOrganization.id },
    }).catch(console.error);
  }, [deleteGitRepository, gitRepositoryWithOrganization.id]);

  const errorMessage = formatError(errorUpdate);
  const repoUrl = `https://github.com/${gitRepositoryFullName}`;

  return (
    <div className={CLASS_NAME}>
      <div className={`${CLASS_NAME}__body`}>
        <div className={`${CLASS_NAME}__details`}>
          <div className={`${CLASS_NAME}__name`}>{gitRepositoryFullName}</div>
          <div>
            <a href={repoUrl} target="github_repo">
              {repoUrl}
            </a>
          </div>
        </div>

        <div className={`${CLASS_NAME}__action`}>
          <Button
            buttonStyle={EnumButtonStyle.Primary}
            eventData={{
              eventName: "changeGithubRepo",
            }}
            onClick={handleDeleteGitRepository}
          >
            Change Repository
          </Button>
        </div>
      </div>

      <Snackbar open={Boolean(errorUpdate)} message={errorMessage} />
    </div>
  );
}
Example #11
Source File: add-new-feed-form.tsx    From nextjs-strapi-boilerplate with MIT License 5 votes vote down vote up
AddNewFeedForm = () => {
  const { colorMode } = useColorMode();
  const bgColor = { light: "white", dark: "gray.800" };
  const color = { light: "gray.800", dark: "gray.100" };
  const [body, setBody] = useState("");
  const { data:session, status } = useSession();

  if (!session) {
    return (
      <AccessDeniedIndicator message="You need to be signed in to add a new feed!" />
    );
  }

  const [
    insertFeed,
    { loading: insertFeedFetching, error: insertFeedError },
  ] = useMutation(insertFeedMutation);

  const handleSubmit = async () => {
    await insertFeed({ variables: { userId: session.id, body } });

    setBody("");
  };

  const errorNode = () => {
    if (!insertFeedError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{insertFeedError.message}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={4}>
      {errorNode()}
      <Box
        p={4}
        bg={bgColor[colorMode]}
        color={color[colorMode]}
        shadow="lg"
        rounded="lg"
      >
        <Stack spacing={4}>
          <FormControl isRequired>
            <FormLabel htmlFor="body">What's on your mind?</FormLabel>
            <Textarea
              id="body"
              value={body}
              onChange={(e: FormEvent<HTMLInputElement>) =>
                setBody(e.currentTarget.value)
              }
              isDisabled={insertFeedFetching}
            />
          </FormControl>
          <FormControl>
            <Button
              loadingText="Posting..."
              onClick={handleSubmit}
              isLoading={insertFeedFetching}
              isDisabled={!body.trim()}
            >
              Post
            </Button>
          </FormControl>
        </Stack>
      </Box>
    </Stack>
  );
}
Example #12
Source File: Login.tsx    From HoldemSolver with MIT License 5 votes vote down vote up
function Login(props: Props): React.ReactElement {
    const { login: setCredentials } = props;
    let history = useHistory();
    const [error, setError] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [
        login,
        { loading }
      ] = useMutation(LOGIN, {
          onCompleted(res) {
              const { jwt, csrf } = res.auth.login;
              // set auth keys
              setCredentials(jwt, csrf);
              // push to home
              history.push('/home');
          },
          onError({ message }) {
              setError(message);
          }
      });

    function onEmailChange(e: ChangeEvent<HTMLInputElement>) {
        const value = (e.target as HTMLInputElement).value;
        setEmail(value);
    }
    function onPasswordChange(e: ChangeEvent<HTMLInputElement>) {
        e.preventDefault();
        const value = (e.target as HTMLInputElement).value;
        setPassword(value);
    }
    function onSubmit(e: MouseEvent<HTMLButtonElement>) {
        e.preventDefault();
        setError('');
        if (email && password)
            login({ variables: { email, password }});
    }
    return (
        <LoginStyle>
            <form className="login-container">
                <h1>Login</h1>
                <p className="login-error">{error}</p>
                <Input className="login-field" label="Email" name="email" onChange={onEmailChange} value={email}/>
                <Input className="login-field" type="password" label="Password" name="password" onChange={onPasswordChange} value={password}/>
                <Button size="lg" isLoading={loading} onClick={onSubmit} type="submit" className="login-btn" block variant="primary">Login</Button>
                <p>Don't have an account? <Link to="/register">Sign up</Link></p>
            </form>
        </LoginStyle>
    );
}
Example #13
Source File: middleware.tsx    From liferay-grow with MIT License 5 votes vote down vote up
AuthMiddleware: React.FC = () => {
  const router = useRouter();
  const i18n = useLang();

  const docs = {
    'not-a-liferay-member': {
      link:
        'https://github.com/liferay-labs-br/liferay-grow/blob/master/docs/not-a-liferay-member.md',
      message: i18n.sub('read-more-about-x', 'not-a-liferay-member'),
    },
  };

  const [onAuthGithub, { loading }] = useMutation(authGithub);
  const { dispatch } = useContext(AppContext);
  const [error, setError] = useState<string>();

  const getUserToken = async () => {
    const urlParams = new URLSearchParams(location.search);
    const code = urlParams.get('code');

    if (code) {
      try {
        const {
          data: { authGithub: bearer },
        } = await onAuthGithub({ variables: { code } });

        dispatch({ payload: { token: bearer }, type: Types.SET_LOGGED_USER });

        const decodedToken = parseJwt(bearer);

        toast.info(i18n.sub('welcome-x', decodedToken?.name));

        router.replace(
          decodedToken?.user?.growMap ? ROUTES.HOME : ROUTES.WELCOME,
        );
      } catch (err) {
        toast.error(i18n.get(err.message));

        setError(err.message);
      }
    } else {
      router.replace(ROUTES.AUTH);
    }
  };

  useEffect(() => {
    getUserToken();
  }, []);

  return (
    <AuthTemplate>
      <SEO title={i18n.sub('app-title-x', 'Auth Middleware')} />

      {error ? (
        <div>
          {docs[error] ? (
            <p>
              <a target="_blank" href={docs[error].link} rel="noreferrer">
                {docs[error].message}
              </a>
            </p>
          ) : (
            <p>{i18n.get(error)}</p>
          )}

          <ClayButton className="mt-4" onClick={() => router.replace('/auth')}>
            {i18n.get('try-again')}
          </ClayButton>
        </div>
      ) : loading ? (
        <LoadingWrapper />
      ) : (
        <div>{`${i18n.get('redirecting')}...`}</div>
      )}
    </AuthTemplate>
  );
}
Example #14
Source File: MainRoomComponent.tsx    From dh-web with GNU General Public License v3.0 5 votes vote down vote up
MainRoomComponent: FC<MainRoomComponentProperties> = ({ room_id }: MainRoomComponentProperties) => {

    const { data, error, loading } = useQuery<RoomDataQuery>(ROOM_DATA, { variables: { room_id } });
    const [leaveRoom] = useMutation(LEAVE_ROOM);
    const router = useRouter();

    // const theme = useTheme();
    // const one = useMediaQuery(`(min-width:${theme.breakpoints.one + 1}px)`);
    // const two = useMediaQuery(`(min-width:${theme.breakpoints.two + 1}px)`);
    // const three = useMediaQuery(`(min-width:${theme.breakpoints.three + 1}px)`);

    if (loading || error) {
        return <></>;
    }

    return (
        <RoomCard>
            <RoomHeader>
                <Title>{data.room.name}</Title>
                {/*<SubTitle> // TODO: Impelement as soon as API Endpoints are available
                    with
                    <span>{"owner"}</span>
                </SubTitle> */}
                <Description>{data.room.description}</Description>
            </RoomHeader>
            <RoomMembers>
                {
                    data.room.members.map((member) => (
                        <div key={member.user.id}>{member.user.username}</div>
                    ))
                }
            </RoomMembers>
            <RoomActions>
                <Button variant="PRIMARY"><img src="/microphone.svg" alt="Microphone Button" /></Button>
                <Button variant="PRIMARY"><img src="/headphone.svg" alt="Headphone  Button" /></Button>
                <Button variant="PRIMARY"><img src="/settings.svg" alt="Settings Button" /></Button>
                <Button variant="PRIMARY"><img src="/users-plus.svg" alt="Invite Button" /></Button>
                <Button variant="PRIMARY" onClick={async () => {
                    await leaveRoom();
                    router.push("/dash");
                }}>Leave</Button>
            </RoomActions>
        </RoomCard>

    );

}
Example #15
Source File: index.tsx    From tinyhouse with MIT License 5 votes vote down vote up
function App() {
    const [viewer, setViewer] = useState<Viewer>(initialViewer);
    const [logIn, { error }] = useMutation<LogInData, LogInVariables>(LOG_IN, {
        onCompleted: (data) => {
            if (data && data.logIn) {
                setViewer(data.logIn);
                if (data.logIn.token) {
                    sessionStorage.setItem("token", data.logIn.token);
                }
            } else {
                sessionStorage.removeItem("token");
            }
        },
    });
    const logInRef = useRef(logIn);
    useEffect(() => {
        logInRef.current();
    }, []);

    if (!viewer.didRequest && !error) {
        return (
            <Layout className="app-skeleton">
                <AppHeaderSkeleton />
                <div className="app-skeleton__spin-section">
                    <Spin size="large" tip="Launching TinyHouse" />
                </div>
            </Layout>
        );
    }

    const logInErrorBannerElement = error ? (
        <ErrorBanner description="We weren't able to verify if you were logged in. Please try again later!" />
    ) : null;
    return (
        <BrowserRouter>
            <Layout id="app">
                {logInErrorBannerElement}
                <Affix offsetTop={0} className="app__affix-header">
                    <AppHeader viewer={viewer} setViewer={setViewer} />
                </Affix>
                <Routes>
                    <Route path="/" element={<Home />} />
                    <Route path="/host" element={<Host />} />
                    <Route path="/listing/:listingId" element={<Listing />} />
                    <Route
                        path="/listings"
                        element={<Listings title="TinyHouse Listings" />}
                    >
                        <Route
                            path="/listings/:location"
                            element={<Listings title="TinyHouse Listings" />}
                        />
                    </Route>
                    <Route
                        path="/login"
                        element={<Login setViewer={setViewer} />}
                    />
                    <Route
                        path="/user/:userId"
                        element={<User viewer={viewer} />}
                    />
                    <Route path="*" element={<NotFound />} />
                </Routes>
            </Layout>
        </BrowserRouter>
    );
}
Example #16
Source File: LikeSet.tsx    From keycapsets.com with GNU General Public License v3.0 5 votes vote down vote up
function LikeSet(props: LikeSetProps) {
    const { keycapset, size = 16 } = props;
    const router: NextRouter = useRouter();
    const [addWantToUser] = useMutation(WANT_SET);

    const isLoggedIn = useStore((state) => state.isLoggedIn);
    const userWants = useStore((state) => state.userWants);
    const setUserWants = useStore((state) => state.setUserWants);

    function removeUserWants(id: string): Keycapset[] {
        const wantsClone = [...userWants];
        const indexOfSetInWants = userWants.map((s: Keycapset) => s._id).indexOf(id);
        wantsClone.splice(indexOfSetInWants, 1);
        return wantsClone;
    }

    function adduserWants(keycapset: Keycapset): Keycapset[] {
        return [...userWants, keycapset];
    }

    async function userWantSet(evt: React.MouseEvent<HTMLSpanElement>) {
        evt.preventDefault();
        evt.stopPropagation();

        if (isLoggedIn) {
            try {
                const { data: response } = await addWantToUser({
                    variables: {
                        setId: keycapset._id,
                    },
                });
                const isLiking: boolean = response.wantSet.message === 'liked';
                setUserWants(isLiking ? adduserWants(keycapset) : removeUserWants(keycapset._id));
            } catch (err) {
                console.error('want set err', { err });
            }
        } else {
            router.push('/sign-up');
        }
    }

    return (
        <span data-tip="Sign up to create collections" onClick={userWantSet} className="heart-icon">
            <HeartIcon
                filled={userWants.map((s: Keycapset) => s._id).includes(keycapset._id)}
                isDisabled={!isLoggedIn}
                width={size}
                height={size - 2}
            />
            {!isLoggedIn && <ReactTooltip delayHide={500} className="tooltip" effect="solid" />}
        </span>
    );
}
Example #17
Source File: use.submission.ts    From ui with GNU Affero General Public License v3.0 5 votes vote down vote up
useSubmission = (formId: string): Submission => {
  const [submission, setSubmission] = useState<{ id: string; token: string }>()

  const [start] = useMutation<SubmissionStartMutationData, SubmissionStartMutationVariables>(
    SUBMISSION_START_MUTATION
  )
  const [save] = useMutation<SubmissionSetFieldMutationData, SubmissionSetFieldMutationVariables>(
    SUBMISSION_SET_FIELD_MUTATION
  )
  const [submit] = useMutation<SubmissionFinishMutationData, SubmissionFinishMutationVariables>(
    SUBMISSION_FINISH_MUTATION
  )

  useEffect(() => {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
    const token = [...Array(40)].map(() => Math.random().toString(36)[2]).join('')

    start({
      variables: {
        form: formId,
        submission: {
          token,
          device: {
            name: /Mobi/i.test(window.navigator.userAgent) ? 'mobile' : 'desktop',
            type: window.navigator.userAgent,
          },
        },
      },
    })
      .then(({ data }) => {
        logger('submission id = %O', data.submission.id)
        setSubmission({
          id: data.submission.id,
          token,
        })
      })
      .catch((e: Error) => logger('failed to start submission %J', e))
  }, [formId])

  const setField = useCallback(
    async (fieldId: string, data: unknown) => {
      if (data === undefined || data === null) {
        logger('skip save field id=%O %O', fieldId, data)
        return
      }

      logger('save field id=%O %O', fieldId, data)
      await save({
        variables: {
          submission: submission.id,
          field: {
            token: submission.token,
            field: fieldId,
            data: JSON.stringify(data),
          },
        },
      })
    },
    [submission]
  )

  const finish = useCallback(async () => {
    logger('finish submission!!', formId)
    await submit({
      variables: {
        submission: submission.id,
      },
    })
  }, [submission])

  return {
    setField,
    finish,
  }
}
Example #18
Source File: useLogin.ts    From magento_react_native_graphql with MIT License 5 votes vote down vote up
useLogin = (): Result<LoginForm> => {
  const [createCustomerToken, { loading, data, error }] = useMutation<
    CreateCustomerTokenDataType,
    CreateCustomerTokenVars
  >(CREATE_CUSTOMER_TOKEN, {
    async update(cache, { data: _data }) {
      if (_data?.generateCustomerToken?.token) {
        await saveCustomerToken(_data.generateCustomerToken.token);
        cache.writeQuery({
          query: IS_LOGGED_IN,
          data: {
            isLoggedIn: true,
          },
        });
      }
    },
  });
  const { values, handleChange, handleSubmit } = useForm<LoginForm>({
    initialValues: {
      email: '',
      password: '',
      secureTextEntry: true,
    },
    onSubmit: async _values => {
      try {
        await createCustomerToken({
          variables: {
            email: _values.email,
            password: _values.password,
          },
        });
      } catch {}
    },
  });

  return {
    values,
    data,
    error,
    loading,
    handleChange,
    handleSubmit,
  };
}
Example #19
Source File: use-push-token-to-remote.ts    From beancount-mobile with MIT License 5 votes vote down vote up
useAddPushTokenToRemote = () => {
  const [mutate, { error, data }] = useMutation<
    addPushToken,
    addPushTokenVariables
  >(addPushTokenToRemote);

  return { error, mutate, data };
}
Example #20
Source File: useEntityEditor.ts    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
export function useEntityEditor<
  TEntity = unknown,
  TData extends Record<string, any> = Record<string, any>,
  TQueryVars extends HasId = HasId,
  TMutationVars = unknown
>(
  options: EntityEditorHookOptions<TEntity, TData, TQueryVars, TMutationVars>
): EntityEditorHookResult<TEntity, TData, TQueryVars, TMutationVars> {
  const multiScreen = useMultiScreen();

  const {
    loadQuery,
    loadQueryOptions,
    upsertMutation,
    upsertMutationOptions,
    entityId = multiScreen?.params?.entityId,
    entityName,
    routingPath,
    onCommit,
    entityInstance,
    useEntityEditorForm = useNoop,
    useEntityEditorFormValidation = useNoop,
    uiKit_to_jmixFront,
    persistEntityCallbacks,
    shouldNotGoToParentScreen
  } = options;

  const intl = useIntl();

  const updateResultName = `upsert_${dollarsToUnderscores(entityName)}`;
  const listQueryName = `${dollarsToUnderscores(entityName)}List`;

  const entityEditorState: EntityEditorState = useEntityEditorStore();

  const {
    item,
    relationOptions,
    executeLoadQuery,
    loadQueryResult
  } = useEntityEditorData({
    loadQuery,
    loadQueryOptions,
    entityInstance,
    entityId,
    entityName,
    cloneEntity: multiScreen?.params?.cloneEntity
  });

  useEntityEditorForm(item, entityName);

  const [executeUpsertMutation, upsertMutationResult] = useMutation<TData, TMutationVars>(upsertMutation, upsertMutationOptions);

  const serverValidationErrors = useExtractBeanValidationErrors(upsertMutationResult.error);
  const [executeClientValidation, clientValidationErrors] = useClientValidation();
  useEntityEditorFormValidation(clientValidationErrors || serverValidationErrors);

  const goToParentScreen = useParentScreen(routingPath, shouldNotGoToParentScreen);

  const handleCancelBtnClick = goToParentScreen;

  const handleSubmit = useSubmitCallback({
    executeUpsertMutation,
    updateResultName,
    listQueryName,
    entityName,
    goToParentScreen,
    entityId: multiScreen?.params?.cloneEntity
      ? undefined
      : entityId,
    entityInstance,
    onCommit,
    uiKit_to_jmixFront,
    persistEntityCallbacks,
    executeClientValidation
  });

  return {
    item,
    serverValidationErrors,
    relationOptions,
    executeLoadQuery,
    loadQueryResult,
    executeUpsertMutation,
    upsertMutationResult,
    entityEditorState,
    intl,
    handleSubmit,
    handleCancelBtnClick,
  };
}
Example #21
Source File: Login.tsx    From Full-Stack-React-TypeScript-and-Node with MIT License 4 votes vote down vote up
Login: FC<ModalProps> = ({ isOpen, onClickToggle }) => {
  const [execLogin] = useMutation(LoginMutation, {
    refetchQueries: [
      {
        query: Me,
      },
    ],
  });
  const [
    { userName, password, resultMsg, isSubmitDisabled },
    dispatch,
  ] = useReducer(userReducer, {
    userName: "test1",
    password: "Test123$%^",
    resultMsg: "",
    isSubmitDisabled: false,
  });
  const { execMe, updateMe } = useRefreshReduxMe();

  const onChangeUserName = (e: React.ChangeEvent<HTMLInputElement>) => {
    dispatch({ type: "userName", payload: e.target.value });
    if (!e.target.value)
      allowSubmit(dispatch, "Username cannot be empty", true);
    else allowSubmit(dispatch, "", false);
  };

  const onChangePassword = (e: React.ChangeEvent<HTMLInputElement>) => {
    dispatch({ type: "password", payload: e.target.value });
    if (!e.target.value)
      allowSubmit(dispatch, "Password cannot be empty", true);
    else allowSubmit(dispatch, "", false);
  };

  const onClickLogin = async (
    e: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    e.preventDefault();
    onClickToggle(e);
    const result = await execLogin({
      variables: {
        userName,
        password,
      },
    });
    console.log("login", result);
    execMe();
    updateMe();
  };

  const onClickCancel = (
    e: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    onClickToggle(e);
  };

  return (
    <ReactModal
      className="modal-menu"
      isOpen={isOpen}
      onRequestClose={onClickToggle}
      shouldCloseOnOverlayClick={true}
    >
      <form>
        <div className="reg-inputs">
          <div>
            <label>username</label>
            <input type="text" value={userName} onChange={onChangeUserName} />
          </div>
          <div>
            <label>password</label>
            <input
              type="password"
              placeholder="Password"
              value={password}
              onChange={onChangePassword}
            />
          </div>
        </div>
        <div className="form-buttons form-buttons-sm">
          <div className="form-btn-left">
            <button
              style={{ marginLeft: ".5em" }}
              className="action-btn"
              disabled={isSubmitDisabled}
              onClick={onClickLogin}
            >
              Login
            </button>
            <button
              style={{ marginLeft: ".5em" }}
              className="cancel-btn"
              onClick={onClickCancel}
            >
              Close
            </button>
          </div>

          <span className="form-btn-left">
            <strong>{resultMsg}</strong>
          </span>
        </div>
      </form>
    </ReactModal>
  );
}
Example #22
Source File: ApplicationAuthSettingForm.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
function ApplicationAuthSettingForm({ match }: Props) {
  const applicationId = match.params.application;

  const { data, error } = useQuery<{
    appSettings: models.AppSettings;
  }>(GET_APP_SETTINGS, {
    variables: {
      id: applicationId,
    },
  });

  const pendingChangesContext = useContext(PendingChangesContext);

  const { trackEvent } = useTracking();

  const [updateAppSettings, { error: updateError }] = useMutation<TData>(
    UPDATE_APP_SETTINGS,
    {
      onCompleted: (data) => {
        pendingChangesContext.addBlock(data.updateAppSettings.id);
      },
    }
  );

  const handleSubmit = useCallback(
    (data: models.AppSettings) => {
      const { dbHost, dbName, dbPassword, dbPort, dbUser, authProvider } = data;
      trackEvent({
        eventName: "updateAppSettings",
      });
      updateAppSettings({
        variables: {
          data: {
            dbHost,
            dbName,
            dbPassword,
            dbPort,
            dbUser,
            authProvider,
          },
          appId: applicationId,
        },
      }).catch(console.error);
    },
    [updateAppSettings, applicationId, trackEvent]
  );

  const errorMessage = formatError(error || updateError);

  return (
    <div className={CLASS_NAME}>
      {data?.appSettings && (
        <Formik
          initialValues={data.appSettings}
          validate={(values: models.AppSettings) =>
            validate(values, FORM_SCHEMA)
          }
          enableReinitialize
          onSubmit={handleSubmit}
        >
          {(formik) => {
            return (
              <Form>
                <FormikAutoSave debounceMS={2000} />
                <h3>Authentication Providers</h3>

                <p>
                  Select the authentication method to be used in the generated
                  app.
                </p>

                <div className={`${CLASS_NAME}__space`}>
                  <SelectField
                    label="Authentication provider"
                    name="authProvider"
                    options={Object.keys(models.EnumAuthProviderType).map(
                      (authProvider) => ({
                        label: authProvider,
                        value: authProvider,
                      })
                    )}
                  />
                </div>
              </Form>
            );
          }}
        </Formik>
      )}
      <Snackbar open={Boolean(error)} message={errorMessage} />
    </div>
  );
}
Example #23
Source File: index.tsx    From nextjs-strapi-boilerplate with MIT License 4 votes vote down vote up
MyAccountPageComponent = () => {
  const { colorMode } = useColorMode();
  const bgColor = { light: "white", dark: "gray.800" };
  const color = { light: "gray.800", dark: "gray.100" };
  const [username, setUsername] = useState("");
  const { data:session, status } = useSession();

  const {
    loading: fetchUserFetching,
    error: fetchUserError,
    data: fetchUserData,
  } = useQuery(usersQuery, {
    variables: { userId: session.id },
  });

  useEffect(() => {
    if (fetchUserData) {
      const { username } = fetchUserData.user;

      setUsername(username || "");
    }
  }, [fetchUserData]);

  const [
    updateUser,
    { loading: updateUserFetching, error: updateUserError },
  ] = useMutation(updateUserMutation);

  if (fetchUserFetching) {
    return <Loader />;
  }

  if (fetchUserError) {
    return <p>Error: {fetchUserError.message}</p>;
  }

  const handleSubmit = () => {
    updateUser({ variables: { userId: session.id, username } });
  };

  const errorNode = () => {
    if (!updateUserError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{updateUserError}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={4}>
      <Heading color={color[colorMode]}>My Account</Heading>
      {errorNode()}
      <Grid templateColumns="repeat(1, 1fr)" gap={4}>
        <Box
          p={4}
          bg={bgColor[colorMode]}
          color={color[colorMode]}
          shadow="sm"
          rounded="lg"
        >
          <Stack spacing={4}>
            <FormControl isRequired>
              <FormLabel htmlFor="username">Username</FormLabel>
              <Input
                type="text"
                id="username"
                value={username}
                onChange={(e: FormEvent<HTMLInputElement>) =>
                  setUsername(e.currentTarget.value)
                }
                isDisabled={updateUserFetching}
              />
            </FormControl>
            <FormControl>
              <Button
                loadingText="Saving..."
                onClick={handleSubmit}
                isLoading={updateUserFetching}
              >
                Save
              </Button>
            </FormControl>
          </Stack>
        </Box>
      </Grid>
    </Stack>
  );
}
Example #24
Source File: FlowEditor.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
FlowEditor = (props: FlowEditorProps) => {
  const { match } = props;
  const history = useHistory();
  const { uuid } = match.params;
  const [publishDialog, setPublishDialog] = useState(false);
  const [simulatorId, setSimulatorId] = useState(0);
  const [loading, setLoading] = useState(true);

  const config = setConfig(uuid);
  const [published, setPublished] = useState(false);
  const [stayOnPublish, setStayOnPublish] = useState(false);
  const [modalVisible, setModalVisible] = useState(false);
  const [showResetFlowModal, setShowResetFlowModal] = useState(false);
  const [lastLocation, setLastLocation] = useState<Location | null>(null);
  const [confirmedNavigation, setConfirmedNavigation] = useState(false);
  const [flowValidation, setFlowValidation] = useState<any>();
  const [IsError, setIsError] = useState(false);
  const [flowKeyword, setFlowKeyword] = useState('');
  const [currentEditDialogBox, setCurrentEditDialogBox] = useState(false);
  const [dialogMessage, setDialogMessage] = useState('');
  const { drawerOpen } = useContext(SideDrawerContext);

  let modal = null;
  let dialog = null;
  let flowTitle: any;

  const [getOrganizationServices] = useLazyQuery(GET_ORGANIZATION_SERVICES, {
    fetchPolicy: 'network-only',
    onCompleted: (services) => {
      const { dialogflow, googleCloudStorage, flowUuidDisplay } = services.organizationServices;

      if (googleCloudStorage) {
        config.attachmentsEnabled = true;
      }
      if (!dialogflow) {
        config.excludeTypes.push('split_by_intent');
      }
      if (flowUuidDisplay) {
        config.showNodeLabel = true;
      }
      showFlowEditor(document.getElementById('flow'), config);
      setLoading(false);
    },
  });

  const [getFreeFlow] = useLazyQuery(GET_FREE_FLOW, {
    fetchPolicy: 'network-only',
    onCompleted: ({ flowGet }) => {
      if (flowGet.flow) {
        getOrganizationServices();
      } else if (flowGet.errors && flowGet.errors.length) {
        setDialogMessage(flowGet.errors[0].message);
        setCurrentEditDialogBox(true);
      }
    },
  });

  const [exportFlowMutation] = useLazyQuery(EXPORT_FLOW, {
    fetchPolicy: 'network-only',
    onCompleted: async ({ exportFlow }) => {
      const { exportData } = exportFlow;
      exportFlowMethod(exportData, flowTitle);
    },
  });
  const [resetFlowCountMethod] = useMutation(RESET_FLOW_COUNT, {
    onCompleted: ({ resetFlowCount }) => {
      const { success } = resetFlowCount;
      if (success) {
        setNotification('Flow counts have been reset', 'success');
        setShowResetFlowModal(false);
        window.location.reload();
      }
    },
  });

  const [publishFlow] = useMutation(PUBLISH_FLOW, {
    onCompleted: (data) => {
      if (data.publishFlow.errors && data.publishFlow.errors.length > 0) {
        setFlowValidation(data.publishFlow.errors);
        setIsError(true);
      } else if (data.publishFlow.success) {
        setPublished(true);
      }
    },
  });

  const { data: flowName } = useQuery(GET_FLOW_DETAILS, {
    fetchPolicy: 'network-only',
    variables: {
      filter: {
        uuid,
      },
      opts: {},
    },
  });

  let flowId: any;

  // flowname can return an empty array if the uuid present is not correct
  if (flowName && flowName.flows.length > 0) {
    flowTitle = flowName.flows[0].name;
    flowId = flowName.flows[0].id;
  }

  const closeModal = () => {
    setModalVisible(false);
  };
  const handleBlockedNavigation = (nextLocation: any): boolean => {
    if (!confirmedNavigation) {
      setModalVisible(true);
      setLastLocation(nextLocation);
      return false;
    }
    return true;
  };
  const handleConfirmNavigationClick = () => {
    setModalVisible(false);
    setConfirmedNavigation(true);
  };
  useEffect(() => {
    if (confirmedNavigation && lastLocation) {
      history.push(lastLocation);
    }
  }, [confirmedNavigation, lastLocation, history]);

  if (modalVisible) {
    modal = (
      <DialogBox
        title="Unsaved changes!"
        handleOk={handleConfirmNavigationClick}
        handleCancel={closeModal}
        colorOk="secondary"
        buttonOk="Ignore & leave"
        buttonCancel="Stay & recheck"
        alignButtons="center"
        contentAlign="center"
        additionalTitleStyles={styles.DialogTitle}
      >
        <div className={styles.DialogContent}>
          Your changes will not be saved if you navigate away. Please save as draft or publish.
        </div>
      </DialogBox>
    );
  }

  const handleResetFlowCount = () => {
    resetFlowCountMethod({ variables: { flowId } });
  };

  if (showResetFlowModal) {
    modal = (
      <DialogBox
        title="Warning!"
        handleOk={handleResetFlowCount}
        handleCancel={() => setShowResetFlowModal(false)}
        colorOk="secondary"
        buttonOk="Accept & reset"
        buttonCancel="DON'T RESET YET"
        alignButtons="center"
        contentAlign="center"
        additionalTitleStyles={styles.DialogTitle}
      >
        <div className={styles.DialogContent}>
          Please be careful, this cannot be undone. Once you reset the flow counts you will lose
          tracking of how many times a node was triggered for users.
        </div>
      </DialogBox>
    );
  }

  useEffect(() => {
    if (flowName) {
      document.title = flowTitle;
    }
    return () => {
      document.title = APP_NAME;
    };
  }, [flowName]);

  useEffect(() => {
    if (flowId) {
      const { fetch, xmlSend, xmlOpen } = setAuthHeaders();
      const files = loadfiles(() => {
        getFreeFlow({ variables: { id: flowId } });
      });

      // when switching tabs we need to check if the flow is still active for the user
      window.onfocus = () => {
        getFreeFlow({ variables: { id: flowId } });
      };

      return () => {
        Object.keys(files).forEach((node: any) => {
          if (files[node]) {
            document.body.removeChild(files[node]);
          }
        });
        // clearing all timeouts when component unmounts
        const highestTimeoutId: any = setTimeout(() => {});
        for (let timeoutId = 0; timeoutId < highestTimeoutId; timeoutId += 1) {
          clearTimeout(timeoutId);
        }
        XMLHttpRequest.prototype.send = xmlSend;
        XMLHttpRequest.prototype.open = xmlOpen;
        window.fetch = fetch;
      };
    }
    return () => {};
  }, [flowId]);

  const handlePublishFlow = () => {
    publishFlow({ variables: { uuid: match.params.uuid } });
  };

  const handleCancelFlow = () => {
    setPublishDialog(false);
    setIsError(false);
    setFlowValidation('');
  };

  const errorMsg = () => (
    <div className={styles.DialogError}>
      Errors were detected in the flow. Would you like to continue modifying?
      <div>
        {flowValidation.map((message: any) => (
          <div key={message.message}>
            <WarningIcon className={styles.ErrorMsgIcon} />
            {message.message}
          </div>
        ))}
      </div>
    </div>
  );

  if (currentEditDialogBox) {
    dialog = (
      <DialogBox
        title={dialogMessage}
        alignButtons="center"
        skipCancel
        buttonOk="Okay"
        handleOk={() => {
          setConfirmedNavigation(true);
          history.push('/flow');
        }}
      >
        <p className={styles.DialogDescription}>Please try again later or contact the user.</p>
      </DialogBox>
    );
  }

  if (publishDialog) {
    dialog = (
      <DialogBox
        title="Ready to publish?"
        buttonOk="Publish & Stay"
        titleAlign="center"
        buttonMiddle="Publish & go back"
        handleOk={() => {
          setStayOnPublish(true);
          handlePublishFlow();
        }}
        handleCancel={() => handleCancelFlow()}
        handleMiddle={() => {
          setStayOnPublish(false);
          handlePublishFlow();
        }}
        alignButtons="center"
        buttonCancel="Cancel"
        additionalTitleStyles={styles.PublishDialogTitle}
      >
        <p className={styles.DialogDescription}>New changes will be activated for the users</p>
      </DialogBox>
    );
  }

  if (IsError) {
    dialog = (
      <DialogBox
        title=""
        buttonOk="Publish"
        handleOk={() => {
          setPublishDialog(false);
          setIsError(false);
          setPublished(true);
        }}
        handleCancel={() => handleCancelFlow()}
        alignButtons="center"
        buttonCancel="Modify"
      >
        {errorMsg()}
      </DialogBox>
    );
  }

  if (published && !IsError) {
    setNotification('The flow has been published');
    if (!stayOnPublish) {
      return <Redirect to="/flow" />;
    }
    setPublishDialog(false);
    setPublished(false);
  }

  const resetMessage = () => {
    setFlowKeyword('');
  };

  const getFlowKeyword = () => {
    const flows = flowName ? flowName.flows : null;
    if (flows && flows.length > 0) {
      const { isActive, keywords } = flows[0];
      if (isActive && keywords.length > 0) {
        setFlowKeyword(`draft:${keywords[0]}`);
      } else if (keywords.length === 0) {
        setFlowKeyword('No keyword found');
      } else {
        setFlowKeyword('Sorry, the flow is not active');
      }
    }
  };

  return (
    <>
      {dialog}
      <div className={styles.ButtonContainer}>
        <a
          href={FLOWS_HELP_LINK}
          className={styles.Link}
          target="_blank"
          rel="noopener noreferrer"
          data-testid="helpButton"
        >
          <HelpIcon className={styles.HelpIcon} />
        </a>

        <Button
          variant="contained"
          color="default"
          className={styles.ContainedButton}
          onClick={() => {
            history.push('/flow');
          }}
        >
          Back
        </Button>

        <div
          className={styles.ExportIcon}
          onClick={() => exportFlowMutation({ variables: { id: flowId } })}
          aria-hidden="true"
        >
          <ExportIcon />
        </div>

        <Button
          variant="outlined"
          color="primary"
          data-testid="saveDraftButton"
          className={simulatorId === 0 ? styles.Draft : styles.SimulatorDraft}
          onClick={() => {
            setNotification('The flow has been saved as draft');
          }}
        >
          Save as draft
        </Button>

        <Button
          variant="contained"
          color="primary"
          data-testid="button"
          className={styles.ContainedButton}
          onClick={() => setPublishDialog(true)}
        >
          Publish
        </Button>
      </div>

      <Simulator
        showSimulator={simulatorId > 0}
        setSimulatorId={setSimulatorId}
        hasResetButton
        flowSimulator
        message={flowKeyword}
        resetMessage={resetMessage}
        getFlowKeyword={getFlowKeyword}
      />

      {modal}
      <Prompt when message={handleBlockedNavigation} />

      <div className={styles.FlowContainer}>
        <div
          className={drawerOpen ? styles.FlowName : styles.FlowNameClosed}
          data-testid="flowName"
        >
          {flowName && (
            <>
              <IconButton disabled className={styles.Icon}>
                <FlowIcon />
              </IconButton>

              {flowTitle}
            </>
          )}
        </div>

        <Button
          variant="outlined"
          color="primary"
          className={drawerOpen ? styles.ResetFlow : styles.ResetClosedDrawer}
          data-testid="resetFlow"
          onClick={() => setShowResetFlowModal(true)}
          aria-hidden="true"
        >
          <ResetFlowIcon /> Reset flow counts
        </Button>
        <div id="flow" />
        {loading && <Loading />}
      </div>
    </>
  );
}