Skip to content

Commit

Permalink
refactor: handle errors and display success message
Browse files Browse the repository at this point in the history
  • Loading branch information
Summer-luna committed Jul 28, 2023
1 parent aa7b877 commit 04a2544
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 78 deletions.
8 changes: 1 addition & 7 deletions packages/client/src/graphql/employee/employee.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,6 @@ query getEmployeeById($id: String!) {
name
email
status
projects {
id
name
description
status
isBillable
}
}
}

Expand All @@ -87,6 +80,7 @@ mutation EmployeeCreateInput($newEmployee: EmployeeCreateInput!) {
id
email
name
status
}
}

Expand Down
25 changes: 6 additions & 19 deletions packages/client/src/graphql/employee/employee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,7 @@ export type GetEmployeeByIdQueryVariables = Types.Exact<{
id: Types.Scalars['String'];
}>;

export type GetEmployeeByIdQuery = {
__typename?: 'Query';
employee: {
__typename?: 'EmployeeModel';
id: string;
name: string;
email: string;
status?: string | null;
projects: Array<{ __typename?: 'ProjectModel'; id: string; name: string; description: string; status: string; isBillable: boolean }>;
};
};
export type GetEmployeeByIdQuery = { __typename?: 'Query'; employee: { __typename?: 'EmployeeModel'; id: string; name: string; email: string; status?: string | null } };

export type GetRecordWithFavoriteProjectQueryVariables = Types.Exact<{
id: Types.Scalars['String'];
Expand All @@ -108,7 +98,10 @@ export type EmployeeCreateInputMutationVariables = Types.Exact<{
newEmployee: Types.EmployeeCreateInput;
}>;

export type EmployeeCreateInputMutation = { __typename?: 'Mutation'; addEmployee: { __typename?: 'EmployeeModel'; id: string; email: string; name: string } };
export type EmployeeCreateInputMutation = {
__typename?: 'Mutation';
addEmployee: { __typename?: 'EmployeeModel'; id: string; email: string; name: string; status?: string | null };
};

export type EmployeeUpdateInputMutationVariables = Types.Exact<{
updateEmployee: Types.EmployeeUpdateInput;
Expand Down Expand Up @@ -288,13 +281,6 @@ export const GetEmployeeByIdDocument = gql`
name
email
status
projects {
id
name
description
status
isBillable
}
}
}
`;
Expand Down Expand Up @@ -379,6 +365,7 @@ export const EmployeeCreateInputDocument = gql`
id
email
name
status
}
}
`;
Expand Down
49 changes: 19 additions & 30 deletions packages/client/src/pages/Employee/components/form/EmployeeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ import * as Yup from 'yup';
import { Form, Formik } from 'formik';
import { Box, MenuItem, Typography } from '@mui/material';
import SendIcon from '@mui/icons-material/Send';
import {
GetEmployeeByIdDocument,
GetEmployeeListDocument,
useEmployeeCreateInputMutation,
useEmployeeUpdateInputMutation,
useGetEmployeeByIdLazyQuery
} from '@graphql/employee/employee';
import { GetEmployeeListDocument, useEmployeeCreateInputMutation, useEmployeeUpdateInputMutation, useGetEmployeeByIdLazyQuery } from '@graphql/employee/employee';
import { ObserverTextInput } from '@components/form/ObserverTextInput';
import { useParams } from 'react-router-dom';
import { FC, useEffect, useState } from 'react';
import { DefaultContainedButton } from '@components/StyledComponent';
import { useSnackBar } from '@context/snackbar.context';

const FormValidation = Yup.object({
name: Yup.string().required('Required'),
Expand All @@ -28,18 +23,17 @@ export const EmployeeForm: FC<EmployeeFormProps> = ({ handleClose }) => {
const { id } = useParams();
const [updateEmployee] = useEmployeeUpdateInputMutation();
const [addEmployee] = useEmployeeCreateInputMutation();

const [getEmployeeById] = useGetEmployeeByIdLazyQuery();
const { toggleSnackBar } = useSnackBar();

useEffect(() => {
id &&
getEmployeeById({
variables: {
id: id as string
},
nextFetchPolicy: 'cache-and-network'
id: id
}
}).then((res) => {
if (res && res.data) {
if (res.data) {
const { name, email, status } = res.data.employee;
setInitialValue({
name,
Expand All @@ -48,7 +42,7 @@ export const EmployeeForm: FC<EmployeeFormProps> = ({ handleClose }) => {
});
}
});
}, [id, open]);
}, [id]);

return (
<>
Expand All @@ -62,36 +56,31 @@ export const EmployeeForm: FC<EmployeeFormProps> = ({ handleClose }) => {
// if no id, create employee
if (!id) {
// after submitting the new employee re-fetch the employees via graphql
await addEmployee({
const res = await addEmployee({
variables: {
newEmployee: { ...values, status: values.status.toString() }
newEmployee: { ...values }
},
refetchQueries: [{ query: GetEmployeeListDocument }]
});

res.data?.addEmployee && toggleSnackBar('Successfully created a new employee!', { variant: 'success' });
} else {
// after updating the employee, re-fetch the employees via graphql
await updateEmployee({
const res = await updateEmployee({
variables: {
updateEmployee: { ...values, status: values.status.toString(), id: id }
},
refetchQueries: [
{
query: GetEmployeeByIdDocument,
variables: {
id: id as string
}
}
]
updateEmployee: { ...values, id: id }
}
});

res.data?.updateEmployee && toggleSnackBar('Successfully update an employee!', { variant: 'success' });
}
return handleClose();
}}
>
<Form>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '1rem', marginTop: '2rem' }}>
<ObserverTextInput id="name" type="text" name="name" label="Name" placeholder="Name" required />
<ObserverTextInput id="email" type="email" name="email" label="Email" placeholder="Email" required />
<ObserverTextInput name="status" select label="Status" placeholder="Status">
<ObserverTextInput id="name" type="text" name="name" label="Name" placeholder="Name" />
<ObserverTextInput id="email" type="email" name="email" label="Email" placeholder="Email" />
<ObserverTextInput id="status" select name="status" label="Status" placeholder="Status">
<MenuItem value="Inactive">Inactive</MenuItem>
<MenuItem value="Active">Active</MenuItem>
</ObserverTextInput>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,15 @@ const dropdownData = [
];

export const EmployeeTable: FC<EmployeeTableProps> = ({ data }) => {
const [open, setOpen] = useState({
add: false,
edit: false
});
const [open, setOpen] = useState(false);
const [searchText, setSearchText] = useState<string>('');
const [filter, setFilter] = useState<string>('Active');
const navigate = useNavigate();

const handleClickOpen = (type: string) => {
setOpen((prevState) => ({ ...prevState, [type]: true }));
};
const handleClickOpen = () => setOpen(true);

const handleOnClose = (type: string) => {
setOpen((prevState) => ({ ...prevState, [type]: false }));
const handleOnClose = () => {
setOpen(false);
navigate(Paths.EMPLOYEE_lIST);
};

Expand Down Expand Up @@ -88,7 +83,7 @@ export const EmployeeTable: FC<EmployeeTableProps> = ({ data }) => {
variant="outlined"
onClick={() => {
navigate(`${Paths.EMPLOYEE_lIST}/${row.id}`);
handleClickOpen('edit');
handleClickOpen();
}}
color="secondary"
>
Expand All @@ -100,19 +95,16 @@ export const EmployeeTable: FC<EmployeeTableProps> = ({ data }) => {

const ToolBar = (
<>
<Stack direction="row" width="100%" justifyContent="space-between" marginBottom={5}>
<Stack direction="row" justifyContent="space-between" mb={5}>
<Typography variant="h6">All Employees</Typography>
<Button
variant="contained"
startIcon={<AddIcon />}
sx={{ borderRadius: '8px', backgroundColor: 'grey.800', '&:hover': { backgroundColor: 'grey.700' } }}
onClick={() => handleClickOpen('add')}
onClick={handleClickOpen}
>
New Employee
</Button>
<FormDialog open={open.add} onClose={() => handleOnClose('add')}>
<EmployeeForm handleClose={() => handleOnClose('add')} />
</FormDialog>
</Stack>
<Stack direction="row" gap={2} mb={3}>
<DropDownMenu data={dropdownData} onChange={handleDropdownOnChange} label="Status" name="employee-status-dropdown" id="employee-status-dropdown" value={filter} />
Expand All @@ -137,20 +129,17 @@ export const EmployeeTable: FC<EmployeeTableProps> = ({ data }) => {
}
}}
/>
<FormDialog open={open.edit} onClose={() => handleOnClose('edit')}>
<EmployeeForm handleClose={() => handleOnClose('edit')} />
</FormDialog>
{data.length === 0 && (
<Box>
<Button sx={{ width: '100%', height: '200px', fontSize: '1.2rem' }} onClick={() => handleClickOpen('add')}>
<Button sx={{ width: '100%', height: '200px', fontSize: '1.2rem' }} onClick={handleClickOpen}>
Add Your First Employee
</Button>
<FormDialog open={open.add} onClose={() => handleOnClose('add')}>
<EmployeeForm handleClose={() => handleOnClose('add')} />
</FormDialog>
</Box>
)}
</StyledPaper>
<FormDialog open={open} onClose={handleOnClose}>
<EmployeeForm handleClose={handleOnClose} />
</FormDialog>
</Box>
);
};
24 changes: 24 additions & 0 deletions packages/server/src/employees/employees.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,36 @@ export class EmployeesService {
return this.prisma.employee.findMany();
}

/**
* Check if an employee exists. If the employeeId is not provided, will
* return false.
*
* @param employeeId The employee id to check
* @returns True if the employee exists, false otherwise
*/
async exists(employeeId: string | undefined): Promise<boolean> {
// If the employee ID is not provided, return false
if (!employeeId) return false;

// Return true if there is an employee with the provided ID
const employeeCount = await this.prisma.employee.count({
where: {
id: employeeId
}
});
return employeeCount > 0;
}

/**
* Get an employee by ID
*
* @return a matched employee
*/
async getEmployeeById(id: string): Promise<Employee> {
const employeeExist = await this.exists(id);

if (!employeeExist) throw new Error('Employee not found');

return this.prisma.employee.findUnique({
where: {
id: id
Expand Down

0 comments on commit 04a2544

Please sign in to comment.