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

offer list #53

Merged
merged 1 commit into from
Dec 19, 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
41 changes: 27 additions & 14 deletions src/components/AppHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,31 @@ import { useQuery } from '@apollo/client';
import { GET_PROFILE, GET_ALL_OFFERS, GET_ALL_INTERVIEWS } from '../graphql/query';
import SearchBar from './SearchBar';
import JobApplicationDialog from './JobApplicationDialog';
import OfferListDialog from './OfferListDialog';
import useJobApplicationDialog from './hooks/useJobApplicationDialog';
import useOfferListDialog from './hooks/useOfferList';
import ProfileDialog from './ProfileDialog';
import JobApplicationList from './JobApplicationList';
import MobileMenu from './MobileMenu';
import dayjs from 'dayjs';

export default function AppHeader() {
const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = useState(null);
const [jobApplicationOpen, setJobApplicationOpen] = useState(false);
const [profileOpen, setProfileOpen] = useState(false);
const [profile, setProfile] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const { data: offersData, loading: offersLoading, error: offersError, refetch } = useQuery(GET_ALL_OFFERS, {
fetchPolicy: 'network-only',
});
const { open, handleOpen, handleClose } = useJobApplicationDialog(refetch);

const {
offerListingDialogOpen,
handleOfferListDialogOpen,
handleOfferListDialogClose,
} = useOfferListDialog(refetch);


const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);

const id = 1;
Expand All @@ -34,10 +48,6 @@ export default function AppHeader() {
variables: { id },
});

const { data: offersData, loading: offersLoading, error: offersError } = useQuery(GET_ALL_OFFERS, {
fetchPolicy: 'network-only',
});

const { data: interviewsData, loading: interviewsLoading, error: interviewsError } = useQuery(GET_ALL_INTERVIEWS, {
fetchPolicy: 'network-only',
});
Expand Down Expand Up @@ -66,8 +76,6 @@ export default function AppHeader() {

const handleProfileClose = () => setProfileOpen(false);
const handleMobileMenuClose = () => setMobileMoreAnchorEl(null);
const handleJobApplicationOpen = () => setJobApplicationOpen(true);
const handleJobApplicationClose = () => setJobApplicationOpen(false);

const handleSearch = (text) => {
setSearchTerm(text);
Expand All @@ -77,9 +85,9 @@ export default function AppHeader() {
<Box sx={{ flexGrow: 1 }}>
<JobApplicationDialog
jobApplication={{ status: 'open' }}
open={jobApplicationOpen}
handleClose={handleJobApplicationClose}
setOpen={setJobApplicationOpen}
open={open}
handleClose={handleClose}
setOpen={handleOpen}
isNew
/>
<ProfileDialog
Expand All @@ -88,6 +96,11 @@ export default function AppHeader() {
handleClose={handleProfileClose}
setOpen={setProfileOpen}
/>
<OfferListDialog
open={offerListingDialogOpen}
handleClose={handleOfferListDialogClose}
setOpen={handleOfferListDialogOpen}
/>
<AppBar position="static">
<Toolbar>
<EmojiEventsIcon sx={{ color: '#FFD700', mr: 2 }} />
Expand All @@ -111,15 +124,15 @@ export default function AppHeader() {
<SearchBar onSearch={handleSearch} />
<Box sx={{ flexGrow: 1 }} />
<Box sx={{ display: { xs: 'none', md: 'flex' } }}>
<IconButton size="large" aria-label="New" color="inherit" onClick={handleJobApplicationOpen}>
<IconButton size="large" aria-label="New" color="inherit" onClick={handleOpen}>
<AddCircleIcon />
</IconButton>
<IconButton size="large" color="inherit">
<IconButton size="large" color="inherit" onClick={() => refetch()}>
<Badge badgeContent={interviewsLoading ? '...' : interviewCount} color="error">
<EventAvailableIcon />
</Badge>
</IconButton>
<IconButton size="large" color="inherit">
<IconButton size="large" color="inherit" onClick={handleOfferListDialogOpen}>
<Badge badgeContent={offersLoading ? '...' : offerCount} color="error">
<NotificationsIcon />
</Badge>
Expand Down Expand Up @@ -154,7 +167,7 @@ export default function AppHeader() {
isMobileMenuOpen={isMobileMenuOpen}
handleMobileMenuClose={handleMobileMenuClose}
handleProfileMenuOpen={handleProfileMenuOpen}
handleJobApplicationOpen={handleJobApplicationOpen}
handleJobApplicationOpen={handleOpen}
interviewCount={interviewCount}
offerCount={offerCount}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/components/JobApplicationDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ export default function JobApplicationDialog({ jobApplication, handleClose, open
)}
{activeTab === 1 && (
<InterviewsForm
jobApplicationId={parseInt(jobApplication.id)}
jobApplicationId={parseInt(jobApplication.id || 0)}
/>
)}

{activeTab === 2 && (
<OfferForm
jobApplicationId={parseInt(jobApplication.id)}
jobApplicationId={parseInt(jobApplication?.id || 0)}
handleClose={handleClose}
/>
)}
Expand Down
17 changes: 9 additions & 8 deletions src/components/JobApplicationList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@ import SnackbarComponent from './common/SnackbarComponent';
import useJobApplications from './hooks/useJobApplications';
import useSnackbar from './hooks/useSnackbar';
import useConfirmDialog from './hooks/useConfirmDialog';
import useJobApplicationDialog from './hooks/useJobApplicationDialog';
import { DELETE_JOB_APPLICATION } from '../graphql/mutation';
import { GET_JOB_APPLICATIONS } from '../graphql/query';

export default function JobApplicationList({ searchTerm }) {
const [open, setOpen] = useState(false);
const [jobApplication, setJobApplication] = useState(null);
// const [open, setOpen] = useState(false);
// const [jobApplication, setJobApplication] = useState(null);
const [jobApplicationToDelete, setJobApplicationToDelete] = useState(null);
const [localData, setLocalData] = useState([]);

const { data, loading, error } = useJobApplications();
const { snackbarOpen, snackbarMessage, showSnackbar, handleSnackbarClose } = useSnackbar();
const { confirmOpen, openConfirmDialog, cancel } = useConfirmDialog();

const { open, jobApplication, handleOpen, handleClose } = useJobApplicationDialog();

const [deleteJobApplication] = useMutation(DELETE_JOB_APPLICATION, {
refetchQueries: [{ query: GET_JOB_APPLICATIONS }],
Expand All @@ -48,12 +49,12 @@ export default function JobApplicationList({ searchTerm }) {
}
}, [data]);

const handleOpen = (jobApplication) => {
setJobApplication(jobApplication);
setOpen(true);
};
// const handleOpen = (jobApplication) => {
// setJobApplication(jobApplication);
// setOpen(true);
// };

const handleClose = () => setOpen(false);
// const handleClose = () => setOpen(false);

const confirmDeleteJobApplication = () => {
if (jobApplicationToDelete) {
Expand Down
139 changes: 139 additions & 0 deletions src/components/OfferListDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, { useState, useEffect } from 'react';
import { useQuery } from '@apollo/client';
import CancelIcon from '@mui/icons-material/Cancel';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Typography from '@mui/material/Typography';
import Paper from '@mui/material/Paper';
import { GET_ALL_OFFERS } from '../graphql/query';
import useJobApplicationDialog from './hooks/useJobApplicationDialog';
import JobApplicationDialog from './JobApplicationDialog';
import DialogTitleBar from './DialogTitleBar';

export default function OfferListDialog({ handleClose, open }) {
const [offers, setOffers] = useState([]);
const { loading, error, data, refetch } = useQuery(GET_ALL_OFFERS, {
fetchPolicy: 'network-only',
onCompleted: (data) => setOffers(data.allOffer),
});
const {
open: jobDialogOpen,
jobApplication,
handleOpen: handleJobDialogOpen,
handleClose: handleJobDialogClose
} = useJobApplicationDialog(() => {
refetch().then(({ data }) => setOffers(data.allOffer));
});

useEffect(() => {
if (open) {
refetch()
.then(({ data }) => {
if (data) setOffers(data.allOffer);
})
.catch((err) => console.error('Refetch error:', err));
}
}, [open, refetch]);

return (
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="modal-title"
aria-describedby="modal-description"
fullWidth
maxWidth="md"
slotProps={{
backdrop: {
sx: {
backdropFilter: 'blur(8px)',
},
},
}}
>
<DialogTitleBar title="Offer List" />

<DialogContent dividers>
{loading && (
<Box display="flex" justifyContent="center" alignItems="center" height="200px">
<CircularProgress />
</Box>
)}

{error && (
<Typography variant="body1" color="error" align="center">
Something went wrong. Please try again later.
</Typography>
)}

{!loading && !error && offers.length === 0 && (
<Box textAlign="center" p={4}>
<Typography variant="h6" color="textSecondary">
No offers available yet!
</Typography>
<Button color="primary" variant="contained" onClick={handleClose}>
Add an Offer
</Button>
</Box>
)}

{!loading && !error && offers.length > 0 && (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell align="left">Company</TableCell>
<TableCell align="left">Offer Date</TableCell>
<TableCell align="left">Salary Offered</TableCell>
<TableCell align="left">Note</TableCell>
<TableCell align="center">Job Application</TableCell>
</TableRow>
</TableHead>
<TableBody>
{offers.map((offer, index) => (
<TableRow key={index}>
<TableCell align="left">{offer.jobApplication.companyName}</TableCell>
<TableCell align="left">{offer.offerDate}</TableCell>
<TableCell align="left">{offer.salaryOffered}</TableCell>
<TableCell align="left">{offer.description}</TableCell>
<TableCell align="center">
<Button
size="small"
color="info"
variant="outlined"
onClick={() => handleJobDialogOpen(offer.jobApplication)}
>
Job Details
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</DialogContent>

<DialogActions>
<Button color="info" variant="outlined" startIcon={<CancelIcon />} onClick={handleClose}>
Cancel
</Button>
</DialogActions>
<JobApplicationDialog
jobApplication={jobApplication}
open={jobDialogOpen}
handleClose={handleJobDialogClose}
/>
</Dialog>
);
}
4 changes: 2 additions & 2 deletions src/components/forms/ApplicationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ADD_JOB_APPLICATION, UPDATE_JOB_APPLICATION } from '../../graphql/mutat
import { GET_JOB_APPLICATIONS } from '../../graphql/query';
import dayjs from 'dayjs';

export default function ApplicationForm({ jobApplication, isNew, setOpen, handleClose }) {
export default function ApplicationForm({ jobApplication, isNew, handleClose }) {
const [formData, setFormData] = useState({
companyName: '',
jobTitle: '',
Expand Down Expand Up @@ -70,7 +70,7 @@ export default function ApplicationForm({ jobApplication, isNew, setOpen, handle
...(jobApplication && { id: jobApplication.id }),
};
mutationFn({ variables });
setOpen(false);
handleClose()
};

return (
Expand Down
10 changes: 8 additions & 2 deletions src/components/forms/OfferForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
});

const [addOffer, { loading: addOfferLoading }] = useMutation(ADD_OFFER, {
refetchQueries: [{ query: GET_OFFER, variables: { jobApplicationId } }],
refetchQueries: [
{ query: GET_OFFER, variables: { jobApplicationId } },
{ query: GET_ALL_OFFERS },
],
});

const [updateOffer, { loading: updateOfferLoading }] = useMutation(UPDATE_OFFER, {
refetchQueries: [{ query: GET_OFFER, variables: { jobApplicationId } }],
refetchQueries: [
{ query: GET_OFFER, variables: { jobApplicationId } },
{ query: GET_ALL_OFFERS },
],
});

const [deleteOffer, { loading: deleteOfferLoading }] = useMutation(DELETE_OFFER, {
Expand Down
31 changes: 31 additions & 0 deletions src/components/hooks/useJobApplicationDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useState } from 'react';

const useJobApplicationDialog = (refetch) => {
const [open, setOpen] = useState(false);
const [jobApplication, setJobApplication] = useState(null);

const handleOpen = (jobApplication) => {
if (refetch) {
refetch();
}
setJobApplication(jobApplication);
setOpen(true);
};

const handleClose = () => {
if (refetch) {
refetch();
}
setOpen(false);
setJobApplication(null);
};

return {
open,
jobApplication,
handleOpen,
handleClose,
};
};

export default useJobApplicationDialog;
23 changes: 23 additions & 0 deletions src/components/hooks/useOfferList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useState } from 'react';

export default function useOfferListDialog(refetch) {
const [offerListingDialogOpen, setOfferListDialogOpen] = useState(false);
const handleOfferListDialogOpen = () => {
setOfferListDialogOpen(true);
if (refetch) {
refetch();
}
}
const handleOfferListDialogClose = () => {
setOfferListDialogOpen(false);
if (refetch) {
refetch();
}
}

return {
offerListingDialogOpen,
handleOfferListDialogOpen,
handleOfferListDialogClose,
};
}
Loading
Loading