diff --git a/src/shared/components/Forms/FormTabs/DeleteFormModal/DeleteFormModal.tsx b/src/shared/components/Forms/FormTabs/DeleteFormModal/DeleteFormModal.tsx new file mode 100644 index 0000000..47bd25e --- /dev/null +++ b/src/shared/components/Forms/FormTabs/DeleteFormModal/DeleteFormModal.tsx @@ -0,0 +1,50 @@ +import React, { memo } from 'react'; +import { areEqual } from 'Utils/equalityChecks'; +import { Modal } from 'Components/Modal'; +import { Button } from 'Components/Button'; + +import classes from './deleteFormModal.module.css'; + +interface Props { + id: number; + isOpen: boolean; + modalCloseClick: (e: any) => void; + onDelete: (id: number) => void; +} + +const DeleteFormModal = ({ id, isOpen, modalCloseClick, onDelete }: Props) => ( +
+ { + e.preventDefault(); + onDelete(id); + }} + > + Delete + + } + modalFooter + closeButtonText="Cancel" + dataBsBackdrop="static" + dataBsKeyboard="false" + modalCloseClick={modalCloseClick} + > +
+

Are you sure you want to delete this form?

+
+
+
+); + +const DeleteFormModalMemo = memo(DeleteFormModal, areEqual); + +export { DeleteFormModalMemo as DeleteFormModal }; diff --git a/src/shared/components/Forms/FormTabs/DeleteFormModal/deleteFormModal.module.css b/src/shared/components/Forms/FormTabs/DeleteFormModal/deleteFormModal.module.css new file mode 100644 index 0000000..94d73a4 --- /dev/null +++ b/src/shared/components/Forms/FormTabs/DeleteFormModal/deleteFormModal.module.css @@ -0,0 +1,86 @@ +.modalContent { + padding: 1.3em 1.5em; + box-shadow: 0px 24px 44px rgba(119, 113, 133, 0.2); + border-radius: 5px; +} +.modalDialog { + max-width: 648px; +} +.modalHeader { + padding: 0 0 0.5em 0; + border-color: #e8e7ed; + height: 40px; +} + +.modalTitle { + font-family: IBM Plex Sans; + font-style: normal; + font-weight: 600; + font-size: 18px; + text-transform: capitalize; + line-height: 24px; + color: #5b476b; + text-align: center; + flex: 1; +} +.modalBody { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding: 1.3rem 0 1.1rem 0; +} +.modalBody { + font-family: IBM Plex Sans; + font-style: normal; + font-weight: normal; + font-size: 16px; + line-height: 24px; + color: #000000; +} +.deleteModalHeader { + width: 100%; + max-width: 343px; + border-bottom: 1px solid #d2cfda; +} +.deleteModalCopy { + width: 350px; + text-transform: capitalize; + font-weight: 500; +} + +.modalFooter { + padding: 1rem 0 0 0; + border: none; + flex-direction: row-reverse; + justify-content: center; +} +.modalFooter button { + font-family: IBM Plex Sans; + font-style: normal; + font-weight: 600; + font-size: 16px; + line-height: 24px; + color: #5b476b; + background-color: #ffffff; + border: 1px solid #5b476b; + border-radius: 5px; + width: 151px; + height: 44px; +} + +.modalFooter .closeButtonClass:hover, +.modalFooter .closeButtonClass:focus { + background-color: #f3f3f6; + border-color: #d2cfda; + color: #777185; +} +.modalFooter .delete { + border-color: #e82828 !important; + color: #e82828 !important; +} +.modalFooter .delete:hover, +.modalFooter .delete:focus { + background-color: #e82828 !important; + color: #ffffff !important; +} diff --git a/src/shared/components/Forms/FormTabs/DeleteFormModal/index.ts b/src/shared/components/Forms/FormTabs/DeleteFormModal/index.ts new file mode 100644 index 0000000..d5bccb5 --- /dev/null +++ b/src/shared/components/Forms/FormTabs/DeleteFormModal/index.ts @@ -0,0 +1 @@ +export { DeleteFormModal } from './DeleteFormModal'; diff --git a/src/shared/components/Forms/FormTabs/DeleteToast/DeleteToast.tsx b/src/shared/components/Forms/FormTabs/DeleteToast/DeleteToast.tsx new file mode 100644 index 0000000..e507b9c --- /dev/null +++ b/src/shared/components/Forms/FormTabs/DeleteToast/DeleteToast.tsx @@ -0,0 +1,46 @@ +import React, { memo } from 'react'; + +import { areEqual } from 'Utils/equalityChecks'; +import { CheckedMarkSvg } from 'Components/Icons/CheckedMark'; +import { Button } from 'Components/Button'; + +import classes from './deleteToast.module.css'; + +export interface Props { + isDisplayed: boolean; + message: string; + closeToast: (e: any) => void; +} + +const DeleteToast = ({ isDisplayed = false, message, closeToast }: Props) => ( +
+
+ {message} + + + +
+
+
+
+); + +DeleteToast.defaultProps = {}; + +const DeleteToastMemo = memo(DeleteToast, areEqual); + +export { DeleteToastMemo as DeleteToast }; diff --git a/src/shared/components/Forms/FormTabs/DeleteToast/deleteToast.module.css b/src/shared/components/Forms/FormTabs/DeleteToast/deleteToast.module.css new file mode 100644 index 0000000..e724271 --- /dev/null +++ b/src/shared/components/Forms/FormTabs/DeleteToast/deleteToast.module.css @@ -0,0 +1,77 @@ +.toastBase { + height: 40px !important; + width: calc(100% - (315px + 1.5rem)) !important; + z-index: 1000; + box-shadow: none; + border-radius: 0; + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; + transition: opacity 0.15s linear; + left: calc(290px + 0.75rem); + text-align: center; +} +.toast-body-override { + width: 95%; + padding: unset ip !important; +} +.toastCloseButtonContainer { + display: flex; + justify-content: flex-end; + width: 10%; +} +.toastCloseButton { + border-radius: 50% !important; + color: #5b476b !important; + background-color: #fff !important; + padding: 0.25rem !important; + opacity: unset !important; + /* This is the actual svg pulled from bootstrap. not the 0.5em beside center on the 2nd line. This is what has changed */ + background: transparent + url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") + center/0.5em auto no-repeat; +} +.toastIcon { + padding-left: 10px; +} +.toastCloseButton:hover { + color: #000; + text-decoration: none; + opacity: unset !important; +} +.toastText { + font-family: IBM Plex Sans !important; + font-style: normal !important; + font-weight: 600 !important; + font-size: 16px !important; + line-height: 24px !important; + color: #5b476b !important; +} + +.toastSuccess { + background-color: #dcf5f0; + border-bottom: 1px solid #40c9ae !important; +} + +.toastWarning { + background-color: #fff0f0; + border-bottom: 1px solid #e82828; +} + +.toastCloseIcon { + position: absolute; + right: 22px; +} + +@media (max-width: 768px) { + .toastBase { + width: 100%; + /* height: auto; */ + padding: 12px 66px 12px 22px; + } + + .toastText { + font-size: 14px; + } +} diff --git a/src/shared/components/Forms/FormTabs/DeleteToast/index.ts b/src/shared/components/Forms/FormTabs/DeleteToast/index.ts new file mode 100644 index 0000000..fd0faf6 --- /dev/null +++ b/src/shared/components/Forms/FormTabs/DeleteToast/index.ts @@ -0,0 +1 @@ +export { DeleteToast } from './DeleteToast'; diff --git a/src/shared/components/Forms/FormTabs/index.ts b/src/shared/components/Forms/FormTabs/index.ts index 6b5814f..1f42edd 100644 --- a/src/shared/components/Forms/FormTabs/index.ts +++ b/src/shared/components/Forms/FormTabs/index.ts @@ -1,2 +1,4 @@ export { ContractForms } from './ContractForms'; export { FormsList } from './FormsList'; +export { DeleteFormModal } from './DeleteFormModal'; +export { DeleteToast } from './DeleteToast'; diff --git a/src/shared/containers/Forms/FormTabs/ContractForms/ContractForms.tsx b/src/shared/containers/Forms/FormTabs/ContractForms/ContractForms.tsx index 4599f65..2be84a0 100644 --- a/src/shared/containers/Forms/FormTabs/ContractForms/ContractForms.tsx +++ b/src/shared/containers/Forms/FormTabs/ContractForms/ContractForms.tsx @@ -1,15 +1,29 @@ -import React, { memo, useEffect, useCallback } from 'react'; +import React, { memo, useEffect, useCallback, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { areEqual } from 'Utils/equalityChecks'; -import { contractFormsSelector, fetchingContractFormsSelector, companyIdSelector } from 'Containers/Forms/selectors'; +import { + contractFormsSelector, + fetchingContractFormsSelector, + companyIdSelector, + deletingContractFormSelector, + contractFormDeletedSelector, +} from 'Containers/Forms/selectors'; import { ContractForms } from 'Components/Forms'; -import { listCompanyContractForms } from 'Containers/Forms/actions'; +import { DeleteFormModal, DeleteToast } from 'Components/Forms/FormTabs'; +import { listCompanyContractForms, deleteContractForm, setDeletedFormId } from '../../actions'; const ContractFormsContainer = () => { const dispatch = useDispatch(); const contractForms = useSelector(contractFormsSelector, areEqual); const fetching = useSelector(fetchingContractFormsSelector, areEqual); const companyId = useSelector(companyIdSelector, 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 getContractForms = useCallback(() => { if (companyId) { @@ -17,18 +31,70 @@ const ContractFormsContainer = () => { } }, [dispatch, companyId]); + const deleteButtonClick = useCallback((id: number) => { + setDeleteModal({ + isOpen: true, + id, + }); + }, []); + + const handleDelete = useCallback( + (id: number) => { + dispatch(deleteContractForm(id)); + setDeleteModal({ + isOpen: false, + id: null, + }); + }, + [dispatch] + ); + + const closeToast = useCallback((e: any) => { + e.preventDefault(); + setShowDeletedToast(false); + }, []); + + useEffect(() => { + if (deletedId) { + dispatch(listCompanyContractForms(companyId)); + + setShowDeletedToast(true); + + setTimeout(() => setShowDeletedToast(false), 1500); + + dispatch(setDeletedFormId(null)); + } + }, [deletedId]); + useEffect(() => { getContractForms(); }, [getContractForms]); return ( - + <> + {}} + onDelete={deleteButtonClick} + onClickRow={() => {}} + // deleting={deleting} + // deletedId={deletedId} + /> + + + setDeleteModal({ + isOpen: false, + id: null, + }) + } + onDelete={handleDelete} + /> + {deletedId && } + ); }; diff --git a/src/shared/containers/Forms/actions.ts b/src/shared/containers/Forms/actions.ts index 6710554..a70ec5d 100644 --- a/src/shared/containers/Forms/actions.ts +++ b/src/shared/containers/Forms/actions.ts @@ -4,10 +4,14 @@ import { FormTemplateResponse } from 'Containers/Forms/Models'; export const CONTRACT_FORMS = 'CONTRACT_FORMS'; export const FETCHING_CONTRACT_FORMS = 'FETCHING_CONTRACT_FORMS'; +export const DELETING_CONTRACT_FORM = 'DELETING_CONTRACT_FORM'; +export const CONTRACT_FORM_DELETED = 'CONTRACT_FORM_DELETED'; interface FormsActionTypes { CONTRACT_FORMS: FormTemplateResponse; FETCHING_CONTRACT_FORMS: boolean; + DELETING_CONTRACT_FORM: boolean; + CONTRACT_FORM_DELETED: number; } interface MessageAction { @@ -31,3 +35,19 @@ export const listCompanyContractForms = (companyId: number) => async (dispatch: payload: response, }); }; + +// 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({ type: CONTRACT_FORM_DELETED, payload: contractFormId }); + } catch (error) { + // handle error if needed + dispatch({ type: DELETING_CONTRACT_FORM, payload: false }); + } +}; + +export const setDeletedFormId = (value: number | null) => async (dispatch: any) => { + dispatch({ type: CONTRACT_FORM_DELETED, payload: value }); +}; diff --git a/src/shared/containers/Forms/reducer.ts b/src/shared/containers/Forms/reducer.ts index 6165e3b..bd19395 100644 --- a/src/shared/containers/Forms/reducer.ts +++ b/src/shared/containers/Forms/reducer.ts @@ -1,8 +1,16 @@ -import { CONTRACT_FORMS, FETCHING_CONTRACT_FORMS, SetFormsActionTypes } from './actions'; +import { + CONTRACT_FORMS, + FETCHING_CONTRACT_FORMS, + SetFormsActionTypes, + DELETING_CONTRACT_FORM, + CONTRACT_FORM_DELETED, +} from './actions'; const initialState = { data: [], fetchingContractForms: false, + deletingContractForm: false, + contractFormDeleted: null, }; export const formsReducer = (state = initialState, action: SetFormsActionTypes) => { @@ -19,6 +27,18 @@ export const formsReducer = (state = initialState, action: SetFormsActionTypes) fetchingContractForms: action.payload, }; } + case DELETING_CONTRACT_FORM: { + return { + ...state, + deletingContractForm: action.payload, + }; + } + case CONTRACT_FORM_DELETED: { + return { + ...state, + contractFormDeleted: action.payload, + }; + } default: return state; } diff --git a/src/shared/containers/Forms/selectors.ts b/src/shared/containers/Forms/selectors.ts index 5dbe34a..7df602a 100644 --- a/src/shared/containers/Forms/selectors.ts +++ b/src/shared/containers/Forms/selectors.ts @@ -2,3 +2,5 @@ export const contractFormsSelector = ({ forms }: any) => forms?.data || []; export const fetchingContractFormsSelector = ({ forms }: any) => forms?.fetchingContractForms || false; export { firstCompanyIdSelector as companyIdSelector } from '../Projects/selectors'; +export const deletingContractFormSelector = ({ forms }: any) => forms?.deletingContractForm || false; +export const contractFormDeletedSelector = ({ forms }: any) => forms?.contractFormDeleted || null;