Compare commits
8 Commits
aa66f262b2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 96e074cc2d | |||
| 680a7c3268 | |||
| fa9788a10f | |||
| 6a9db34a6e | |||
| 1d4e242054 | |||
| e9f51d6ea0 | |||
| b7bbcc50c6 | |||
| fda58bf19a |
+19
-2
@@ -16,6 +16,18 @@ module.exports = merge(common, {
|
||||
filename: 'js/[name].[contenthash].bundle.js',
|
||||
},
|
||||
// Spin up a server for quick development
|
||||
// devServer: {
|
||||
// historyApiFallback: true,
|
||||
// contentBase: path.normalize(paths.build),
|
||||
// index: '/',
|
||||
// open: true,
|
||||
// compress: true,
|
||||
// hot: true,
|
||||
// port: 3000,
|
||||
// host: 'test.manaknightdev.com',
|
||||
// https: true,
|
||||
// noInfo: true, //This turns off information regarding the bundle. Set to false if you need to view the messages
|
||||
// },
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
contentBase: path.normalize(paths.build),
|
||||
@@ -24,9 +36,14 @@ module.exports = merge(common, {
|
||||
compress: true,
|
||||
hot: true,
|
||||
port: 3000,
|
||||
host: 'test.manaknightdev.com',
|
||||
host: '0.0.0.0',
|
||||
https: true,
|
||||
noInfo: true, //This turns off information regarding the bundle. Set to false if you need to view the messages
|
||||
allowedHosts: [
|
||||
'.ngrok-free.app', // allow any ngrok subdomain
|
||||
// 'localhost',
|
||||
'test.manaknightdev.com',
|
||||
],
|
||||
noInfo: true,
|
||||
},
|
||||
|
||||
module: {
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@
|
||||
"prepare-husky": "husky install",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"test": "npx cypress open",
|
||||
"commit": "git add . && git commit -m \"Update Host\" && git push"
|
||||
"commit": "git add . && git commit -m \"Update Host\" && git push",
|
||||
"build": "webpack --config config/webpack.base.js --mode production"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Label } from 'Components/Label';
|
||||
import { TextBox } from 'Components/TextBox';
|
||||
import { CheckBox } from 'Components/CheckBox';
|
||||
import { TextArea } from 'Components/TextArea';
|
||||
import { ContractFormsSuccessToast } from '../ContractFormsSuccessToast';
|
||||
import { ContractFormsToast } from '../ContractFormsToast';
|
||||
|
||||
import classes from './contractFormsModal.module.css';
|
||||
|
||||
@@ -35,6 +35,7 @@ interface Props {
|
||||
submitText: string;
|
||||
showToast: boolean;
|
||||
toastMessage: string;
|
||||
toastType?: string;
|
||||
onFormSubmit: (e: any) => void;
|
||||
onClickClose: (e: any) => void;
|
||||
handleChange: (e: any) => void;
|
||||
@@ -51,6 +52,7 @@ const ContractFormsModal = ({
|
||||
formErrors,
|
||||
showToast,
|
||||
toastMessage,
|
||||
toastType = 'success',
|
||||
onFormSubmit,
|
||||
onClickClose,
|
||||
handleChange,
|
||||
@@ -66,7 +68,7 @@ const ContractFormsModal = ({
|
||||
leftHeaderIcon="projects"
|
||||
modalHeader
|
||||
modalCloseClick={onClickClose}
|
||||
toast={<ContractFormsSuccessToast showToast={showToast} message={toastMessage} />}
|
||||
toast={<ContractFormsToast type={toastType as 'success' | 'error'} showToast={showToast} message={toastMessage} />}
|
||||
>
|
||||
<form className={classes.form} onSubmit={onFormSubmit}>
|
||||
<ValidateBackGround isValid={!formErrors?.name.length}>
|
||||
@@ -139,6 +141,10 @@ const ContractFormsModal = ({
|
||||
</Modal>
|
||||
);
|
||||
|
||||
ContractFormsModal.defaultProps = {
|
||||
toastType: 'success',
|
||||
};
|
||||
|
||||
const ContractFormsModalMemo = memo(ContractFormsModal, areEqual);
|
||||
|
||||
export { ContractFormsModalMemo as ContractFormsModal };
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { areEqual } from 'Utils/equalityChecks';
|
||||
import { CheckedMarkSvg } from 'Components/Icons/CheckedMark';
|
||||
|
||||
import classes from './contractFormsSuccessToast.module.css';
|
||||
|
||||
export interface Props {
|
||||
showToast: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const ContractFormsSuccessToast = ({ showToast = false, message }: Props) => (
|
||||
<div
|
||||
className={`toast fade d-flex align-items-center position-absolute border-0 bottom-0 ${
|
||||
showToast ? 'show' : 'hide'
|
||||
} ${classes.toastBase} ${classes.toastSuccess}`}
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<div className={`toast-body ${classes['toast-body-override']} ${classes.toastText}`}>
|
||||
{message}
|
||||
<span className={`${classes.toastIcon}`}>
|
||||
<CheckedMarkSvg />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
ContractFormsSuccessToast.defaultProps = {};
|
||||
|
||||
const ContractFormsSuccessToastMemo = memo(ContractFormsSuccessToast, areEqual);
|
||||
|
||||
export { ContractFormsSuccessToastMemo as ContractFormsSuccessToast };
|
||||
@@ -1 +0,0 @@
|
||||
export { ContractFormsSuccessToast } from './ContractFormsSuccessToast';
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { areEqual } from 'Utils/equalityChecks';
|
||||
import { CheckedMarkSvg } from 'Components/Icons/CheckedMark';
|
||||
|
||||
import classes from './contractFormsToast.module.css';
|
||||
|
||||
export interface Props {
|
||||
showToast: boolean;
|
||||
message: string;
|
||||
type?: 'success' | 'error';
|
||||
}
|
||||
|
||||
const ContractFormsToast = ({ showToast = false, message, type = 'success' }: Props) => {
|
||||
const getToastClass = () => (type === 'success' ? classes.toastSuccess : classes.toastWarning);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`toast fade d-flex align-items-center position-absolute border-0 bottom-0 ${
|
||||
showToast ? 'show' : 'hide'
|
||||
} ${classes.toastBase} ${getToastClass()}`}
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<div className={`toast-body ${classes['toast-body-override']} ${classes.toastText}`}>
|
||||
{message}
|
||||
{type === 'success' && (
|
||||
<span className={`${classes.toastIcon}`}>
|
||||
<CheckedMarkSvg />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ContractFormsToast.defaultProps = {
|
||||
type: 'success',
|
||||
};
|
||||
|
||||
const ContractFormsToastMemo = memo(ContractFormsToast, areEqual);
|
||||
|
||||
export { ContractFormsToastMemo as ContractFormsToast };
|
||||
+5
-1
@@ -57,7 +57,11 @@
|
||||
|
||||
.toastWarning {
|
||||
background-color: #fff0f0;
|
||||
border-bottom: 1px solid #e82828;
|
||||
border-bottom: 1px solid #e82828 !important;
|
||||
}
|
||||
|
||||
.toastWarning .toastText {
|
||||
color: #d32f2f !important;
|
||||
}
|
||||
|
||||
.toastCloseIcon {
|
||||
@@ -0,0 +1 @@
|
||||
export { ContractFormsToast } from './ContractFormsToast';
|
||||
@@ -10,36 +10,45 @@ export interface Props {
|
||||
isDisplayed: boolean;
|
||||
message: string;
|
||||
closeToast: (e: any) => void;
|
||||
type?: 'success' | 'error';
|
||||
}
|
||||
|
||||
const DeleteToast = ({ isDisplayed = false, message, closeToast }: Props) => (
|
||||
<div
|
||||
className={`toast fade d-flex align-items-center position-fixed border-0 bottom-0 ${
|
||||
isDisplayed ? 'show' : 'hide'
|
||||
} ${classes.toastBase} ${classes.toastSuccess}`}
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<div className={`toast-body ${classes['toast-body-override']} ${classes.toastText}`}>
|
||||
{message}
|
||||
<span className={`${classes.toastIcon}`}>
|
||||
<CheckedMarkSvg />
|
||||
</span>
|
||||
</div>
|
||||
<div className={classes.toastCloseButtonContainer}>
|
||||
<Button
|
||||
type="button"
|
||||
className={`btn-close ${classes.toastCloseButton}`}
|
||||
data-bs-dismiss="toast"
|
||||
aria-label="Close"
|
||||
onClick={closeToast}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const DeleteToast = ({ isDisplayed = false, message, closeToast, type = 'success' }: Props) => {
|
||||
const getToastClass = () => (type === 'success' ? classes.toastSuccess : classes.toastWarning);
|
||||
|
||||
DeleteToast.defaultProps = {};
|
||||
return (
|
||||
<div
|
||||
className={`toast fade d-flex align-items-center position-fixed border-0 bottom-0 ${
|
||||
isDisplayed ? 'show' : 'hide'
|
||||
} ${classes.toastBase} ${getToastClass()}`}
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<div className={`toast-body ${classes['toast-body-override']} ${classes.toastText}`}>
|
||||
{message}
|
||||
{type === 'success' && (
|
||||
<span className={`${classes.toastIcon}`}>
|
||||
<CheckedMarkSvg />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.toastCloseButtonContainer}>
|
||||
<Button
|
||||
type="button"
|
||||
className={`btn-close ${classes.toastCloseButton}`}
|
||||
data-bs-dismiss="toast"
|
||||
aria-label="Close"
|
||||
onClick={closeToast}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
DeleteToast.defaultProps = {
|
||||
type: 'success',
|
||||
};
|
||||
|
||||
const DeleteToastMemo = memo(DeleteToast, areEqual);
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { FormsList, ContractForms } from './FormTabs';
|
||||
export { ContractFormsSuccessToast } from './ContractFormsSuccessToast';
|
||||
export { ContractFormsToast } from './ContractFormsToast';
|
||||
export { ContractFormsModal } from './ContractFormsModal';
|
||||
|
||||
@@ -33,6 +33,8 @@ const AddContractForms = ({ isOpen, onClose }: Props) => {
|
||||
const [formData, setFormData] = useState({ ...initData });
|
||||
const [toastMessage, setToastMessage] = useState('');
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [toastType, setToastType] = useState<'success' | 'error'>('success');
|
||||
const [wasAdding, setWasAdding] = useState(false);
|
||||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
@@ -89,6 +91,7 @@ const AddContractForms = ({ isOpen, onClose }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isAdded) {
|
||||
setToastType('success');
|
||||
setToastMessage('Contract Form Added');
|
||||
setShowToast(true);
|
||||
setFormData({ ...initData });
|
||||
@@ -102,7 +105,22 @@ const AddContractForms = ({ isOpen, onClose }: Props) => {
|
||||
dispatch(setContractFormAdded(false));
|
||||
}, 1500);
|
||||
}
|
||||
}, [isAdded]);
|
||||
}, [isAdded, companyId, dispatch, onClose]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAdding) {
|
||||
setWasAdding(true);
|
||||
} else if (wasAdding && !isAdded) {
|
||||
// API call finished but form wasn't added (error occurred)
|
||||
setToastType('error');
|
||||
setToastMessage('Failed to add contract form. Please try again.');
|
||||
setShowToast(true);
|
||||
setWasAdding(false);
|
||||
setTimeout(() => {
|
||||
setShowToast(false);
|
||||
}, 3000);
|
||||
}
|
||||
}, [isAdding, isAdded, wasAdding]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
@@ -123,6 +141,7 @@ const AddContractForms = ({ isOpen, onClose }: Props) => {
|
||||
}}
|
||||
showToast={showToast}
|
||||
toastMessage={toastMessage}
|
||||
toastType={toastType}
|
||||
onFormSubmit={onFormSubmit}
|
||||
onClickClose={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -33,6 +33,8 @@ const EditContractForms = ({ isOpen, onClose, initData }: Props) => {
|
||||
});
|
||||
const [toastMessage, setToastMessage] = useState('');
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [toastType, setToastType] = useState<'success' | 'error'>('success');
|
||||
const [wasEditing, setWasEditing] = useState(false);
|
||||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -47,6 +49,7 @@ const EditContractForms = ({ isOpen, onClose, initData }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdited) {
|
||||
setToastType('success');
|
||||
setToastMessage('Contract Form Updated');
|
||||
setShowToast(true);
|
||||
if (companyId) {
|
||||
@@ -59,8 +62,22 @@ const EditContractForms = ({ isOpen, onClose, initData }: Props) => {
|
||||
dispatch(setContractFormEdited(false));
|
||||
}, 1500);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [isEdited]);
|
||||
}, [isEdited, companyId, dispatch, onClose]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEditing) {
|
||||
setWasEditing(true);
|
||||
} else if (wasEditing && !isEdited) {
|
||||
// API call finished but form wasn't edited (error occurred)
|
||||
setToastType('error');
|
||||
setToastMessage('Failed to update contract form. Please try again.');
|
||||
setShowToast(true);
|
||||
setWasEditing(false);
|
||||
setTimeout(() => {
|
||||
setShowToast(false);
|
||||
}, 3000);
|
||||
}
|
||||
}, [isEditing, isEdited, wasEditing]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
@@ -121,7 +138,7 @@ const EditContractForms = ({ isOpen, onClose, initData }: Props) => {
|
||||
template: formData.template,
|
||||
has_signature: formData.requireSignature,
|
||||
};
|
||||
dispatch(editContractForm(payload, companyId));
|
||||
dispatch(editContractForm(payload));
|
||||
}
|
||||
},
|
||||
[formData, companyId, dispatch]
|
||||
@@ -139,6 +156,7 @@ const EditContractForms = ({ isOpen, onClose, initData }: Props) => {
|
||||
}}
|
||||
showToast={showToast}
|
||||
toastMessage={toastMessage}
|
||||
toastType={toastType}
|
||||
onFormSubmit={onFormSubmit}
|
||||
onClickClose={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
contractFormsSelector,
|
||||
fetchingContractFormsSelector,
|
||||
companyIdSelector,
|
||||
// deletingContractFormSelector,
|
||||
deletingContractFormSelector,
|
||||
contractFormDeletedSelector,
|
||||
} from 'Containers/Forms/selectors';
|
||||
import { ContractForms } from 'Components/Forms';
|
||||
@@ -20,13 +20,16 @@ const ContractFormsContainer = () => {
|
||||
const contractForms = useSelector(contractFormsSelector, areEqual);
|
||||
const fetching = useSelector(fetchingContractFormsSelector, areEqual);
|
||||
const companyId = useSelector(companyIdSelector, areEqual);
|
||||
// const deleting = useSelector(deletingContractFormSelector, areEqual);
|
||||
const deleting = useSelector(deletingContractFormSelector, areEqual);
|
||||
const deletedId = useSelector(contractFormDeletedSelector, areEqual);
|
||||
const [deleteModal, setDeleteModal] = useState({
|
||||
isOpen: false,
|
||||
id: null,
|
||||
});
|
||||
const [showDeletedToast, setShowDeletedToast] = useState(false);
|
||||
const [deleteToastMessage, setDeleteToastMessage] = useState('Form Deleted');
|
||||
const [deleteToastType, setDeleteToastType] = useState<'success' | 'error'>('success');
|
||||
const [wasDeleting, setWasDeleting] = useState(false);
|
||||
const [showAddFormsModal, setShowAddFormsModal] = useState(false);
|
||||
const [editModal, setEditModal] = useState<{
|
||||
isOpen: boolean;
|
||||
@@ -89,15 +92,34 @@ const ContractFormsContainer = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (deletedId) {
|
||||
// Success case
|
||||
setDeleteToastType('success');
|
||||
setDeleteToastMessage('Form Deleted');
|
||||
setShowDeletedToast(true);
|
||||
dispatch(listCompanyContractForms(companyId));
|
||||
|
||||
setShowDeletedToast(true);
|
||||
|
||||
setTimeout(() => setShowDeletedToast(false), 1500);
|
||||
|
||||
dispatch(setDeletedFormId(null));
|
||||
setTimeout(() => {
|
||||
setShowDeletedToast(false);
|
||||
dispatch(setDeletedFormId(null));
|
||||
}, 1500);
|
||||
}
|
||||
}, [deletedId]);
|
||||
}, [deletedId, companyId, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (deleting) {
|
||||
setWasDeleting(true);
|
||||
} else if (wasDeleting && !deletedId) {
|
||||
// API call finished but form wasn't deleted (error occurred)
|
||||
setDeleteToastType('error');
|
||||
setDeleteToastMessage('Failed to delete form. Please try again.');
|
||||
setShowDeletedToast(true);
|
||||
setWasDeleting(false);
|
||||
|
||||
setTimeout(() => {
|
||||
setShowDeletedToast(false);
|
||||
}, 3000);
|
||||
}
|
||||
}, [deleting, deletedId, wasDeleting]);
|
||||
|
||||
useEffect(() => {
|
||||
getContractForms();
|
||||
@@ -128,7 +150,12 @@ const ContractFormsContainer = () => {
|
||||
}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
{deletedId && <DeleteToast isDisplayed={showDeletedToast} closeToast={closeToast} message="Form Deleted" />}
|
||||
<DeleteToast
|
||||
isDisplayed={showDeletedToast}
|
||||
closeToast={closeToast}
|
||||
message={deleteToastMessage}
|
||||
type={deleteToastType}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint-disable */
|
||||
import { handleApiRequest } from 'Utils/handleApiRequest';
|
||||
import { Api } from 'Utils/api';
|
||||
import { FormRequestBody, FormTemplateResponse } from 'Containers/Forms/Models';
|
||||
@@ -53,12 +54,12 @@ export const setDeletedFormId = (value: number | null) => async (dispatch: any)
|
||||
// Thunk to delete a contract form by id
|
||||
export const deleteContractForm = (contractFormId: number) => async (dispatch: any) => {
|
||||
dispatch({ type: DELETING_CONTRACT_FORM, payload: true });
|
||||
|
||||
try {
|
||||
await handleApiRequest(dispatch, Api.delete(`/contract-forms/${contractFormId}`), '', DELETING_CONTRACT_FORM);
|
||||
|
||||
dispatch(setDeletedFormId(contractFormId));
|
||||
// dispatch({ type: CONTRACT_FORM_DELETED, payload: contractFormId });
|
||||
} catch (error) {
|
||||
// handle error if needed
|
||||
dispatch({ type: DELETING_CONTRACT_FORM, payload: false });
|
||||
}
|
||||
};
|
||||
@@ -73,16 +74,19 @@ export const setContractFormAdded = (value: boolean) => (dispatch: any) => {
|
||||
// Thunk to add a contract form
|
||||
export const addContractForm = (formData: FormRequestBody) => async (dispatch: any) => {
|
||||
dispatch({ type: ADDING_CONTRACT_FORM, payload: true });
|
||||
try {
|
||||
await handleApiRequest(
|
||||
dispatch,
|
||||
Api.post('/contract-forms', formData),
|
||||
'CONTRACT_FORM_ERRORS',
|
||||
ADDING_CONTRACT_FORM
|
||||
);
|
||||
|
||||
const response = await handleApiRequest(
|
||||
dispatch,
|
||||
Api.post('/contract-forms', formData),
|
||||
'CONTRACT_FORM_ERRORS',
|
||||
ADDING_CONTRACT_FORM
|
||||
);
|
||||
|
||||
if (response) {
|
||||
// API call succeeded
|
||||
dispatch(setContractFormAdded(true));
|
||||
} catch (error) {
|
||||
dispatch({ type: ADDING_CONTRACT_FORM, payload: false });
|
||||
} else {
|
||||
// API call failed
|
||||
dispatch(setContractFormAdded(false));
|
||||
}
|
||||
};
|
||||
@@ -95,18 +99,21 @@ export const setContractFormEdited = (value: boolean) => (dispatch: any) => {
|
||||
};
|
||||
|
||||
// Thunk to edit a contract form
|
||||
export const editContractForm = (formData: FormRequestBody, contractId: string) => async (dispatch: any) => {
|
||||
export const editContractForm = (formData: FormRequestBody) => async (dispatch: any) => {
|
||||
dispatch({ type: EDITING_CONTRACT_FORM, payload: true });
|
||||
try {
|
||||
await handleApiRequest(
|
||||
dispatch,
|
||||
Api.put('/contract-forms', formData),
|
||||
'CONTRACT_FORM_ERRORS',
|
||||
EDITING_CONTRACT_FORM
|
||||
);
|
||||
|
||||
const response = await handleApiRequest(
|
||||
dispatch,
|
||||
Api.put('/contract-forms', formData),
|
||||
'CONTRACT_FORM_ERRORS',
|
||||
EDITING_CONTRACT_FORM
|
||||
);
|
||||
|
||||
if (response) {
|
||||
// API call succeeded
|
||||
dispatch(setContractFormEdited(true));
|
||||
} catch (error) {
|
||||
dispatch({ type: EDITING_CONTRACT_FORM, payload: false });
|
||||
} else {
|
||||
// API call failed
|
||||
dispatch(setContractFormEdited(false));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user