Skip to content

Commit

Permalink
Move job list so the list isn't fetched until after auth
Browse files Browse the repository at this point in the history
  • Loading branch information
vcarl committed May 1, 2024
1 parent bbfe235 commit 119e932
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 173 deletions.
174 changes: 174 additions & 0 deletions src/components/JobSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React from "react";
import styled from "styled-components";

import { Button, FocusBoundary, Form, Link, Offer } from "@components";
import { toQueryString } from "@utils/toQueryString";

const ResultsFooter = styled.div`
margin: 8rem 2rem;
text-align: center;
@media (min-width: 600px) {
margin: 8rem;
}
> button {
margin: 0;
}
`;

const LOADING = "LOADING";
const SUCCESS = "SUCCESS";
const ERROR = "ERROR";

const fields = [
{
name: "type",
type: "select",
defaultValue: "hiring",
options: [
{ value: "hiring", label: "I'm looking for a job" },
{ value: "for-hire", label: "I'm looking for an employee" },
],
},
{
name: "range",
type: "select",
defaultValue: "180",
options: [
{ value: "7", label: "offers posted in last week" },
{ value: "30", label: "offers posted in last 30 days" },
{ value: "90", label: "offers posted in last 3 months" },
{ value: "180", label: "offers posted in last 6 months" },
{ value: "365", label: "offers posted in last year" },
],
},
{
name: "limit",
type: "select",
defaultValue: "50",
options: [
{ value: "50", label: "load 50 results at a time" },
{ value: "100", label: "load 100 results at a time" },
],
},
{
name: "query",
type: "text",
placeholder: "Text search",
},
{
label: "Employer helps with visa",
name: "provideVisa",
type: "checkbox",
defaultValue: false,
},
{
label: "Accepts remote candidates",
name: "allowRemote",
type: "checkbox",
defaultValue: false,
},
{
label: "Offer is an internship / no exp. required",
name: "internship",
type: "checkbox",
defaultValue: false,
},
];

const JobSearch = ({ toggleModal, setSidebar }) => {
const debounceTimer = React.useRef();
const [state, setState] = React.useState({});
const { limit, next, results = [], status = LOADING, url } = state;

const onChangeQuery = React.useCallback(
(query) => {
setState((_state) => ({
..._state,
clearResults: true,
limit: parseInt(query.limit, 10),
url: `https://reactistory.com:8443/api/job?${toQueryString(query)}`,
}));
},
[setState],
);

const onClickNext = () =>
setState((_state) => ({
..._state,
next: undefined,
status: LOADING,
url: _state.next,
}));

React.useEffect(() => {
if (url) {
clearTimeout(debounceTimer.current);

debounceTimer.current = setTimeout(() => {
setState((_state) => ({
..._state,
clearResults: undefined,
results: _state.clearResults ? [] : _state.results,
status: LOADING,
}));

fetch(url)
.then((res) => res.json())
.then((data) => {
setState((_state) => ({
..._state,
status: SUCCESS,
results: _state.results.concat(data.messages),
next:
data.messages.length === limit
? `https://reactistory.com:8443${data.paginate.prev}`
: undefined,
}));
})
.catch(() => {
setState((_state) => ({ ..._state, status: ERROR }));
});
}, 400);
}
}, [limit, url]);

return (
<>
<FocusBoundary onChange={setSidebar}>
<Form allowSubmit={false} fields={fields} onChange={onChangeQuery} />
</FocusBoundary>
<div>
{results.map((result, i) => (
<Offer
{...result}
key={result.id}
last={i === results.length - 1}
onClickGetInTouch={toggleModal}
/>
))}
<ResultsFooter>
{status === LOADING ? (
<p>Fetching posts...</p>
) : next ? (
<Button onClick={onClickNext}>Fetch More Results</Button>
) : null}
{status === ERROR ? (
<p>
Something went wrong - please try again! If this problem persists,
please <Link href="/contact/">let us know</Link>.
</p>
) : status === SUCCESS && results.length === 0 ? (
<p>
We couldn’t find any results for your query - try something
different!
</p>
) : null}
</ResultsFooter>
</div>
</>
);
};

export default JobSearch;
179 changes: 6 additions & 173 deletions src/pages/jobs.js
Original file line number Diff line number Diff line change
@@ -1,191 +1,24 @@
import React from "react";
import styled from "styled-components";

import {
Button,
FocusBoundary,
Form,
Layout,
Link,
Modal,
Offer,
} from "@components";
import { toQueryString } from "@utils/toQueryString";
import { DiscordAuth } from "@components/DiscordAuth";

const ResultsFooter = styled.div`
margin: 8rem 2rem;
text-align: center;
@media (min-width: 600px) {
margin: 8rem;
}
> button {
margin: 0;
}
`;

const LOADING = "LOADING";
const SUCCESS = "SUCCESS";
const ERROR = "ERROR";

const fields = [
{
name: "type",
type: "select",
defaultValue: "hiring",
options: [
{ value: "hiring", label: "I'm looking for a job" },
{ value: "for-hire", label: "I'm looking for an employee" },
],
},
{
name: "range",
type: "select",
defaultValue: "180",
options: [
{ value: "7", label: "offers posted in last week" },
{ value: "30", label: "offers posted in last 30 days" },
{ value: "90", label: "offers posted in last 3 months" },
{ value: "180", label: "offers posted in last 6 months" },
{ value: "365", label: "offers posted in last year" },
],
},
{
name: "limit",
type: "select",
defaultValue: "50",
options: [
{ value: "50", label: "load 50 results at a time" },
{ value: "100", label: "load 100 results at a time" },
],
},
{
name: "query",
type: "text",
placeholder: "Text search",
},
{
label: "Employer helps with visa",
name: "provideVisa",
type: "checkbox",
defaultValue: false,
},
{
label: "Accepts remote candidates",
name: "allowRemote",
type: "checkbox",
defaultValue: false,
},
{
label: "Offer is an internship / no exp. required",
name: "internship",
type: "checkbox",
defaultValue: false,
},
];
import { Layout, Link, Modal } from "@components";
import JobSearch from "../components/JobSearch";

const Jobs = () => {
const debounceTimer = React.useRef();
const [state, setState] = React.useState({});
const { limit, next, results = [], status = LOADING, url } = state;
const [showModal, setModal] = React.useState(false);
const toggleModal = () => setModal((prev) => !prev);

const onChangeQuery = React.useCallback(
(query) => {
setState((_state) => ({
..._state,
clearResults: true,
limit: parseInt(query.limit, 10),
url: `https://reactistory.com:8443/api/job?${toQueryString(query)}`,
}));
},
[setState],
);

const onClickNext = () =>
setState((_state) => ({
..._state,
next: undefined,
status: LOADING,
url: _state.next,
}));

React.useEffect(() => {
if (url) {
clearTimeout(debounceTimer.current);

debounceTimer.current = setTimeout(() => {
setState((_state) => ({
..._state,
clearResults: undefined,
results: _state.clearResults ? [] : _state.results,
status: LOADING,
}));

fetch(url)
.then((res) => res.json())
.then((data) => {
setState((_state) => ({
..._state,
status: SUCCESS,
results: _state.results.concat(data.messages),
next:
data.messages.length === limit
? `https://reactistory.com:8443${data.paginate.prev}`
: undefined,
}));
})
.catch(() => {
setState((_state) => ({ ..._state, status: ERROR }));
});
}, 400);
}
}, [limit, url]);

return (
<Layout title="Jobs" largeTitle sidebar>
{(setSidebar) => (
<>
<h1>
Reactiflux <span>Jobs</span>
</h1>
<FocusBoundary onChange={setSidebar}>
<Form
allowSubmit={false}
fields={fields}
onChange={onChangeQuery}
/>
</FocusBoundary>
<div>
{results.map((result, i) => (
<Offer
{...result}
key={result.id}
last={i === results.length - 1}
onClickGetInTouch={toggleModal}
/>
))}
<ResultsFooter>
{status === LOADING ? (
<p>Fetching posts...</p>
) : next ? (
<Button onClick={onClickNext}>Fetch More Results</Button>
) : null}
{status === ERROR ? (
<p>
Something went wrong - please try again! If this problem
persists, please <Link href="/contact/">let us know</Link>.
</p>
) : status === SUCCESS && results.length === 0 ? (
<p>
We couldn’t find any results for your query - try something
different!
</p>
) : null}
</ResultsFooter>
</div>
<DiscordAuth>
<JobSearch toggleModal={toggleModal} setSidebar={setSidebar} />
</DiscordAuth>
<Modal close={() => setModal(false)} isOpen={showModal}>
<p>
If the job post does not contain a dedicated email, link, or phone
Expand Down

0 comments on commit 119e932

Please sign in to comment.