Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update UX #57

Merged
merged 3 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/JobApplicationDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export default function JobApplicationDialog({ jobApplication, handleClose, open
<Tab label="Interviews" />
<Tab label="Offer" />
</Tabs>
)}
{isNew && (
<div/>
)}
{activeTab === 0 && (
<ApplicationForm
Expand Down
67 changes: 47 additions & 20 deletions src/components/JobApplicationList.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState, useEffect } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useMutation } from '@apollo/client';
import CalendarMonthRoundedIcon from '@mui/icons-material/CalendarMonthRounded';
import CommentRoundedIcon from '@mui/icons-material/CommentRounded';
import DeleteIcon from '@mui/icons-material/Delete';
import ErrorIcon from '@mui/icons-material/Error';
import LinkRoundedIcon from '@mui/icons-material/LinkRounded';
Expand All @@ -28,6 +27,12 @@ import useJobApplicationDialog from './hooks/useJobApplicationDialog';
import { DELETE_JOB_APPLICATION } from '../graphql/mutation';
import { GET_JOB_APPLICATIONS } from '../graphql/query';

const STATUS_COLORS = {
open: '#66BB6A',
active: '#42A5F5',
rejected: '#EF5350',
};

export default function JobApplicationList({ searchTerm }) {
const [jobApplicationToDelete, setJobApplicationToDelete] = useState(null);
const [localData, setLocalData] = useState([]);
Expand All @@ -41,9 +46,19 @@ export default function JobApplicationList({ searchTerm }) {
refetchQueries: [{ query: GET_JOB_APPLICATIONS }],
});

const sortJobApplications = (jobApplications) => {
const statusOrder = { active: 1, open: 2, rejected: 3 };

return [...jobApplications].sort((a, b) => {
const statusComparison = statusOrder[a.status] - statusOrder[b.status];
return statusComparison !== 0
? statusComparison
: new Date(b.appliedDate) - new Date(a.appliedDate);
});
};
useEffect(() => {
if (data) {
setLocalData(data.allJobApplication);
setLocalData(sortJobApplications(data.allJobApplication));
}
}, [data]);

Expand Down Expand Up @@ -95,14 +110,25 @@ export default function JobApplicationList({ searchTerm }) {
Failed to fetch data. Please check your network connection and try again.
</Alert>
)}
{!loading && !error && (
{!loading && !error && filteredData.length === 0 && (
<Typography variant="body1">No job applications match your search.</Typography>
)}
{!loading && !error && filteredData.length > 0 && (
<Grid container spacing={4}>
{filteredData.map((jobApplication) => (
<Grid item key={jobApplication.id} xs={12} sm={6} md={4}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
borderTop: 4,
borderColor: STATUS_COLORS[jobApplication.status] || 'grey',
}}
>
<CardContent sx={{ flexGrow: 1 }}>
<Typography sx={{ fontSize: 12 }} color="text.secondary" gutterBottom>
<CalendarMonthRoundedIcon fontSize="inherit" />{" "}
<CalendarMonthRoundedIcon fontSize="inherit" />{' '}
{jobApplication.appliedDate}
</Typography>
<Typography variant="h5" component="div">
Expand All @@ -111,15 +137,25 @@ export default function JobApplicationList({ searchTerm }) {
<Typography sx={{ mb: 1.5 }} color="text.secondary">
{jobApplication.jobTitle}
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
<MonetizationOnIcon fontSize="inherit" />
<Typography sx={{ mb: 1.5, display: 'flex', alignItems: 'center' }} color="text.secondary">
<MonetizationOnIcon fontSize="inherit" sx={{ marginRight: 0.5 }} />
{jobApplication.salaryRange}
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
Status: {jobApplication.status}
status: {jobApplication.status}
</Typography>
<Typography sx={{ mb: 1.5, display: 'flex', alignItems: 'center' }} color="text.secondary">
<LinkRoundedIcon fontSize="inherit" sx={{ marginRight: 0.5 }} />
<Link
href={jobApplication.jobUrl || '#'}
underline="hover"
color="inherit"
target="_blank"
rel="noopener noreferrer"
>
job link
</Link>
</Typography>
</CardContent>
<CardContent sx={{ flexGrow: 1 }}>
<Typography
color="text.secondary"
sx={{
Expand All @@ -129,16 +165,8 @@ export default function JobApplicationList({ searchTerm }) {
WebkitLineClamp: 3,
}}
>
<CommentRoundedIcon fontSize="inherit" />
<br />
{jobApplication.description}
</Typography>
<Typography color="text.secondary">
<LinkRoundedIcon fontSize="inherit" />{" "}
<Link href={jobApplication.jobUrl} underline="hover" color="inherit">
job link
</Link>
</Typography>
</CardContent>
<CardActions sx={{ justifyContent: 'flex-end' }}>
<Button
Expand Down Expand Up @@ -179,7 +207,6 @@ export default function JobApplicationList({ searchTerm }) {
severity="success"
onClose={handleSnackbarClose}
/>

</main>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProfileTextField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const ProfileTextField = ({ id, label, value, onChange }) => (
name={id}
label={label}
fullWidth
variant="standard"
variant="outlined"
value={value}
onChange={onChange}
/>
Expand Down
10 changes: 5 additions & 5 deletions src/components/__tests__/JobApplicationDialog.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ it('Should render New Job Application Dialog', async() => {
expect(screen.getByDisplayValue("open")).toBeInTheDocument();
const currentDate = dayjs(new Date()).format('MM/DD/YYYY')
expect(screen.getByDisplayValue(currentDate)).toBeInTheDocument();
expect(screen.getByText("Company Name")).toBeInTheDocument();
expect(screen.getByText("Job Title")).toBeInTheDocument();
expect(screen.getByText("Salary Range")).toBeInTheDocument();
expect(screen.getByText("Description")).toBeInTheDocument();
expect(screen.getByText("Job Link")).toBeInTheDocument();
expect(screen.getByLabelText("Company Name *")).toBeInTheDocument();
expect(screen.getByLabelText("Job Title *")).toBeInTheDocument();
expect(screen.getByLabelText("Salary Range *")).toBeInTheDocument();
expect(screen.getByLabelText("Description")).toBeInTheDocument();
expect(screen.getByLabelText("Job Link")).toBeInTheDocument();

});

Expand Down
13 changes: 13 additions & 0 deletions src/components/__tests__/JobApplicationList.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ describe('JobApplicationList', () => {
});
});

test('filters job applications based on search term', async () => {
render(
<MockedProvider mocks={mocks} addTypename={false}>
<JobApplicationList searchTerm="Backend" />
</MockedProvider>
);

await waitFor(() => {
expect(screen.queryByText('Company A')).not.toBeInTheDocument();
expect(screen.getByText('Company B')).toBeInTheDocument();
});
});

test('deletes a job application', async () => {
render(
<MockedProvider mocks={deleteMocks} addTypename={false}>
Expand Down
Loading
Loading