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 |
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 |
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 |
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 |
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} 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 |
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 |
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 |
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 |
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 |
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
<Link to="/terms-and-conditions">Terms and Conditions</Link>
and our
<Link to="/privacy-policy">Privacy Policy</Link>.
</span>
</AuthBoxContainer>
</PageContainer>
)
}