@apollo/client#useLazyQuery JavaScript Examples

The following examples show how to use @apollo/client#useLazyQuery. 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: PrivateRoute.js    From saasgear with MIT License 6 votes vote down vote up
PrivateRoute = ({ render }) => {
  const history = useHistory();
  const dispatch = useDispatch();
  const [getProfile, { data: userProfile, loading: loadingUserProfile }] = useLazyQuery(
    getProfileQuery,
  );
  const { data } = useSelector((state) => state.user);

  useEffect(() => {
    if (!data || Object.keys(data).length === 0) {
      getProfile();
    }
  }, []);

  useEffect(() => {
    if (userProfile && userProfile.profileUser) {
      if (userProfile?.profileUser?.invitationToken) {
        history.push(`/teams/invitation/${data?.profileUser?.invitationToken}`);
      }
      dispatch(setProfileUser({ data: userProfile.profileUser, loading: loadingUserProfile }));
    }
  }, [userProfile]);

  return (
    <>
      {data && data.id && !loadingUserProfile && (
        <Route
          render={ props => render(props)}
        />
      )}
      {(!data || !data.id || loadingUserProfile) && (
        <div>Loading...</div>
      )}
    </>
  );
}
Example #2
Source File: podcasts.js    From grandcast.fm with Apache License 2.0 6 votes vote down vote up
Podcasts = () => {
  const [getPodcasts, { data }] = useLazyQuery(PodcastSearchQuery)
  const { isSignedIn } = useAuth()
  const [searchString, setSearchString] = useState('')
  return (
    <Container>
      {!isSignedIn() && <SignIn />}
      {isSignedIn() && (
        <div>
          <FormControl id="podcastsearch">
            <FormLabel>Search podcasts</FormLabel>
            <Flex>
              <Input onChange={(e) => setSearchString(e.target.value)} />
              <Button
                ml={4}
                onClick={() =>
                  getPodcasts({ variables: { searchTerm: searchString } })
                }
              >
                Search
              </Button>
            </Flex>
          </FormControl>
          <VStack>
            {data?.podcastSearch.map((p) => {
              return <Podcast podcast={p} />
            })}
          </VStack>
        </div>
      )}
    </Container>
  )
}
Example #3
Source File: favorites.js    From climatescape.org with MIT License 6 votes vote down vote up
// Fetches all favorites data from the GraphQL API, waiting until Auth0 is done
// loading so that the current user's favorites may be fetched. Returns a hooked
// object that will eventually take the following shape:
// {
//   "rec1": { count: 14, id: "uuid-of-users-favorite" },
//   "rec2": { count: 2, id: null },
// }
export function useFavorites(defaultData) {
  const { loading: authLoading, user } = useAuth0()
  const [favorites, setFavorites] = useState(() =>
    indexFavoritesData(defaultData)
  )
  const uuid = user?.[APP_CLAIM]?.uuid

  const [getFavorites, { data }] = useLazyQuery(GetFavorites, {
    variables: {
      loggedIn: !!user,
      userId: uuid,
    },
  })

  // Only fetch favorites from the server once we know if a user is logged in
  useEffect(() => {
    if (!authLoading) getFavorites()
  }, [authLoading, getFavorites])

  // Index and set favorites any time data changes
  useMemo(() => data && setFavorites(indexFavoritesData(data)), [data])

  return favorites
}
Example #4
Source File: favorites.js    From goodhere with MIT License 6 votes vote down vote up
// Fetches all favorites data from the GraphQL API, waiting until Auth0 is done
// loading so that the current user's favorites may be fetched. Returns a hooked
// object that will eventually take the following shape:
// {
//   "rec1": { count: 14, id: "uuid-of-users-favorite" },
//   "rec2": { count: 2, id: null },
// }
export function useFavorites(defaultData, { isFavoritesPage } = {}) {
  const { loading: authLoading, user } = useAuth0()
  const [favorites, setFavorites] = useState(indexFavoritesData(defaultData))

  const uuid = user?.sub?.replace(/.+\|/, '')

  const [getFavorites, { data, loading: favoritesLoading, called }] = useLazyQuery(isFavoritesPage ? GetUserFavorites : GetFavorites, {
    variables: {
      loggedIn: !!user,
      userId: uuid,
    },
  })

  // Only fetch favorites from the server once we know if a user is logged in
  useEffect(() => {
    if (!authLoading) getFavorites()
  }, [authLoading, getFavorites])

  // Index and set favorites any time data changes
  useEffect(() => {
    if (data) {
      setFavorites(isFavoritesPage ? normalizeFavorites(data) : indexFavoritesData(data))
    }
  }, [data, favoritesLoading])

  const isLoading = favoritesLoading || authLoading

  return [favorites, isLoading]
}
Example #5
Source File: [uid].js    From RC4Community with Apache License 2.0 5 votes vote down vote up
Profile = () => {
    const router = useRouter()
    const { uid } = router.query
    const cookies = Cookies.get('user');
    const [getCurrentUser, { data, error, loading }] = useLazyQuery(FindUserByUid);

    useEffect(() => {
        if(!cookies) {
          router.push('/')
        } 
        getCurrentUser({
          variables: {
            uid: uid
          }
        })
      }, [])

    if(error) console.log(error)
  
    if(data?.findUserByUid){
        const user = data.findUserByUid
        return(
            <>
            <div className="my-3" style={{display:"flex", alignItems:"center", flexDirection:"column"}}>{
            user?.photoURL ?
            <img src={user.photoURL}
                alt={user.displayName}
                className="rounded-circle"
                height="130px"
                width="130px" />
            :
            <NoUserAvatar name={user?.displayName} size="130" />
            }
            <h2 className="my-3">{user.displayName}</h2>
            </div>
            </>
        )
      }

    return <></>
    }
Example #6
Source File: comments.js    From stacker.news with MIT License 5 votes vote down vote up
export default function Comments ({ parentId, comments, ...props }) {
  const client = useApolloClient()
  useEffect(() => {
    const hash = window.location.hash
    if (hash) {
      document.querySelector(hash).scrollIntoView({ behavior: 'smooth' })
    }
  }, [])
  const [getComments, { loading }] = useLazyQuery(COMMENTS_QUERY, {
    fetchPolicy: 'network-only',
    onCompleted: data => {
      client.writeFragment({
        id: `Item:${parentId}`,
        fragment: gql`
          ${COMMENTS}
          fragment Comments on Item {
            comments {
              ...CommentsRecursive
            }
          }
        `,
        fragmentName: 'Comments',
        data: {
          comments: data.comments
        }
      })
    }
  })

  return (
    <>
      {comments.length ? <CommentsHeader handleSort={sort => getComments({ variables: { id: parentId, sort } })} /> : null}
      {loading
        ? <CommentsSkeleton />
        : comments.map(item => (
          <Comment depth={1} key={item.id} item={item} {...props} />
        ))}
    </>
  )
}
Example #7
Source File: HospitalPage.js    From bedav with GNU General Public License v3.0 5 votes vote down vote up
function HospitalPage(props) {
  let { hospitalId } = useParams();
  const client = useApolloClient();
  hospitalId = decodeURIComponent(hospitalId);

  useEffect(() => {
    document.body.scrollTop = 0;
    document.documentElement.scrollTop = 0;
  });

  let cachedHospital = client.readFragment({
    id: `Hospital:${hospitalId}`,
    fragment: HospitalInfoFragment,
  });

  const [hospital, setHospital] = useState(cachedHospital);
  const [getHospital, { data, loading }] = useLazyQuery(PageQuery, {
    variables: {
      hospitalId: hospitalId,
    },
  });

  if (data && data.hospital && !hospital) {
    setHospital(data.hospital);
  }

  if (hospital) {
    document.title = "Bedav - " + hospital.name;
  } else if (loading) {
    return <Spinner />;
  } else if (hospital === null) {
    getHospital({
      variables: {
        hospitalId,
      },
    });

    return <Spinner />;
  }

  return (
    <>
      <MainContainer>
        <BedInfoSection hospital={hospital} />
        <HospitalInfoSection hospital={hospital} />
      </MainContainer>
    </>
  );
}
Example #8
Source File: use-constructor-loader.js    From horondi_client_fe with MIT License 5 votes vote down vote up
useConstructorLoader = () => {
  const [constructorValues, setConstructorValues] = useState({});
  const [constructorModel, setConstructorModel] = useState('');
  const currentConstructorModel = useRef({});
  const allModels = useRef([]);

  const [allPrices, setAllPrice] = useState({});
  const [valuesLoading, setValuesLoading] = useState(true);

  const { data: constructors, error: constructorsError } = useQuery(getAllConstructors, {
    variables: {
      limit: 0,
      skip: 0
    }
  });

  const [
    getConstructorByModelHandler,
    { data: constructorByModel, refetch, called, constructorError }
  ] = useLazyQuery(getConstructorByModel, {
    variables: {
      id: constructorModel
    }
  });

  useEffect(() => {
    if (constructors) {
      allModels.current = constructors.getAllConstructors.items.map((item) => item.model);
      setConstructorModel(constructors.getAllConstructors.items[0].model._id);
      currentConstructorModel.current = constructors.getAllConstructors.items[0];
    }
  }, [constructors]);

  useEffect(() => {
    if (constructorByModel) {
      const pocket =
        constructorByModel.getConstructorByModel.pocketsWithRestrictions[0]
          ?.currentPocketWithPosition?.pocket;

      const values = {
        name: constructorByModel.getConstructorByModel.name,
        size: constructorByModel.getConstructorByModel.model.sizes[0],
        pattern: constructorByModel.getConstructorByModel.patterns[0],
        bottom: constructorByModel.getConstructorByModel.bottoms[0],
        basic: constructorByModel.getConstructorByModel.basics[0],
        model: constructorByModel.getConstructorByModel.model,
        basePrice: constructorByModel.getConstructorByModel.basePrice,
        pocket
      };

      setConstructorValues(values);

      currentConstructorModel.current = constructorByModel.getConstructorByModel;

      currentConstructorModel.current.model && setValuesLoading(false);
    }
  }, [constructorByModel]);

  useEffect(() => {
    !called && constructorModel && getConstructorByModelHandler();
    called && refetch();
  }, [constructorModel]);

  return {
    constructorValues,
    setConstructorValues,
    constructorModel,
    setConstructorModel,
    currentConstructorModel,
    allPrices,
    setAllPrice,
    allModels,
    valuesLoading,
    constructorsError,
    constructorError
  };
}
Example #9
Source File: certificate-thanks-page.js    From horondi_client_fe with MIT License 5 votes vote down vote up
CertificateThanksPage = () => {
  const styles = useStyles();
  const { i18n } = useTranslation();

  const language = i18n.language === 'ua' ? 0 : 1;

  const [paidOrderLoading, setLoading] = useState(true);
  const [certificatesArr, setCertificates] = useState(null);

  const certificatesOrderId = getFromLocalStorage(orderDataToLS.certificatesOrderId);

  const [sendCertificateCodesToEmail] = useLazyQuery(sendCertificatesCodesToEmail);

  useSubscription(certificatesPaidSubscription, {
    variables: { certificatesOrderId },
    onSubscriptionData: ({ subscriptionData: { data } }) => {
      const {
        certificatesPaid: { certificates }
      } = data;

      sendCertificateCodesToEmail({
        variables: {
          language,
          certificates
        }
      });
      setCertificates(certificates);
      setLoading(false);
    }
  });

  if (paidOrderLoading) {
    return <Loader data-testid='loader' />;
  }

  return (
    <div className={styles.thanksBackground}>
      <div className={styles.thanksContainer}>
        {!paidOrderLoading && (
          <div className={styles.thanksInfo}>
            <CertificateThanksCard
              count={certificatesArr.length}
              name={certificatesArr[0]?.name}
              email={certificatesArr[0]?.email}
              value={certificatesArr[0]?.value}
              paymentStatus={certificatesArr[0]?.paymentStatus}
              dateStart={certificatesArr[0]?.dateStart}
              dateEnd={certificatesArr[0]?.dateEnd}
            />
          </div>
        )}
      </div>
    </div>
  );
}
Example #10
Source File: search-bar.js    From horondi_client_fe with MIT License 5 votes vote down vote up
SearchBar = ({
  searchParams,
  setSearchParams,
  fromNavBar = true,
  searchHandler,
  defaultValue
}) => {
  const styles = useStyles({ fromNavBar });
  const { t } = useTranslation();

  const [searchValue, setSearchValue] = useState('');
  const debouncedSearchValue = useDebounce(searchValue, 1000);

  const [getProductsQuery, { loading }] = useLazyQuery(getFilteredProductsQuery, {
    onCompleted: (data) => {
      setSearchParams((prevState) => ({
        ...prevState,
        loading,
        products: data.getProducts.items
      }));
    },
    variables: { search: searchParams.searchFilter },
    fetchPolicy: 'no-cache'
  });

  const visibilityToggle = (value) => {
    setSearchParams((prevState) => ({
      ...prevState,
      searchBarVisibility: value
    }));
  };

  useEffect(() => {
    if (debouncedSearchValue) {
      setSearchParams(() => ({
        products: [],
        searchFilter: debouncedSearchValue,
        searchBarVisibility: true
      }));
      getProductsQuery();
    } else {
      handleOnBlur();
    }
  }, [debouncedSearchValue]);

  const handleSearch = (event) => {
    const { value } = event.target;
    setSearchValue(value);
  };

  const mainClass = fromNavBar ? styles.root : styles.notFromNavbar;

  const handleOnBlur = () => {
    setTimeout(() => visibilityToggle(false), 100);
  };

  const handleOnFocus = () => {
    if (searchValue) {
      visibilityToggle(true);
    }
  };

  return (
    <div className={mainClass}>
      <SearchIcon />
      <TextField
        placeholder={t('searchBar.search')}
        value={defaultValue || searchValue}
        onBlur={handleOnBlur}
        onFocus={handleOnFocus}
        inputProps={{ maxLength: 20 }}
        onChange={searchHandler || handleSearch}
      />
    </div>
  );
}
Example #11
Source File: filled-cart.js    From horondi_client_fe with MIT License 4 votes vote down vote up
FilledCart = ({ items, cartOperations }) => {
  const styles = useStyles();
  const { t } = useTranslation();
  const history = useHistory();
  const { getCurrencySign } = useCurrency();

  const [addConstructorProduct] = useMutation(addProductFromConstructor);

  const promoCodeInput = useRef(null);
  const { pathToCategory, pathToCheckout } = routes;
  const [price, setPrice] = useState();
  const [promoCodeValue, setPromoCodeValue] = useState('');
  const [productFromConstructorLoading, setProductFromConstructorLoading] = useState(false);

  const { currency } = useContext(CurrencyContext);

  const { cartLoading, user } = useSelector(({ User }) => ({
    user: User.userData
  }));

  const [getPromoCode, { data: promoCode, error }] = useLazyQuery(getPromoCodeByCode, {
    variables: {
      code: promoCodeValue
    }
  });

  const currencySign = getCurrencySign();
  const { getTotalPrice, setCartItem, getTotalPricesWithPromoCode } = cartOperations;

  const checkPromoCode = () => {
    setPromoCodeValue(promoCodeInput.current.value);
    getPromoCode();
    promoCodeInput.current.value = '';
  };

  useEffect(() => {
    promoCode ? setPrice(getTotalPricesWithPromoCode(promoCode)) : setPrice(getTotalPrice());
  }, [items, currency, getTotalPrice, promoCode, getTotalPricesWithPromoCode]);

  if (cartLoading || productFromConstructorLoading) {
    return <Loader />;
  }

  const onGoToCheckout = async () => {
    const itemsFromConstructor = items.filter((item) => item.isFromConstructor);

    for (const item of itemsFromConstructor) {
      const input = {
        product: {
          name: item.name,
          model: item.model?._id,
          pattern: item.pattern?._id,
          mainMaterial: {
            material: item.basic?.features.material._id,
            color: item.basic?.features.color._id
          },
          bottomMaterial: {
            material: item.bottom?.features.material._id,
            color: item.bottom?.features.color._id
          },
          sizes: [item.sizeAndPrice.size._id],
          basePrice: item.sizeAndPrice.price
        },
        upload: []
      };

      setProductFromConstructorLoading(true);

      const { data } = await addConstructorProduct({
        variables: {
          product: input.product,
          upload: input.upload
        }
      });

      setCartItem(item.id, {
        ...item,
        productId: data.addProductFromConstructor._id
      });
    }

    history.push(pathToCheckout, { promoCode });
  };

  return (
    <>
      <PathBack />
      <div className={styles.root} data-cy='filled-cart'>
        <div className={styles.orderWrapper}>
          <div className={styles.orderTable}>
            <OrderTable
              items={items}
              user={user}
              cartOperations={cartOperations}
              promoCode={promoCode}
            />
          </div>
        </div>
        <div>
          <div className={styles.promoAndTotalWrapper}>
            <div className={styles.promoWrapper}>
              <div>
                <TextField
                  className={styles.textField}
                  InputProps={{
                    className: styles.promoInput
                  }}
                  placeholder={t('cart.promoPlaceHolder')}
                  variant={TEXT_FIELD_VARIANT.OUTLINED}
                  inputRef={promoCodeInput}
                  error={!!error}
                  helperText={error && t('cart.notFound')}
                />
                <Button
                  data-testid='promoButton'
                  variant='contained'
                  className={`${styles.promoButton} ${styles.promoInput}`}
                  onClick={checkPromoCode}
                >
                  {t('cart.applyPromoCode')}
                </Button>
              </div>
              <Link to={pathToCategory}>
                <Button className={styles.shoppingButton}>{t('cart.continue')}</Button>
              </Link>
            </div>
            <div className={styles.totalWrapper}>
              {promoCode && (
                <div className={styles.totalPrice}>
                  <span>{t('cart.saving')}</span>
                  <div>
                    {currencySign}
                    {getTotalPrice() - getTotalPricesWithPromoCode(promoCode)}
                  </div>
                </div>
              )}
              <div className={styles.totalPrice}>
                <span>{t('cart.totalPrice')}</span>
                <div>
                  {currencySign}
                  {price}
                </div>
              </div>
              <Button variant='contained' className={styles.ordersButton} onClick={onGoToCheckout}>
                {t('cart.checkout')}
              </Button>
            </div>
          </div>
        </div>
        <SimilarProducts cartList={items} />
      </div>
    </>
  );
}
Example #12
Source File: gift-certificate.js    From horondi_client_fe with MIT License 4 votes vote down vote up
GiftCertificate = () => {
  const { t } = useTranslation();
  const styles = useStyles();
  const appStyles = useAppStyles();

  const [getPaymentCheckoutForCertificate] = useLazyQuery(getPaymentCheckoutForCertificates, {
    onCompleted: (data) => {
      const { paymentUrl, paymentToken, certificatesOrderId } =
        data.getPaymentCheckoutForCertificates;
      setToLocalStorage(orderDataToLS.certificatesOrderId, certificatesOrderId);
      window.open(`${process.env.REACT_APP_ROOT_PATH}${pathToCertificateThanks}/${paymentToken}`);
      window.open(paymentUrl);
    }
  });

  const [generateCertificates] = useMutation(generateCertificate, {
    onCompleted: (data) => {
      const { certificates, certificatesPrice } = data.generateCertificate;
      getPaymentCheckoutForCertificate({
        variables: {
          data: {
            currency: getCurrentCurrency(currency),
            amount: String(certificatesPrice),
            certificates
          }
        }
      });
    }
  });

  const initialValues = {
    email: ''
  };

  const { userData, currency } = useSelector(({ User, Currency }) => ({
    userData: User.userData,
    currency: Currency.currency
  }));

  const CHECKBOXES_STATE = [
    { value: 500, checked: false, count: INITIAL_CERTIFICATE_COUNT },
    { value: 1000, checked: true, count: INITIAL_CERTIFICATE_COUNT },
    { value: 1500, checked: false, count: INITIAL_CERTIFICATE_COUNT }
  ];

  const [checkboxesArr, setCheckboxesArr] = useState(CHECKBOXES_STATE);

  const { errors, values, touched, handleChange, handleSubmit, handleBlur, resetForm } = useFormik({
    validationSchema,
    initialValues,
    onSubmit: () => {
      const newCertificates = findCheckedCertificates(checkboxesArr);
      generateCertificates({
        variables: {
          newCertificates,
          email: values.email
        }
      });
    }
  });

  useEffect(() => {
    if (userData) {
      resetForm({
        values: {
          email: userData.email
        }
      });
    }
  }, [userData, resetForm]);

  const handleCheckboxChange = (value, checkboxIndex) => {
    setCheckboxesArr(
      checkboxesArr.map((checkbox, index) =>
        index === checkboxIndex ? { ...checkbox, checked: value } : checkbox
      )
    );
  };

  const handleCountChange = (count, index) => {
    setCheckboxesArr(
      checkboxesArr.map((checkbox, key) => (key === index ? { ...checkbox, count } : checkbox))
    );
  };

  const findCheckedCertificates = (certificates) =>
    certificates
      .filter((certificate) => certificate.checked === true)
      .map(({ checked, ...keepAtrs }) => keepAtrs);

  const checkboxContent = checkboxesArr.map((checkbox, index) => (
    <CertificateCheckbox
      handleAllCheckboxesChange={handleCheckboxChange}
      handleAllCountChange={handleCountChange}
      checked={checkbox.checked}
      index={index}
      key={checkbox.value}
      value={checkbox.value}
    />
  ));

  const certificateText = (index) => t(`certificate.certificateRules.${index}`);

  const certificateRulesContent = certificateRules.map((_, index) => (
    <React.Fragment key={index}>
      {`${index + 1}. ${certificateText(index)}`}
      <br />
    </React.Fragment>
  ));

  return (
    <div className={appStyles.rootApp}>
      <div className={appStyles.containerApp}>
        <h1 className={styles.pageTitle}>{t('certificate.giftCertificate')}</h1>
        <h2 className={styles.chooseCertificate}>{t('certificate.chooseCertificate')}</h2>
        <div className={styles.checkboxWrapper}>{checkboxContent}</div>
        <div className={styles.lowerWrapper}>
          <div>{certificateRulesContent}</div>
          <form onSubmit={handleSubmit}>
            <div className={styles.formWrapper}>
              <TextField
                id='email'
                data-testid='email'
                fullWidth
                label={t('checkout.checkoutTextFields.email')}
                variant={TEXT_FIELD_VARIANT.OUTLINED}
                className={styles.textField}
                onBlur={handleBlur}
                onChange={handleChange}
                value={values.email}
                color={MATERIAL_UI_COLOR.PRIMARY}
                name='email'
                error={touched.email && Boolean(t(errors.email))}
                helperText={touched.email && t(errors.email)}
              />
              <Button
                data-testid='button'
                className={styles.purchaseButton}
                fullWidth
                type='submit'
              >
                {t('buttons.buyButton')}
              </Button>
            </div>
          </form>
        </div>
      </div>
    </div>
  );
}
Example #13
Source File: UserPage.js    From stack-underflow with MIT License 4 votes vote down vote up
UserPage = () => {
  const classes = useUserPageStyles();
  const { notify } = useStateContext();
  const { username } = useParams();
  const [fetchedUser, setFetchedUser] = useState(null);
  const [fetchUser, { data, loading }] = useLazyQuery(GET_USER, {
    onError: (err) => {
      notify(getErrorMsg(err), 'error');
    },
  });

  useEffect(() => {
    fetchUser({ variables: { username } });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [username]);

  useEffect(() => {
    if (data) {
      setFetchedUser(data.getUser);
    }
  }, [data]);

  if (loading || !fetchedUser) {
    return (
      <div style={{ minWidth: '100%', marginTop: '20%' }}>
        <LoadingSpinner size={80} />
      </div>
    );
  }

  const {
    id,
    username: userName,
    createdAt,
    reputation,
    totalQuestions,
    totalAnswers,
    recentQuestions,
    recentAnswers,
  } = fetchedUser;

  return (
    <div className={classes.root}>
      <div className={classes.userCard}>
        <Avatar
          src={`https://secure.gravatar.com/avatar/${id}?s=164&d=identicon`}
          alt={username}
          className={classes.avatar}
          component={RouterLink}
          to={`/user/${username}`}
        />
        <Typography variant="h5" color="secondary" className={classes.cardText}>
          {reputation} <Typography variant="caption">REPUTATION</Typography>
        </Typography>
      </div>
      <div className={classes.infoCard}>
        <div className={classes.userInfo}>
          <div>
            <Typography
              variant="h4"
              color="primary"
              className={classes.bigText}
            >
              {userName}
            </Typography>
            <Typography
              variant="body1"
              color="secondary"
              className={classes.smallText}
            >
              member for {formatDateAgo(createdAt)}
            </Typography>
          </div>
          <div className={classes.statsBar}>
            <div style={{ marginRight: 10 }}>
              <Typography
                variant="h4"
                color="primary"
                className={classes.bigText}
              >
                {totalAnswers}
              </Typography>
              <Typography
                variant="body1"
                color="secondary"
                className={classes.smallText}
              >
                answers
              </Typography>
            </div>
            <div>
              <Typography
                variant="h4"
                color="primary"
                className={classes.bigText}
              >
                {totalQuestions}
              </Typography>
              <Typography
                variant="body1"
                color="secondary"
                className={classes.smallText}
              >
                questions
              </Typography>
            </div>
          </div>
        </div>
        <Divider />
        <div className={classes.recentActivity}>
          <div style={{ marginBottom: '1em' }}>
            <Typography variant="h6" color="primary">
              Last Asked Questions
            </Typography>
            <Divider />
            {recentQuestions.length !== 0 ? (
              recentQuestions.map((q) => (
                <div key={q.id}>
                  <RecentQuestions question={q} />
                  <Divider />
                </div>
              ))
            ) : (
              <Typography variant="subtitle1">
                No questions asked yet.
              </Typography>
            )}
          </div>
          <div>
            <Typography variant="h6" color="primary">
              Last Answered Questions
            </Typography>
            <Divider />
            {recentAnswers.length !== 0 ? (
              recentAnswers.map((q) => (
                <div key={q.id}>
                  <RecentQuestions question={q} />
                  <Divider />
                </div>
              ))
            ) : (
              <Typography variant="subtitle1">
                No questions answered yet.
              </Typography>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}
Example #14
Source File: comments-item.js    From horondi_client_fe with MIT License 4 votes vote down vote up
CommentsItem = ({ userFirstName, commentItem, commentId, productId, refetchComments }) => {
  const styles = useStyles();
  const { user, text, date, show, rate, replyCommentsCount, verifiedPurchase } = commentItem;

  const { userData } = useSelector(({ User }) => ({
    userData: User.userData
  }));

  const { t, i18n } = useTranslation();

  const { firstName, email } = user || {
    firstName: t('common.userData.firstName'),
    email: t('common.userData.email')
  };

  const [isModalShown, toggleModal] = useState(false);
  const [isReplyShown, toggleReply] = useState();
  const [isReplyListShown, toggleReplyList] = useState(false);
  const [currentLimit, setCurrentLimit] = useState(3);
  const dateLanguage = i18n.language === 'ua' ? 'ukr-UA' : 'en-US';
  const dateToShow = new Date(date);

  const [getReplyCommentsByID, { loading, data: replyCommentsData, error, called }] = useLazyQuery(
    getReplyCommentsQuery,
    {
      variables: {
        filter: { commentId, filters: false },
        pagination: { skip: handleSkip(replyCommentsCount, currentLimit), limit: currentLimit }
      },
      fetchPolicy: 'network-only',
      nextFetchPolicy: 'cache-first'
    }
  );

  if (error || loading) return errorOrLoadingHandler(error, loading);

  const { replyComments } = replyCommentsData
    ? replyCommentsData.getReplyCommentsByComment.items[0]
    : { replyComments: [] };

  const commentDate = dateToShow.toLocaleString(dateLanguage, COMMENTS_TIME_OPTIONS);

  const reloadCommentsData = async () => {
    toggleReplyList(true);
    await refetchComments();
    getReplyCommentsByID();
  };

  const handleOpen = () => {
    toggleModal(true);
  };

  const handleClose = () => {
    toggleModal(false);
  };

  const handleReplyOpen = () => {
    toggleReply(true);
  };

  const handleReplyClose = () => {
    toggleReply(false);
  };

  const showReplyList = async () => {
    !called && getReplyCommentsByID();
    toggleReplyList((prevIsReplyListShown) => !prevIsReplyListShown);
  };

  const getMoreComments = async () => {
    getReplyCommentsByID();
    setCurrentLimit((prev) => prev + 10);
  };

  const replyCommentsList = replyComments.map(({ _id, ...rest }) => (
    <ReplyCommentsItem
      key={_id}
      replyItem={rest}
      replyCommentId={_id}
      updateReplies={reloadCommentsData}
    />
  ));

  const limitOption = replyCommentsList.length === replyCommentsCount;

  const loadMore = limitOption ? null : t('product.comments.loadMore');
  return (
    <div className={styles.container}>
      <div className={styles.comments}>
        <div className={styles.comment}>
          <div className={styles.userContainer}>
            <div className={styles.user}>
              <span className={styles.name} data-testid='firstName'>
                {firstName}
              </span>
            </div>
            <div className={styles.commentActions}>
              {verifiedPurchase ? (
                <Tooltip className={styles.checkIcon} title={t('product.tooltips.bought')}>
                  <>
                    <VerifiedPurchaseIcon
                      alt='Verified purchase icon'
                      className={styles.boughtIcon}
                    />
                  </>
                </Tooltip>
              ) : null}
              {handleUserCommentApprove(userData, email, show) ? (
                <Tooltip title={t('product.tooltips.feedbackComment')}>
                  <>
                    <FeedbackOutlinedIcon className={styles.iconF} />
                  </>
                </Tooltip>
              ) : null}
            </div>
          </div>
          <div className={styles.date}>{commentDate}</div>
        </div>
        <div className={styles.rateIcon}>{handleRate(rate)}</div>
        <div className={styles.textContent}>
          <div
            className={handleTextStyle(show, styles.text, `${styles.notAproveText} ${styles.text}`)}
            data-testid='commentText'
          >
            {text}
          </div>
          <div className={styles.userIcons}>
            {handleUserCommentOwner(userData, email) ? (
              <div className={styles.icons}>
                <Tooltip title={t('product.tooltips.delete')}>
                  <DeleteIcon className={styles.deleteIcon} onClick={handleOpen} />
                </Tooltip>
              </div>
            ) : null}
          </div>
        </div>

        <div className={styles.reply}>
          <ReplyOutlinedIcon className={styles.replyIcon} />
          <Tooltip title={userData ? '' : t(`product.tooltips.unregisteredReply`)}>
            <p className={styles.button} onClick={handleReplyOpen}>
              {t('common.reply.submit')}
            </p>
          </Tooltip>

          {replyCommentsCount ? (
            <div className={styles.replyCount} onClick={showReplyList}>
              <ChatBubbleOutlineOutlinedIcon className={styles.icon} />
              <span className={styles.replyText}>
                {replyCommentsCount}
                {'\u00A0'}
                {t('common.reply.answers')}
              </span>
            </div>
          ) : null}
        </div>

        {isReplyShown && userData?._id && (
          <ReplyForm
            userFirstName={userFirstName}
            user={user}
            className={styles.replyForm}
            cancel={handleReplyClose}
            refetchComments={reloadCommentsData}
            commentId={commentId}
            productId={productId}
          />
        )}

        {isReplyListShown ? (
          <div>
            {replyCommentsCount > currentLimit && (
              <div className={styles.loadMore}>
                {handleArrowIcon(limitOption)}
                <span onClick={getMoreComments} className={styles.loadMoreText}>
                  {loadMore}
                </span>
              </div>
            )}

            {loading && (
              <div className={styles.loader}>
                <Loader width={20} height={20} heightWrap={40} />
              </div>
            )}
            {replyCommentsList}
          </div>
        ) : null}
      </div>
      <CommentDialog
        handleClose={handleClose}
        isModalShown={isModalShown}
        id={commentId}
        userId={handleUserId(userData)}
        isDeleteComment
        refetchComments={refetchComments}
      />
    </div>
  );
}
Example #15
Source File: thanks-page.js    From horondi_client_fe with MIT License 4 votes vote down vote up
ThanksPage = () => {
  const router = useLocation();
  const { t, i18n } = useTranslation();
  const dispatch = useDispatch();

  const language = i18n.language === 'ua' ? 0 : 1;

  const [paidOrderLoading, setLoading] = useState(true);

  const { order, loading, user } = useSelector(({ Order, User }) => ({
    order: Order.order,
    loading: Order.loading,
    user: User.userData
  }));

  const styles = useStyles();
  const paymentMethod = getFromLocalStorage(orderDataToLS.paymentMethod);

  const [sendPaidOrderToEmail] = useLazyQuery(sendOrderToEmail);

  const paidOrderNumber = router.pathname.slice(router.pathname.length - ORDER_NUMBER_LENGTH);

  useSubscription(orderPaidSubscription, {
    variables: { orderId: paidOrderNumber },
    onSubscriptionData: ({
      subscriptionData: {
        data: { paidOrder }
      }
    }) => {
      dispatch(setOrder(paidOrder));
      setToLocalStorage(orderDataToLS.order, paidOrder);
      sendPaidOrderToEmail({
        variables: {
          language,
          paidOrderNumber
        }
      });
      setLoading(false);
    }
  });

  useEffect(() => {
    if (paymentMethod === checkoutPayMethod.cash) {
      dispatch(getOrder());
    }
  }, [dispatch, language, user]);

  if ((paymentMethod === checkoutPayMethod.card && paidOrderLoading) || loading) {
    return <Loader data-testid='loader' />;
  }

  const getDeliveryAddress = (orderPayload) => {
    const deliveryType = orderPayload?.delivery.sentBy;
    const courierOffice = orderPayload?.delivery.courierOffice;
    const customerAddress = `${orderPayload?.delivery.city}, ${orderPayload?.delivery.street}, ${orderPayload?.delivery.house}`;
    if (deliveryType === deliveryTypes.NOVAPOST || deliveryType === deliveryTypes.UKRPOST) {
      return courierOffice;
    }
    if (deliveryType === deliveryTypes.SELFPICKUP) {
      return t('thanksPage.thanksCard.selfDelivery');
    }
    return customerAddress;
  };

  return (
    <div className={styles.thanksBackground}>
      <div className={styles.thanksContainer}>
        {!order && <Redirect to={pathToMain} /> &&
          paymentMethod !== t('checkout.checkoutPayment.card')}
        {(!loading || !paidOrderLoading) && (
          <>
            <div className={styles.thunksInfo}>
              <ThanksCard
                orderNumber={order?.orderNumber}
                customerName={`${order?.recipient.firstName} ${order?.recipient.lastName}`}
                phoneNumber={order?.recipient.phoneNumber}
                deliveryType={order?.delivery.sentBy}
                address={getDeliveryAddress(order)}
              />
            </div>
          </>
        )}
      </div>
    </div>
  );
}
Example #16
Source File: QuestionPage.js    From stack-underflow with MIT License 4 votes vote down vote up
QuestionPage = () => {
  const { clearEdit, notify } = useStateContext();
  const { user } = useAuthContext();
  const { quesId } = useParams();
  const [question, setQuestion] = useState(null);
  const classes = useQuesPageStyles();
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('xs'));
  const [fetchQuestion, { data, loading }] = useLazyQuery(VIEW_QUESTION, {
    onError: (err) => {
      notify(getErrorMsg(err), 'error');
    },
  });

  useEffect(() => {
    fetchQuestion({ variables: { quesId } });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [quesId]);

  useEffect(() => {
    if (data) {
      setQuestion(data.viewQuestion);
    }
  }, [data]);

  if (loading || !question) {
    return (
      <div style={{ minWidth: '100%', marginTop: '20%' }}>
        <LoadingSpinner size={80} />
      </div>
    );
  }

  const { title, views, createdAt, updatedAt } = question;

  return (
    <div className={classes.root}>
      <div className={classes.topBar}>
        <div className={classes.titleWrapper}>
          <Typography
            variant={isMobile ? 'h6' : 'h5'}
            color="secondary"
            style={{ wordWrap: 'anywhere' }}
          >
            {title}
          </Typography>
          {user ? (
            <Button
              variant="contained"
              color="primary"
              size={isMobile ? 'small' : 'medium'}
              component={RouterLink}
              to="/ask"
              onClick={() => clearEdit()}
              style={{ minWidth: '9em' }}
            >
              Ask Question
            </Button>
          ) : (
            <AuthFormModal buttonType="ask" />
          )}
        </div>
        <div className={classes.quesInfo}>
          <Typography variant="caption" style={{ marginRight: 10 }}>
            Asked <strong>{formatDateAgo(createdAt)} ago</strong>
          </Typography>
          {createdAt !== updatedAt && (
            <Typography variant="caption" style={{ marginRight: 10 }}>
              Edited <strong>{formatDateAgo(updatedAt)} ago</strong>
            </Typography>
          )}
          <Typography variant="caption">
            Viewed <strong>{views} times</strong>
          </Typography>
        </div>
      </div>
      <Divider />
      <Grid container direction="row" wrap="nowrap" justify="space-between">
        <QuesPageContent question={question} />
        <RightSidePanel />
      </Grid>
    </div>
  );
}
Example #17
Source File: index.js    From openeew-dashboard with Apache License 2.0 4 votes vote down vote up
Sensors = ({ history }) => {
  const { addToast } = useContext(AppContext)
  const [execQuery, { data, loading }] = useLazyQuery(GET_SENSORS, {
    errorPolicy: 'all',
    // TODO
    onError() {
      addToast({
        kind: 'warning',
        caption:
          'There were errors generated by the sensor query that may affect the display of relevant data. If this is a recurring warning, consider opening an issue.',
        title: 'Sensor Data Loaded with Error(s)',
      })
    },
  })

  const [pageSize, setPageSize] = useState(5)
  const [page, setPage] = useState(1)
  const [sensors, setSensors] = useState([])
  const [currentlyVisibleSensors, setCurrentlyVisibleSensors] = useState([])

  const [shouldShowSideMenu, setShouldShowSideMenu] = useState(false)
  const [shouldShowRemoveMenu, setShouldShowRemoveMenu] = useState(false)
  const [displayedSensor, setDisplayedSensor] = useState({})
  const [removeSensorLoading, setRemoveSensorLoading] = useState(false)
  const [currentHoveredSensor, setCurrentHoveredSensor] = useState()

  const [sendRemoveSensor] = useMutation(SEND_SENSOR_REMOVE)

  useEffect(() => {
    execQuery()
  }, [execQuery])

  useEffect(() => {
    if (data && data.sensors) {
      setSensors(processSensorData(data.sensors))
    }
  }, [data])

  useEffect(() => {
    setCurrentlyVisibleSensors(
      sensors.slice((page - 1) * pageSize, page * pageSize)
    )
  }, [page, pageSize, sensors])

  const onSensorHover = (index) => {
    setCurrentHoveredSensor(index)
  }

  const onModify = (sensor) => {
    setShouldShowSideMenu(true)
    setDisplayedSensor(sensor)
  }

  const onRemove = (sensor) => {
    setShouldShowRemoveMenu(true)
    setDisplayedSensor(sensor)
  }

  const removeSensor = () => {
    setRemoveSensorLoading(true)

    sendRemoveSensor({ variables: { sensorId: displayedSensor.id } })
      .then(() => {
        setRemoveSensorLoading(false)
        setShouldShowRemoveMenu(false)

        setSensors(() => {
          return sensors.filter((sensor) => sensor.id !== displayedSensor.id)
        })
      })
      .catch((e) => {
        return handleGraphQLError(e)
      })
  }

  const onPaginationChange = (paginationInfo) => {
    setPage(paginationInfo.page)
    setPageSize(paginationInfo.pageSize)
  }

  const setFilter = (e) => {
    if (e.selectedItem.id === 'my-sensors') {
      setSensors(sensors.filter((sensor) => sensor.isUserOwner))
    } else {
      setSensors(processSensorData(data.sensors))
    }
  }

  return (
    <div className="sensors-page">
      <p className="title" tabIndex={0}>
        Sensors
      </p>
      <Dropdown
        id="sensor-group-dropdown"
        className="sensor-group-dropdown"
        label={sensorGroupDropdownItems[0].text}
        itemToString={(item) => (item ? item.text : '')}
        items={sensorGroupDropdownItems}
        onChange={setFilter}
      />

      <div className="sensors-map__container">
        <SensorsMap
          sensors={sensors}
          setDisplayedSensor={setDisplayedSensor}
          setShouldShowSideMenu={setShouldShowSideMenu}
          onSensorHover={onSensorHover}
          currentHoveredSensor={currentHoveredSensor}
        />
      </div>

      <div className="sensors-table__container">
        <SensorsTable
          loading={loading}
          history={history}
          sensors={sensors}
          onSensorHover={onSensorHover}
          currentHoveredSensor={currentHoveredSensor}
          onPaginationChange={onPaginationChange}
          page={page}
          pageSize={pageSize}
          currentlyVisibleSensors={currentlyVisibleSensors}
          setSensors={setSensors}
          shouldShowSideMenu={shouldShowSideMenu}
          shouldShowRemoveMenu={shouldShowRemoveMenu}
          removeSensorLoading={removeSensorLoading}
          displayedSensor={displayedSensor}
          setDisplayedSensor={setDisplayedSensor}
          setShouldShowSideMenu={setShouldShowSideMenu}
          setShouldShowRemoveMenu={setShouldShowRemoveMenu}
          removeSensor={removeSensor}
          onModify={onModify}
          onRemove={onRemove}
        />
      </div>
    </div>
  )
}
Example #18
Source File: QuesListPage.js    From stack-underflow with MIT License 4 votes vote down vote up
QuesListPage = ({ tagFilterActive, searchFilterActive }) => {
  const { tagName, query } = useParams();
  const { clearEdit, notify } = useStateContext();
  const { user } = useAuthContext();
  const [quesData, setQuesData] = useState(null);
  const [sortBy, setSortBy] = useState('HOT');
  const [page, setPage] = useState(1);
  const classes = useQuesListStyles();
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('xs'));
  const [fetchQuestions, { data, loading }] = useLazyQuery(GET_QUESTIONS, {
    fetchPolicy: 'network-only',
    onError: (err) => {
      notify(getErrorMsg(err), 'error');
    },
  });

  const getQues = (sortBy, page, limit, filterByTag, filterBySearch) => {
    fetchQuestions({
      variables: { sortBy, page, limit, filterByTag, filterBySearch },
    });
  };

  useEffect(() => {
    getQues(sortBy, 1, 12, tagName, query);
    setPage(1);
    window.scrollTo(0, 0);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sortBy, tagName, query]);

  useEffect(() => {
    if (data && page === 1) {
      setQuesData(data.getQuestions);
    }

    if (data && page !== 1) {
      setQuesData((prevState) => ({
        ...data.getQuestions,
        questions: prevState.questions.concat(
          filterDuplicates(prevState.questions, data.getQuestions.questions)
        ),
      }));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data]);

  const handleLoadPosts = () => {
    getQues(sortBy, page + 1, 12, tagName, query);
    setPage(page + 1);
  };

  return (
    <div className={classes.root}>
      <div className={classes.topBar}>
        <Typography
          variant={isMobile ? 'h6' : 'h5'}
          color="secondary"
          style={{ wordWrap: 'anywhere' }}
        >
          {tagFilterActive
            ? `Questions tagged [${tagName}]`
            : searchFilterActive
            ? `Search results for "${query}"`
            : 'All Questions'}
        </Typography>
        {user ? (
          <Button
            variant="contained"
            color="primary"
            size={isMobile ? 'small' : 'medium'}
            component={RouterLink}
            to="/ask"
            onClick={() => clearEdit()}
            style={{ minWidth: '9em' }}
          >
            Ask Question
          </Button>
        ) : (
          <AuthFormModal buttonType="ask" />
        )}
      </div>
      <SortQuesBar isMobile={isMobile} sortBy={sortBy} setSortBy={setSortBy} />
      <Divider />
      {loading && page === 1 && (
        <div style={{ minWidth: '100%', marginTop: '1em' }}>
          <LoadingSpinner size={60} />
        </div>
      )}
      {quesData &&
        (quesData.questions.length !== 0 ? (
          quesData.questions.map((q) => <QuesCard key={q.id} question={q} />)
        ) : (
          <Typography
            color="secondary"
            variant="h6"
            className={classes.noQuesText}
          >
            {tagFilterActive
              ? `There are no questions tagged "${tagName}".`
              : searchFilterActive
              ? `No matches found for your search "${query}".`
              : 'No questions found.'}
          </Typography>
        ))}
      {quesData && quesData.next && (
        <LoadMoreButton
          loading={page !== 1 && loading}
          handleLoadPosts={handleLoadPosts}
        />
      )}
    </div>
  );
}
Example #19
Source File: BankTransferFlow.js    From ucurtmetre with GNU General Public License v3.0 4 votes vote down vote up
function BankTransferFlow() {
  const location = useLocation();
  const [currentBank, setCurrentBank] = React.useState(-1);
  const blAuth = localStorage.getItem('blAuth');

  const [
    collectDonation,
    { data: donationData, error: donationError, loading: donationLoading },
  ] = useMutation(COLLECT_DONATION, { onError: err => err });

  const [getOauthUrl, { data }] = useLazyQuery(GET_OAUTH_URL, {
    variables: {
      campaignId: 'donate-all',
      returnUrl: 'https://destek.ucurtmaprojesi.com/auth/callback',
    },
  });

  const [
    getBanks,
    { error: bankError, data: bankData, loading: bankLoading },
  ] = useLazyQuery(GET_BANKS, {
    context: {
      headers: {
        oauth2: blAuth,
      },
    },
  });

  useEffect(() => {
    if (blAuth) {
      getBanks();
    } else {
      getOauthUrl();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (bankError) {
      localStorage.removeItem('blAuth');
      getOauthUrl();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [bankError]);

  return (
    <div className="bank-transfer-flow">
      {location.state?.state?.redirectError && (
        <Alert
          icon={<AlertCircle />}
          variant="danger"
          message="BiLira ile bağlantı kurulurken bir hata oluştu. Lütfen daha sonra tekrar deneyin."
        />
      )}
      <Alert
        message={
          <>
            <p>
              Yapacağınız destekleri güvenli ve hızlı bir şekilde öğrencimize
              ulaştırabilmek için{' '}
              <a
                href="https://www.bilira.co/"
                target="_blank"
                rel="noopener noreferrer"
              >
                BiLira
              </a>{' '}
              ile çalışıyoruz.
            </p>
            {!blAuth && (
              <p>
                Aşağıdaki butonu kullanarak hızlıca hesap oluşturabilir, varolan
                hesabınızla transferi yapacağınız banka hesabına kolayca
                ulaşabilirsiniz.
              </p>
            )}
          </>
        }
        icon={<AlertCircle />}
      />
      {bankLoading && (
        <div>
          <Skeleton count={4} />
        </div>
      )}
      {data && !blAuth && (
        <a
          className="login-with-bilira"
          href={data.biliraOAuthUrl.authorizationUri}
        >
          BiLira ile giriş yap
        </a>
      )}
      {bankData && !donationData && (
        <>
          <SelectBank
            bankData={bankData}
            onSelect={bankId => setCurrentBank(bankId)}
            selectedBank={currentBank}
          />
          {currentBank !== -1 && (
            <Formik
              initialValues={{
                email: '',
                amount: '',
                consentToReceiveNews: false,
                consentToUserAgreement: false,
              }}
              validationSchema={bankTransferValidation}
              onSubmit={async (values, { setSubmitting }) => {
                setSubmitting(true);
                collectDonation({
                  variables: {
                    campaignCode: 'campaign-all',
                    bankId: parseInt(currentBank, 10),
                    email: values.email,
                    amount: parseFloat(values.amount),
                  },
                  context: {
                    headers: {
                      oauth2: blAuth,
                    },
                  },
                });
              }}
            >
              {({ isSubmitting, dirty, isValid }) => (
                <Form data-private>
                  <div>
                    <Input
                      label="Email"
                      name="email"
                      type="email"
                      placeholder="Lütfen email adresinizi girin."
                    />

                    <Input
                      label="Miktar"
                      name="amount"
                      type="number"
                      placeholder="Lütfen göndermek istediğiniz destek miktarını giriniz."
                    />
                  </div>

                  <Agreements
                    kvkkName="consentToReceiveNews"
                    agreementName="consentToUserAgreement"
                  />

                  <button
                    className="button secondary-button submit"
                    type="submit"
                    disabled={
                      isSubmitting || !dirty || !isValid || donationLoading
                    }
                    width="full"
                  >
                    Destekle
                  </button>
                </Form>
              )}
            </Formik>
          )}
        </>
      )}
      {(donationData || donationError) && (
        <BankDetailViewer error={donationError} data={donationData} />
      )}
    </div>
  );
}
Example #20
Source File: job-form.js    From stacker.news with MIT License 4 votes vote down vote up
// need to recent list items
export default function JobForm ({ item, sub }) {
  const storageKeyPrefix = item ? undefined : `${sub.name}-job`
  const router = useRouter()
  const [monthly, setMonthly] = useState(satsMin2Mo(item?.maxBid || sub.baseCost))
  const [getAuctionPosition, { data }] = useLazyQuery(gql`
    query AuctionPosition($id: ID, $bid: Int!) {
      auctionPosition(sub: "${sub.name}", id: $id, bid: $bid)
    }`,
  { fetchPolicy: 'network-only' })
  const [upsertJob] = useMutation(gql`
    mutation upsertJob($id: ID, $title: String!, $company: String!, $location: String,
      $remote: Boolean, $text: String!, $url: String!, $maxBid: Int!, $status: String) {
      upsertJob(sub: "${sub.name}", id: $id, title: $title, company: $company,
        location: $location, remote: $remote, text: $text,
        url: $url, maxBid: $maxBid, status: $status) {
        id
      }
    }`
  )

  const JobSchema = Yup.object({
    title: Yup.string().required('required').trim(),
    company: Yup.string().required('required').trim(),
    text: Yup.string().required('required').trim(),
    url: Yup.string()
      .or([Yup.string().email(), Yup.string().url()], 'invalid url or email')
      .required('Required'),
    maxBid: Yup.number('must be number')
      .integer('must be whole').min(sub.baseCost, `must be at least ${sub.baseCost}`)
      .required('required'),
    location: Yup.string().test(
      'no-remote',
      "don't write remote, just check the box",
      v => !v?.match(/\bremote\b/gi))
      .when('remote', {
        is: (value) => !value,
        then: Yup.string().required('required').trim()
      })
  })

  const position = data?.auctionPosition

  useEffect(() => {
    const initialMaxBid = Number(item?.maxBid || localStorage.getItem(storageKeyPrefix + '-maxBid')) || sub.baseCost
    getAuctionPosition({ variables: { id: item?.id, bid: initialMaxBid } })
    setMonthly(satsMin2Mo(initialMaxBid))
  }, [])

  return (
    <>
      <Form
        className='py-5'
        initial={{
          title: item?.title || '',
          company: item?.company || '',
          location: item?.location || '',
          remote: item?.remote || false,
          text: item?.text || '',
          url: item?.url || '',
          maxBid: item?.maxBid || sub.baseCost,
          stop: false,
          start: false
        }}
        schema={JobSchema}
        storageKeyPrefix={storageKeyPrefix}
        onSubmit={(async ({ maxBid, stop, start, ...values }) => {
          let status
          if (start) {
            status = 'ACTIVE'
          } else if (stop) {
            status = 'STOPPED'
          }

          const { error } = await upsertJob({
            variables: {
              id: item?.id,
              sub: sub.name,
              maxBid: Number(maxBid),
              status,
              ...values
            }
          })
          if (error) {
            throw new Error({ message: error.toString() })
          }

          if (item) {
            await router.push(`/items/${item.id}`)
          } else {
            await router.push(`/~${sub.name}/recent`)
          }
        })}
      >
        <Input
          label='job title'
          name='title'
          required
          autoFocus
        />
        <Input
          label='company'
          name='company'
          required
        />
        <BForm.Row className='mr-0'>
          <Col>
            <Input
              label='location'
              name='location'
            />
          </Col>
          <Checkbox
            label={<div className='font-weight-bold'>remote</div>} name='remote' hiddenLabel
            groupClassName={styles.inlineCheckGroup}
          />
        </BForm.Row>
        <MarkdownInput
          label='description'
          name='text'
          as={TextareaAutosize}
          minRows={6}
          required
        />
        <Input
          label={<>how to apply <small className='text-muted ml-2'>url or email address</small></>}
          name='url'
          required
        />
        <Input
          label={
            <div className='d-flex align-items-center'>bid
              <Info>
                <ol className='font-weight-bold'>
                  <li>The higher your bid the higher your job will rank</li>
                  <li>The minimum bid is {sub.baseCost} sats/min</li>
                  <li>You can increase or decrease your bid, and edit or stop your job at anytime</li>
                  <li>Your job will be hidden if your wallet runs out of sats and can be unhidden by filling your wallet again</li>
                </ol>
              </Info>
            </div>
          }
          name='maxBid'
          onChange={async (formik, e) => {
            if (e.target.value >= sub.baseCost && e.target.value <= 100000000) {
              setMonthly(satsMin2Mo(e.target.value))
              getAuctionPosition({ variables: { id: item?.id, bid: Number(e.target.value) } })
            } else {
              setMonthly(satsMin2Mo(sub.baseCost))
            }
          }}
          append={<InputGroup.Text className='text-monospace'>sats/min</InputGroup.Text>}
          hint={<PriceHint monthly={monthly} />}
        />
        <><div className='font-weight-bold text-muted'>This bid puts your job in position: {position}</div></>
        {item && <StatusControl item={item} />}
        <SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
      </Form>
    </>
  )
}
Example #21
Source File: link-form.js    From stacker.news with MIT License 4 votes vote down vote up
export function LinkForm ({ item, editThreshold }) {
  const router = useRouter()
  const client = useApolloClient()

  const [getPageTitle, { data }] = useLazyQuery(gql`
    query PageTitle($url: String!) {
      pageTitle(url: $url)
    }`, {
    fetchPolicy: 'network-only'
  })
  const [getDupes, { data: dupesData }] = useLazyQuery(gql`
  ${ITEM_FIELDS}
  query Dupes($url: String!) {
    dupes(url: $url) {
      ...ItemFields
    }
  }`, {
    fetchPolicy: 'network-only'
  })

  const [upsertLink] = useMutation(
    gql`
      mutation upsertLink($id: ID, $title: String!, $url: String!, $boost: Int, $forward: String) {
        upsertLink(id: $id, title: $title, url: $url, boost: $boost, forward: $forward) {
          id
        }
      }`
  )

  const LinkSchema = Yup.object({
    title: Yup.string().required('required').trim(),
    url: Yup.string().matches(URL, 'invalid url').required('required'),
    ...AdvPostSchema(client)
  })

  return (
    <Form
      initial={{
        title: item?.title || '',
        url: item?.url || '',
        ...AdvPostInitial
      }}
      schema={LinkSchema}
      onSubmit={async ({ boost, ...values }) => {
        const { error } = await upsertLink({
          variables: { id: item?.id, boost: Number(boost), ...values }
        })
        if (error) {
          throw new Error({ message: error.toString() })
        }
        if (item) {
          await router.push(`/items/${item.id}`)
        } else {
          await router.push('/recent')
        }
      }}
      storageKeyPrefix={item ? undefined : 'link'}
    >
      <Input
        label='title'
        name='title'
        overrideValue={data?.pageTitle}
        required
      />
      <Input
        label='url'
        name='url'
        required
        autoFocus
        hint={editThreshold
          ? <div className='text-muted font-weight-bold'><Countdown date={editThreshold} /></div>
          : null}
        onChange={async (formik, e) => {
          if ((/^ *$/).test(formik?.values.title)) {
            getPageTitle({
              variables: { url: e.target.value }
            })
          }
          getDupes({
            variables: { url: e.target.value }
          })
        }}
      />
      {!item && <AdvPostForm />}
      <ActionTooltip>
        <SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
      </ActionTooltip>
      {dupesData?.dupes?.length > 0 &&
        <div className='mt-3'>
          <AccordianItem
            show
            headerColor='#c03221'
            header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>dupes</div>}
            body={
              <div>
                {dupesData.dupes.map((item, i) => (
                  <Item item={item} key={item.id} />
                ))}
              </div>
              }
          />
        </div>}

    </Form>
  )
}