diff --git a/api/index.js b/api/index.js
index d84d94a..fdf3bc2 100644
--- a/api/index.js
+++ b/api/index.js
@@ -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);
});
diff --git a/client/package.json b/client/package.json
index c93adbf..8e7f12c 100644
--- a/client/package.json
+++ b/client/package.json
@@ -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",
@@ -20,6 +21,7 @@
"eslintConfig": {
"extends": "react-app"
},
+ "proxy": "http://localhost:5000",
"browserslist": {
"production": [
">0.2%",
@@ -32,4 +34,4 @@
"last 1 safari version"
]
}
-}
+}
\ No newline at end of file
diff --git a/client/src/App.css b/client/src/App.css
index 6cfd7e5..2be5e9f 100644
--- a/client/src/App.css
+++ b/client/src/App.css
@@ -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;
}
diff --git a/client/src/App.js b/client/src/App.js
index 41372e1..4f30d81 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -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 (
-
+
);
}
diff --git a/client/src/Job.js b/client/src/Job.js
index a63fdb3..2a68cb0 100644
--- a/client/src/Job.js
+++ b/client/src/Job.js
@@ -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 (
-
- {job.title}
- {job.company}
-
+
+
+
+ {job.title}
+ {job.company}
+ {job.location}
+
+
+
+ {makeDate(job.created_at)}
+
+
);
}
diff --git a/client/src/JobModal.js b/client/src/JobModal.js
new file mode 100644
index 0000000..abba91d
--- /dev/null
+++ b/client/src/JobModal.js
@@ -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 ;
+});
+
+export default function JobModal({job, open, handleClose}) {
+ if (!job.title) {
+ return ;
+ }
+ return (
+
+
+
+ );
+}
diff --git a/client/src/Jobs.js b/client/src/Jobs.js
index b08e6df..8afdea9 100644
--- a/client/src/Jobs.js
+++ b/client/src/Jobs.js
@@ -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 (
-
Entry Level Software Jobs
- {jobs.map(job => (
-
+
+
+ Entry Level Software Jobs
+
+
+ Found {numJobs} Jobs
+
+ {jobsOnPage.map((job, i) => (
+
{
+ handleClickOpen();
+ selectJob(job);
+ }}
+ />
))}
+
+ Page {activeStep + 1} of {numPages}
+
+
+ Next
+
+
+ }
+ backButton={
+
+ }
+ />
);
}
diff --git a/client/yarn.lock b/client/yarn.lock
index 8a6827b..da80d82 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -1091,6 +1091,13 @@
react-is "^16.8.0"
react-transition-group "^4.3.0"
+"@material-ui/icons@4.5.1":
+ 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"
@@ -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==
diff --git a/worker/tasks/fetch-github.js b/worker/tasks/fetch-github.js
index 191eeda..39a4b2e 100644
--- a/worker/tasks/fetch-github.js
+++ b/worker/tasks/fetch-github.js
@@ -43,6 +43,4 @@ const fetchGithub = async () => {
console.log(successs);
};
-fetchGithub();
-
module.exports = fetchGithub;