hooks#useQueryString JavaScript Examples

The following examples show how to use hooks#useQueryString. 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: CourseSearch.jsx    From ResoBin with MIT License 6 votes vote down vote up
CourseSearch = ({ loading, setLoading }) => {
  const { isDesktop } = useResponsive()
  const { deleteQueryString, getQueryString, setQueryString } = useQueryString()
  const showFilter = useSelector(selectIsDropdownActive)

  const [search, setSearch] = useState(getQueryString('q'))

  const handleSearch = (event) => {
    setLoading(true)
    setSearch(event.target.value)

    setQueryString('q', event.target.value)
    deleteQueryString('p')
  }

  return (
    <>
      <SearchContainer>
        <CourseFinderFilterDropdown
          showFilter={showFilter && isDesktop}
          setLoading={setLoading}
        />
        {showFilter && isDesktop && <Overlay />}

        <StyledInput
          size="large"
          placeholder="Course code, name or description"
          allowClear
          maxLength={100}
          onChange={handleSearch}
          value={search}
          prefix={<StyledIcon Icon={loading ? LoadingOutlined : Search} />}
        />
      </SearchContainer>

      <CourseFinderFilterFloatButton />
    </>
  )
}
Example #2
Source File: CourseFinderFilterContainer.jsx    From ResoBin with MIT License 6 votes vote down vote up
CourseFinderFilterDropdown = ({ setLoading }) => {
  const { deleteQueryString } = useQueryString()
  const showFilter = useSelector(selectIsDropdownActive)

  useEffect(() => {
    document.body.style.overflow = showFilter ? 'hidden' : 'auto'
    return () => {
      document.body.style.overflow = 'auto'
    }
  }, [showFilter])

  return (
    <ContainerDropdown showFilter={showFilter}>
      <Header>
        <Title>Filter</Title>
        <ClearAll onClick={() => deleteQueryString(...filterKeys)}>
          Reset all
        </ClearAll>
      </Header>

      <Divider margin="0.75rem 0" />

      <ListDropdown showFilter={showFilter}>
        <CourseFinderFilterForm setLoading={setLoading} />
      </ListDropdown>
    </ContainerDropdown>
  )
}
Example #3
Source File: CourseContainer.jsx    From ResoBin with MIT License 5 votes vote down vote up
CourseFinderContainer = () => {
  const { getQueryString, deleteQueryString } = useQueryString()

  const [courseData, setCourseData] = useState([])
  const [loading, setLoading] = useState(true)

  const fetchCourses = async (params) => {
    setLoading(true)

    try {
      if (ajaxRequest) ajaxRequest.cancel()
      ajaxRequest = axios.CancelToken.source()

      const response = await API.courses.list({
        params,
        cancelToken: ajaxRequest.token,
      })
      setCourseData(response)
    } catch (error) {
      if (axios.isCancel(error)) return
      toast({ status: 'error', content: error })
    }

    setLoading(false)
  }

  useEffect(() => {
    const filter = getQueryString()
    const params = {
      search_fields: 'code,title,description',
      q: filter.q,
      ...filterKeys.reduce(
        (accumulator, value) => ({ ...accumulator, [value]: filter[value] }),
        {}
      ),
    }

    fetchCourses(params)
  }, [getQueryString])

  return (
    <>
      <CourseSearch loading={loading} setLoading={setLoading} />

      <CourseList
        title="Courses"
        count={courseData.count}
        courseList={courseData.results}
        loading={loading}
        setLoading={setLoading}
      />

      <Aside
        title="Filter"
        subtitle={
          <ClearAll onClick={() => deleteQueryString(...filterKeys)}>
            Reset all
          </ClearAll>
        }
        loading={loading}
      >
        <CourseFinderFilterForm setLoading={setLoading} />
      </Aside>
    </>
  )
}
Example #4
Source File: CourseList.jsx    From ResoBin with MIT License 5 votes vote down vote up
CourseFinderList = ({
  title,
  count,
  courseList,
  loading = false,
  setLoading,
}) => {
  const { getQueryString, setQueryString } = useQueryString()
  const pageNo = getQueryString('p') || 1

  const handlePageChange = (page) => {
    setLoading(true)
    setQueryString('p', page)
  }

  return (
    <>
      <PageHeading>
        <PageTitle>{title}</PageTitle>
        {!loading && <PageSubtitle>{count}&nbsp;results</PageSubtitle>}
      </PageHeading>

      <CardSplitSkeleton active={loading} />
      <NotFoundSearch active={!loading && !count} />

      <TransitionGroup>
        {!loading &&
          courseList?.map((courseData) => (
            <CSSTransition
              key={courseData.code}
              timeout={200}
              unmountOnExit
              classNames="card"
            >
              <CardTransition>
                <CourseItem courseData={courseData} />
              </CardTransition>
            </CSSTransition>
          ))}
      </TransitionGroup>

      {!loading && (
        <Pagination
          defaultPageSize="10"
          defaultCurrent={pageNo}
          responsive
          showSizeChanger={false}
          hideOnSinglePage
          onChange={handlePageChange}
          total={count}
        />
      )}
    </>
  )
}
Example #5
Source File: FavouritesContainer.jsx    From ResoBin with MIT License 5 votes vote down vote up
FavouritesContainer = () => {
  const { getQueryString } = useQueryString()

  const [loading, setLoading] = useState(true)
  const [courseData, setCourseData] = useState([])

  const fetchCourses = async (params) => {
    try {
      setLoading(true)
      const response = await API.profile.favorites({ params })
      setCourseData(response)
    } catch (error) {
      toast({ status: 'error', content: error })
    } finally {
      setLoading(false)
    }
  }

  useEffect(() => {
    const filter = getQueryString()
    const params = {
      q: filter.q,
      search_fields: 'code,title,description',
      page: filter.p,
    }

    fetchCourses(params)
  }, [getQueryString])

  return (
    <>
      <CourseSearch
        loading={loading}
        setLoading={setLoading}
        showFilter={false}
      />

      <CourseList
        title="Favourites"
        count={courseData.count}
        courseList={courseData.results}
        loading={loading}
        setLoading={setLoading}
      />

      <Aside title="My contributions">
        <Empty description={<PageSubtitle>Coming soon!</PageSubtitle>} />
      </Aside>
    </>
  )
}
Example #6
Source File: TimetableShareContainer.jsx    From ResoBin with MIT License 5 votes vote down vote up
TimetableContainerCustom = () => {
  const { getQueryString } = useQueryString()
  const [courseTimetableList, setCourseTimetableList] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    const fetchTimetableData = async () => {
      try {
        setLoading(true)
        const params = { ids: encodeURIComponent(getQueryString('ids')) }
        const response = await API.timetable.list({ params })
        setCourseTimetableList(response.results)
      } catch (error) {
        toast({ status: 'error', content: error })
      } finally {
        setLoading(false)
      }
    }

    fetchTimetableData()
  }, [getQueryString])

  return (
    <>
      <PageHeading>
        <PageTitle>Timetable (Shared)</PageTitle>
      </PageHeading>

      {loading && <LoaderAnimation />}

      <Spin
        spinning={loading}
        indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
      >
        <TimetableLayout>
          {courseTimetableList.map((item) => (
            <TimetableCourseItem key={item.id} data={item} />
          ))}

          <CurrentTime mode="vertical" />
        </TimetableLayout>
      </Spin>

      <Aside />
    </>
  )
}
Example #7
Source File: ContributeForm.jsx    From ResoBin with MIT License 4 votes vote down vote up
ContributeForm = ({ fileItem, handleUpload, handleDelete }) => {
  const { getQueryString } = useQueryString()
  const course = getQueryString('course')

  const tagOptions = tags.resourceTags.map((tag) => ({
    label: tag,
    value: kebabCase(tag),
  }))

  const courseListMinified = useSelector(selectCourseListMinified)
  const courseOptions = courseListMinified.map(({ code, title }) => ({
    label: `${code}: ${title}`,
    value: code,
  }))

  const [form] = Form.useForm()

  return (
    <Form
      form={form}
      name="contribute"
      onFinish={handleUpload}
      layout="vertical"
      initialValues={{ ...fileItem.details, course }}
    >
      <Form.Item
        name="title"
        rules={[
          { required: true, message: 'Title is required.' },
          { min: 5, message: 'Title must be atleast 5 characters.' },
          { max: 100, message: 'Title must be atmost 100 characters.' },
        ]}
      >
        <Input placeholder="Title" />
      </Form.Item>

      <Form.Item
        name="author"
        rules={[{ max: 255, message: 'Author must be atmost 255 characters.' }]}
      >
        <Input placeholder="Author" />
      </Form.Item>

      <Form.Item
        name="description"
        rules={[
          { max: 500, message: 'Description must be atmost 500 characters.' },
        ]}
      >
        <Input.TextArea
          autoSize={{ minRows: 1, maxRows: 10 }}
          placeholder="Description"
        />
      </Form.Item>

      <Form.Item
        name="course"
        rules={[{ required: true, message: 'Course is required.' }]}
      >
        <Select showSearch placeholder="Course" options={courseOptions} />
      </Form.Item>

      <Form.Item name="tags">
        <Select
          mode="tags"
          placeholder="Add tags"
          showArrow
          tokenSeparators={[',']}
          options={tagOptions}
        />
      </Form.Item>

      <ButtonContainer>
        <ButtonSquare
          type="primary"
          htmlType="submit"
          loading={fileItem.status === 'uploading'}
        >
          Submit
        </ButtonSquare>

        <Button
          type="primary"
          danger
          onClick={handleDelete}
          hidden={fileItem.status === 'uploading'}
          style={{ borderRadius: '0.5rem' }}
        >
          Delete
        </Button>
      </ButtonContainer>
    </Form>
  )
}
Example #8
Source File: CourseFinderFilterForm.jsx    From ResoBin with MIT License 4 votes vote down vote up
CourseFinderFilterForm = ({ setLoading }) => {
  const { deleteQueryString, getQueryString, setQueryString } = useQueryString()
  const [form] = Form.useForm()

  const handleFilterClear = (formField, qsFields) => () => {
    const defaultInitialValues = {
      semester: [],
      credits: [2, 9],
      halfsem: false,
      running: false,
      department: [],
      tags: [],
      slots: [],
    }

    form.setFieldsValue({
      [formField]: defaultInitialValues[formField],
    })

    deleteQueryString(...qsFields, 'p')
  }

  const handleFilterUpdate = (_, allFields) => {
    setLoading(true)
    deleteQueryString('p')

    if (allFields?.credits?.[0] !== 2)
      setQueryString('credits_min', allFields.credits[0])
    else deleteQueryString('credits_min')

    if (allFields?.credits?.[1] !== 9)
      setQueryString('credits_max', allFields.credits[1])
    else deleteQueryString('credits_max')

    setQueryString('department', allFields.department)
    setQueryString('semester', allFields.semester)
    setQueryString('tags', allFields.tags)
    setQueryString('slots', allFields.slots)

    if (allFields.halfsem) setQueryString('halfsem', 'true')
    else deleteQueryString('halfsem')

    if (allFields.running) setQueryString('running', 'true')
    else deleteQueryString('running')
  }

  const semesterOptions = [
    { label: 'Autumn', value: 'autumn' },
    { label: 'Spring', value: 'spring' },
  ]

  const departmentOptions = useSelector(selectDepartments)?.map(
    (department) => ({
      label: department.name,
      value: department.slug,
    })
  )

  const tagOptions = tags.courseTags.map((tag) => ({
    label: tag,
    value: kebabCase(tag),
  }))

  const slotOptions = Object.keys(slots).map((slot) => ({
    label: slot,
    value: slot,
  }))

  return (
    <Form
      form={form}
      name="course_filter"
      layout="vertical"
      onValuesChange={handleFilterUpdate}
      initialValues={{
        semester: getQueryString('semester')?.split(',') ?? [],
        halfsem: getQueryString('halfsem') === 'true',
        running: getQueryString('running') === 'true',
        credits: [
          parseInt(getQueryString('credits_min') ?? 2, 10),
          parseInt(getQueryString('credits_max') ?? 9, 10),
        ],
        department: getQueryString('department')?.split(',') ?? [],
        tags: getQueryString('tags')?.split(',') ?? [],
        slots: getQueryString('slots')?.split(',') ?? [],
      }}
      style={{ gap: '1rem', padding: '0 0.5rem' }}
    >
      <CourseFinderFilterItem
        label="Running courses only"
        onClear={handleFilterClear('running', ['running'])}
        content={
          <Form.Item name="running" valuePropName="checked">
            <Switch />
          </Form.Item>
        }
      />

      <div>
        <CourseFinderFilterItem
          label="Departments"
          onClear={handleFilterClear('department', ['department'])}
        />
        <Form.Item name="department">
          <Select
            mode="multiple"
            options={departmentOptions}
            placeholder="Type something..."
            showArrow
          />
        </Form.Item>
      </div>

      <div>
        <CourseFinderFilterItem
          label={
            <>
              Slots <b>(beta)</b>
            </>
          }
          onClear={handleFilterClear('slots', ['slots'])}
        />
        <Form.Item name="slots">
          <Select
            mode="multiple"
            options={slotOptions}
            placeholder="Type something..."
            showArrow
          />
        </Form.Item>
      </div>

      <div>
        <CourseFinderFilterItem
          label="Semesters"
          onClear={handleFilterClear('semester', ['semester'])}
        />
        <Form.Item name="semester">
          <Checkbox.Group
            options={semesterOptions}
            style={{ display: 'flex', gap: '1rem' }}
          />
        </Form.Item>
      </div>

      <CourseFinderFilterItem
        label="Half semester only"
        onClear={handleFilterClear('halfsem', ['halfsem'])}
        content={
          <Form.Item name="halfsem" valuePropName="checked">
            <Switch />
          </Form.Item>
        }
      />

      <div>
        <CourseFinderFilterItem
          label="Credits"
          onClear={handleFilterClear('credits', ['credits_min', 'credits_max'])}
        />
        <Form.Item name="credits">
          <Slider
            range
            min={2}
            step={null}
            max={9}
            marks={{
              2: '<3',
              3: '3',
              4: '4',
              5: '5',
              6: '6',
              7: '7',
              8: '8',
              9: '>8',
            }}
            tipFormatter={null}
            style={{ marginRight: '1rem', marginLeft: '0.5rem' }}
          />
        </Form.Item>
      </div>

      <div>
        <CourseFinderFilterItem
          label="Tags"
          onClear={handleFilterClear('tags', ['tags'])}
        />
        <Form.Item name="tags">
          <Select
            mode="multiple"
            options={tagOptions}
            placeholder="Select something..."
            showArrow
          />
        </Form.Item>
      </div>
    </Form>
  )
}
Example #9
Source File: Login.jsx    From ResoBin with MIT License 4 votes vote down vote up
Login = () => {
  const dispatch = useDispatch()
  const navigate = useNavigate()
  const location = useLocation()
  const { deleteQueryString, getQueryString } = useQueryString()
  const { isAuthenticated, loading } = useSelector((state) => state.auth)

  useEffect(() => {
    // * isAuthenticated === true => already authenticated => redirect away from login
    // * isAuthenticated === null => auth status unknown => check with backend server
    // * isAuthenticated === false => not authenticated => check if auth is possible

    if (isAuthenticated) {
      const state = JSON.parse(getQueryString('state')) ?? '/'
      navigate(state, { replace: true })
    } else if (isAuthenticated === null) {
      dispatch(getAuthStatusAction())
    } else {
      // ? If user is not authenticated
      const code = getQueryString('code')
      if (code) {
        const loginUser = async (params) => {
          try {
            const response = await dispatch(loginAction({ params }))
            toast({ status: 'success', content: response?.payload?.detail })
          } catch (error) {
            toast({ status: 'error', content: error })
          }
        }

        const params = { code, redir: SSO.BASE_REDIRECT_URI }
        deleteQueryString('code')
        loginUser(params)
      }

      // ? If SSO login is unsuccessfull, an error param appears in the query string
      const error = getQueryString('error')
      if (error) {
        toast({ status: 'error', content: `Error: ${error}` })
        deleteQueryString('error')
      }
    }
  }, [dispatch, navigate, getQueryString, deleteQueryString, isAuthenticated])

  const redirectLogin = () => {
    window.location.href = getLoginURL(location.state?.from)
  }

  if (loading) return <LoaderAnimation fixed />

  return (
    <PageContainer disable={['menu', 'aside']}>
      <Helmet>
        <title>Log In - ResoBin</title>
        <meta name="description" content="Login to continue" />
      </Helmet>
      <CSRFToken />

      <AuthBoxContainer>
        <ResoBinLogo width="3rem" alt="logo" />

        <h1>Welcome to ResoBin!</h1>

        <AuthButton
          type="primary"
          size="large"
          color="#303f9f"
          onClick={redirectLogin}
        >
          Login via SSO
        </AuthButton>

        <span>
          By logging in you accept our&nbsp;
          <Link to="/terms-and-conditions">Terms and Conditions</Link>
          &nbsp;and our &nbsp;
          <Link to="/privacy-policy">Privacy Policy</Link>.
        </span>
      </AuthBoxContainer>
    </PageContainer>
  )
}