Skip to content

Commit

Permalink
update UX for offer form (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
januschung authored Dec 16, 2024
1 parent 3485f7e commit 99b9bce
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 86 deletions.
6 changes: 6 additions & 0 deletions src/components/forms/InterviewsForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ export default function InterviewsForm({ jobApplicationId }) {
showSnackbar('Error saving interview', 'error');
}
};

const handleDelete = (id) => {
setInterviewToDelete(id);
setConfirmDeleteOpen(true);
}

const confirmDeleteInterview = async () => {
try {
Expand All @@ -114,6 +119,7 @@ export default function InterviewsForm({ jobApplicationId }) {
showSnackbar('Error deleting interview', 'error');
} finally {
setInterviewToDelete(null);
setConfirmDeleteOpen(false);
}
};

Expand Down
203 changes: 117 additions & 86 deletions src/components/forms/OfferForm.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import React, { useEffect, useState } from 'react';
import { useMutation, useQuery } from '@apollo/client';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableRow from '@mui/material/TableRow';
import TextField from '@mui/material/TextField';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import Button from '@mui/material/Button';
import CancelIcon from '@mui/icons-material/Cancel';
import SaveIcon from '@mui/icons-material/Save';
import IconButton from '@mui/material/IconButton';
import DeleteIcon from '@mui/icons-material/Delete';
import ConfirmDialog from '../common/ConfirmDialog';
import SnackbarComponent from '../common/SnackbarComponent';
import useSnackbar from '../hooks/useSnackbar';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { ADD_OFFER, UPDATE_OFFER, DELETE_OFFER } from '../../graphql/mutation';
import { GET_OFFER } from '../../graphql/query';
import dayjs from 'dayjs';
import { Grid } from '@mui/material';

export default function OfferForm({ jobApplicationId, handleClose }) {
const [offerData, setOfferData] = useState({
Expand All @@ -27,6 +25,9 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
});
const [errors, setErrors] = useState({});
const [isFormVisible, setIsFormVisible] = useState(false);
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);

const { snackbarOpen, snackbarMessage, snackbarSeverity, showSnackbar, handleSnackbarClose } = useSnackbar();

const { data, loading: queryLoading, error } = useQuery(GET_OFFER, {
variables: { jobApplicationId },
Expand All @@ -41,7 +42,7 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
refetchQueries: [{ query: GET_OFFER, variables: { jobApplicationId } }],
});

const [deleteOffer] = useMutation(DELETE_OFFER, {
const [deleteOffer, { loading: deleteOfferLoading }] = useMutation(DELETE_OFFER, {
refetchQueries: [{ query: GET_OFFER, variables: { jobApplicationId } }],
});

Expand Down Expand Up @@ -69,7 +70,6 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};


const handleOfferChange = (e) => {
const { name, value } = e.target;
Expand All @@ -91,6 +91,7 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
offerDate: offerData.offerDate,
},
});
showSnackbar('Offer updated successfully!', 'success');
} else {
await addOffer({
variables: {
Expand All @@ -100,62 +101,71 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
offerDate: offerData.offerDate,
},
});
showSnackbar('Offer added successfully!', 'success');
}
handleClose();
setTimeout(() => handleClose(), 500);
} catch (err) {
console.error('Error saving offer:', err);
showSnackbar('Error saving offer. Please try again.', 'error');
}
};

const handleDelete = async () => {
const handleDelete = () => {
if (data && data.offerByJobApplicationId) {
// If an existing offer is saved, show the confirmation dialog
setConfirmDeleteOpen(true);
} else {
// If no saved offer exists, reset the form and hide it
resetForm();
}
};

const resetForm = () => {
setOfferData({
salaryOffered: '',
description: '',
offerDate: dayjs().format('YYYY-MM-DD'),
});
setErrors({});
setIsFormVisible(false);
};

const confirmDeleteOffer = async () => {
try {
if (data && data.offerByJobApplicationId) {
const { id } = data.offerByJobApplicationId;
await deleteOffer({
variables: { id },
});
setOfferData({
salaryOffered: '',
description: '',
offerDate: dayjs().format('YYYY-MM-DD'),
});
setIsFormVisible(false);
} else {
console.warn('No offer to delete.');
await deleteOffer({ variables: { id } });
resetForm();
showSnackbar('Offer deleted successfully!', 'success');
}
} catch (err) {
console.error('Error deleting offer:', err);
console.error('Error deleting offer:', err.message);
showSnackbar('Error deleting offer. Please try again.', 'error');
} finally {
setConfirmDeleteOpen(false);
}
};

const handleAddOfferClick = () => {
setIsFormVisible(true);
const cancelDeleteOffer = () => {
setConfirmDeleteOpen(false);
};

const isLoading = queryLoading || addOfferLoading || updateOfferLoading;
const isLoading = queryLoading || addOfferLoading || updateOfferLoading || deleteOfferLoading;

return (
<>
<DialogContent>
<Table>
<TableBody>
{!isFormVisible && (
<TableRow>
<TableCell colSpan={4} align="center">
<Button
variant="contained"
color="primary"
onClick={handleAddOfferClick}
>
Add Offer
</Button>
</TableCell>
</TableRow>
)}
{isFormVisible && (
<TableRow>
<TableCell>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Grid container spacing={2}>
{!isFormVisible && (
<Grid container justifyContent="center" sx={{ marginTop: 3 }}>
<Button variant="contained" onClick={() => setIsFormVisible(true)}>
Add Offer
</Button>
</Grid>
)}
{isFormVisible && (
<>
<Grid item xs={12} sm={6}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
id="offerDate"
label="Offer Date"
Expand All @@ -167,51 +177,57 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
}
renderInput={(params) => (
<TextField
sx={{ maxWidth: 150 }}
sx={{ width: '100%' }}
{...params}
error={!!errors.offerDate}
helperText={errors.offerDate}
/>
)}
/>
</LocalizationProvider>
</TableCell>
<TableCell>
<TextField
required
id="salaryOffered"
name="salaryOffered"
label="Salary Offered"
value={offerData.salaryOffered}
onChange={handleOfferChange}
variant="outlined"
error={!!errors.salaryOffered}
helperText={errors.salaryOffered}
/>
</TableCell>
<TableCell>
<TextField
required
id="description"
name="description"
label="Notes"
value={offerData.description}
onChange={handleOfferChange}
variant="outlined"
multiline
error={!!errors.description}
helperText={errors.description}
/>
</TableCell>
<TableCell>
<IconButton color="error" onClick={handleDelete}>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</LocalizationProvider>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
required
id="salaryOffered"
name="salaryOffered"
label="Salary Offered"
value={offerData.salaryOffered}
onChange={handleOfferChange}
variant="outlined"
error={!!errors.salaryOffered}
helperText={errors.salaryOffered}
fullWidth
/>
</Grid>
<Grid item xs={12} sm={10}>
<TextField
required
id="description"
name="description"
label="Notes"
value={offerData.description}
onChange={handleOfferChange}
variant="outlined"
multiline
error={!!errors.description}
helperText={errors.description}
fullWidth
/>
</Grid>
<Grid item xs={12} sm={2} container justifyContent="flex-end" alignItems="center">
<Button
variant="outlined"
color="warning"
onClick={handleDelete}
disabled={isLoading}
>
Delete
</Button>
</Grid>
</>
)}
</Grid>
</DialogContent>
<DialogActions>
<Button
Expand All @@ -228,11 +244,26 @@ export default function OfferForm({ jobApplicationId, handleClose }) {
variant="contained"
startIcon={<SaveIcon />}
onClick={handleSave}
disabled={isLoading}
disabled={isLoading || !isFormVisible}
>
{isLoading ? 'Saving...' : 'Save'}
Save
</Button>
</DialogActions>
<ConfirmDialog
open={confirmDeleteOpen}
onCancel={cancelDeleteOffer}
onConfirm={confirmDeleteOffer}
confirmText="Delete"
confirmColor="error"
title="Confirm Deletion"
content="Are you sure you want to delete this offer? This action cannot be undone."
/>
<SnackbarComponent
open={snackbarOpen}
message={snackbarMessage}
severity={snackbarSeverity}
onClose={handleSnackbarClose}
/>
</>
);
}

0 comments on commit 99b9bce

Please sign in to comment.