Skip to content
This repository has been archived by the owner on Jul 19, 2023. It is now read-only.

Commit

Permalink
Merge pull request #6 from AJRedDevil/develop
Browse files Browse the repository at this point in the history
Job Board UI
  • Loading branch information
AjanShrestha authored Dec 23, 2019
2 parents 36486ec + 733c1bc commit 7a221a8
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 53 deletions.
11 changes: 10 additions & 1 deletion api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ const getAsync = promisify(client.get).bind(client);
const app = express();
const port = 5000;

app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // update to match the domain you will make the request from
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
next();
});

app.get('/', (req, res) => res.send('Jobs API!'));
app.get('/jobs', async (req, res) => {
app.get('/api/jobs', async (req, res) => {
const jobs = await getAsync('github');
return res.send(jobs);
});
Expand Down
4 changes: 3 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@material-ui/core": "4.8.0",
"@material-ui/icons": "4.5.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
Expand All @@ -20,6 +21,7 @@
"eslintConfig": {
"extends": "react-app"
},
"proxy": "http://localhost:5000",
"browserslist": {
"production": [
">0.2%",
Expand All @@ -32,4 +34,4 @@
"last 1 safari version"
]
}
}
}
52 changes: 18 additions & 34 deletions client/src/App.css
Original file line number Diff line number Diff line change
@@ -1,46 +1,30 @@
.App {
text-align: center;
body {
margin: 20 40px;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
.job {
width: 80%;
display: flex;
justify-content: space-between;
margin: 20px 0;
padding: 20px 10px;
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
.job:hover {
cursor: pointer;
color: #666666;
}

.App-link {
color: #61dafb;
.detail-logo {
height: 30px;
float: right;
}

.job {
width: 80%;
.flex-align-mid {
display: flex;
justify-content: space-around;
margin: 10px 0;
padding: 10px 0;
align-items: center;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.job-title-location {
margin-left: 20px;
}
21 changes: 15 additions & 6 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@ import './App.css';

import Jobs from './Jobs';

const mockJobs = [
{title: 'SME 1', company: 'Facebook'},
{title: 'SME 1', company: 'Google'},
{title: 'SME 1', company: 'Apple'},
];
const JOB_API_URL = '/api/jobs';

const fetchJobs = async updateCb => {
const res = await fetch(JOB_API_URL);
const json = await res.json();

updateCb(json);
};

function App() {
const [jobList, updateJobs] = React.useState([]);

React.useEffect(() => {
fetchJobs(updateJobs);
}, []);

return (
<div className="App">
<Jobs jobs={mockJobs} />
<Jobs jobs={jobList} />
</div>
);
}
Expand Down
47 changes: 42 additions & 5 deletions client/src/Job.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
import React from 'react';
import Paper from '@material-ui/core/Paper';
import {Typography} from '@material-ui/core';

export default function Job({job}) {
const ONE_DAY_MS = 24 * 3600 * 1000;

// returns a date like Fri Jun 14
function getMDY(ts) {
return ts
.toDateString()
.split(' ')
.slice(0, 3)
.join(' ');
}

// makeDate takes a TS and returns a date like Fri Jun 14
// if it's today or yesterday, it returns that instead
function makeDate(timestamp) {
const date = new Date(timestamp);
const dateStr = getMDY(date);
const todayStr = getMDY(new Date());
const yesterdayStr = getMDY(new Date(Date.now() - ONE_DAY_MS));
if (dateStr === todayStr) {
return 'today';
} else if (dateStr === yesterdayStr) {
return 'yesterday';
} else {
return dateStr;
}
}

export default function Job({job, onClick}) {
return (
<div className="job">
{job.title}
{job.company}
</div>
<Paper onClick={onClick} className="job">
<div className="flex-align-md">
<div className="job-title-location ">
<Typography variant="h6">{job.title}</Typography>
<Typography variant="h5">{job.company}</Typography>
<Typography>{job.location}</Typography>
</div>
</div>
<div className="flex-align-md">
<Typography>{makeDate(job.created_at)}</Typography>
</div>
</Paper>
);
}
49 changes: 49 additions & 0 deletions client/src/JobModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Slide from '@material-ui/core/Slide';

const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});

export default function JobModal({job, open, handleClose}) {
if (!job.title) {
return <div />;
}
return (
<div>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle id="alert-dialog-slide-title">
{job.title} - {job.company}
<img className="detail-logo" src={job.company_logo} alt="" />
</DialogTitle>
<DialogContent>
<DialogContentText
id="alert-dialog-slide-description"
dangerouslySetInnerHTML={{__html: job.description}}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Close
</Button>
<a href={job.url} target="_blank" rel="noopener noreferrer">
<Button color="primary">Apply</Button>
</a>
</DialogActions>
</Dialog>
</div>
);
}
86 changes: 83 additions & 3 deletions client/src/Jobs.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,95 @@
import React from 'react';
import {Typography} from '@material-ui/core';
import MobileStepper from '@material-ui/core/MobileStepper';
import Button from '@material-ui/core/Button';
import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';

import Job from './Job';
import JobModal from './JobModal';

export default function Jobs({jobs}) {
// modal
const [open, setOpen] = React.useState(false);
const [selectedJob, selectJob] = React.useState([]);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};

// pagination
const numJobs = jobs.length;
const numPages = Math.ceil(numJobs / 50);
const [activeStep, setActiveStep] = React.useState(0);
const jobsOnPage = jobs.slice(activeStep * 50, activeStep * 50 + 50);

// step == 0, show 0-49
// step == 1, show 50-99

function scrollToTop() {
const c = document.documentElement.scrollTop || document.body.scrollTop;
if (c > 0) {
window.requestAnimationFrame(scrollToTop);
window.scrollTo(0, c - c / 8);
}
}

const handleNext = () => {
setActiveStep(prevActiveStep => prevActiveStep + 1);
scrollToTop();
};

const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
scrollToTop();
};

return (
<div className="jobs">
<Typography variant="h1">Entry Level Software Jobs</Typography>
{jobs.map(job => (
<Job job={job} />
<JobModal open={open} job={selectedJob} handleClose={handleClose} />
<Typography variant="h4" component="h1">
Entry Level Software Jobs
</Typography>
<Typography variant="h6" component="h2">
Found {numJobs} Jobs
</Typography>
{jobsOnPage.map((job, i) => (
<Job
key={i}
job={job}
onClick={() => {
handleClickOpen();
selectJob(job);
}}
/>
))}
<div>
Page {activeStep + 1} of {numPages}
</div>
<MobileStepper
variant="progress"
steps={numPages}
position="static"
activeStep={activeStep}
nextButton={
<Button
size="small"
onClick={handleNext}
disabled={activeStep === numPages - 1}
>
Next
<KeyboardArrowRight />
</Button>
}
backButton={
<Button size="small" onClick={handleBack} disabled={activeStep === 0}>
<KeyboardArrowLeft />
Back
</Button>
}
/>
</div>
);
}
9 changes: 8 additions & 1 deletion client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,13 @@
react-is "^16.8.0"
react-transition-group "^4.3.0"

"@material-ui/[email protected]":
version "4.5.1"
resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.5.1.tgz#6963bad139e938702ece85ca43067688018f04f8"
integrity sha512-YZ/BgJbXX4a0gOuKWb30mBaHaoXRqPanlePam83JQPZ/y4kl+3aW0Wv9tlR70hB5EGAkEJGW5m4ktJwMgxQAeA==
dependencies:
"@babel/runtime" "^7.4.4"

"@material-ui/styles@^4.7.1":
version "4.7.1"
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.7.1.tgz#48fa70f06441c35e301a9c4b6c825526a97b7a29"
Expand Down Expand Up @@ -9204,7 +9211,7 @@ serialize-javascript@^1.7.0:
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb"
integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==

serialize-javascript@^2.1.1:
serialize-javascript@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
Expand Down
2 changes: 0 additions & 2 deletions worker/tasks/fetch-github.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,4 @@ const fetchGithub = async () => {
console.log(successs);
};

fetchGithub();

module.exports = fetchGithub;

0 comments on commit 7a221a8

Please sign in to comment.