Compare commits
2 Commits
fb79e0ef12
...
3b46073ee8
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b46073ee8 | |||
| b4a7a56200 |
@@ -42,6 +42,7 @@ import { Project } from 'Containers/Project';
|
|||||||
import { Account, About } from 'Containers/User';
|
import { Account, About } from 'Containers/User';
|
||||||
import { ProjectData } from 'Containers/ProjectData';
|
import { ProjectData } from 'Containers/ProjectData';
|
||||||
import { RocketDry } from 'Containers/RocketDry';
|
import { RocketDry } from 'Containers/RocketDry';
|
||||||
|
import { Forms } from 'Containers/Forms';
|
||||||
|
|
||||||
// route components
|
// route components
|
||||||
import { PhotoShareProvider } from 'Context/PhotoShare/PhotoShareProvider';
|
import { PhotoShareProvider } from 'Context/PhotoShare/PhotoShareProvider';
|
||||||
@@ -182,6 +183,13 @@ const PeopleRoute = () => (
|
|||||||
</DashboardWrapper>
|
</DashboardWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Form route
|
||||||
|
const FormsRoute = () => (
|
||||||
|
<DashboardWrapper>
|
||||||
|
<Forms />
|
||||||
|
</DashboardWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
const PhotoViewRoute = () => (
|
const PhotoViewRoute = () => (
|
||||||
<PhotoViewWrapper>
|
<PhotoViewWrapper>
|
||||||
<PhotoView />
|
<PhotoView />
|
||||||
@@ -378,6 +386,8 @@ export const Routes = () => (
|
|||||||
|
|
||||||
<PrivateRoute exact path="/people" render={PeopleRoute} />
|
<PrivateRoute exact path="/people" render={PeopleRoute} />
|
||||||
|
|
||||||
|
<PrivateRoute exact path="/form" render={FormsRoute} />
|
||||||
|
|
||||||
<PrivateRoute exact path="/user/account" render={AccountRoute} />
|
<PrivateRoute exact path="/user/account" render={AccountRoute} />
|
||||||
<PrivateRoute exact path="/user/about" render={AboutRoute} />
|
<PrivateRoute exact path="/user/about" render={AboutRoute} />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { FormTemplateResponse } from 'Containers/Forms/Models';
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { TabContent } from 'Components/Tabs';
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import { Spinner } from 'Components/Spinner';
|
||||||
|
import { PurpleButton } from 'Components/Button';
|
||||||
|
|
||||||
|
import { FormsList } from '../FormsList';
|
||||||
|
import classes from './contractForms.module.css';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
forms: FormTemplateResponse;
|
||||||
|
fetching: boolean;
|
||||||
|
onClickRow?: (e: any) => void;
|
||||||
|
onAdd: () => void;
|
||||||
|
onDelete: (id: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContractForms = ({ forms, onClickRow, fetching, onAdd, onDelete }: Props) => (
|
||||||
|
<TabContent key="tab-content-contract-forms" id="contract-forms" className="show active position-relative">
|
||||||
|
<div className={classes.formsContent}>
|
||||||
|
<div className={`d-flex justify-content-start align-items-center ${classes.contentHeader}`}>
|
||||||
|
<h2>Form Templates</h2>
|
||||||
|
<PurpleButton className={classes.addButton} onClick={onAdd}>
|
||||||
|
Add +
|
||||||
|
</PurpleButton>
|
||||||
|
</div>
|
||||||
|
{fetching && <Spinner loading />}
|
||||||
|
{!fetching && <FormsList forms={forms} onClickRow={onClickRow} onDelete={onDelete} />}
|
||||||
|
</div>
|
||||||
|
</TabContent>
|
||||||
|
);
|
||||||
|
|
||||||
|
ContractForms.defaultProps = {
|
||||||
|
onClickRow: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContractFormsMemo = memo(ContractForms, areEqual);
|
||||||
|
|
||||||
|
export { ContractFormsMemo as ContractForms };
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
.formsContent {
|
||||||
|
min-height: calc(42px + (64px * 15));
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: baseline;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentHeader {
|
||||||
|
width: 100%;
|
||||||
|
height: 46px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.contentHeader h2 {
|
||||||
|
font-family: IBM Plex Sans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 19px;
|
||||||
|
color: #000000;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addButton {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #9a00ff;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 160px;
|
||||||
|
border-radius: 25px;
|
||||||
|
margin-left: 2.4rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ContractForms } from './ContractForms';
|
||||||
@@ -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) => (
|
||||||
|
<div>
|
||||||
|
<Modal
|
||||||
|
id={id && id.toString()}
|
||||||
|
classes={classes}
|
||||||
|
title="Delete Contract Form?"
|
||||||
|
isOpen={isOpen}
|
||||||
|
modalHeader
|
||||||
|
footerButtons={
|
||||||
|
<Button
|
||||||
|
className={`${classes.delete}`}
|
||||||
|
id={id && id.toString()}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onDelete(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
modalFooter
|
||||||
|
closeButtonText="Cancel"
|
||||||
|
dataBsBackdrop="static"
|
||||||
|
dataBsKeyboard="false"
|
||||||
|
modalCloseClick={modalCloseClick}
|
||||||
|
>
|
||||||
|
<div className={classes.deleteModalCopy}>
|
||||||
|
<p>Are you sure you want to delete this form?</p>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const DeleteFormModalMemo = memo(DeleteFormModal, areEqual);
|
||||||
|
|
||||||
|
export { DeleteFormModalMemo as DeleteFormModal };
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { DeleteFormModal } from './DeleteFormModal';
|
||||||
@@ -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) => (
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
|
||||||
|
DeleteToast.defaultProps = {};
|
||||||
|
|
||||||
|
const DeleteToastMemo = memo(DeleteToast, areEqual);
|
||||||
|
|
||||||
|
export { DeleteToastMemo as DeleteToast };
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { DeleteToast } from './DeleteToast';
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { FormTemplateResponse, FormTemplate } from 'Containers/Forms/Models';
|
||||||
|
import { Table, TableBody, TableColumn, TableHeader, TableRow, Th } from 'Components/Table';
|
||||||
|
import { Icon } from 'Components/Icons';
|
||||||
|
|
||||||
|
import { formatDate } from 'Utils/helpers';
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import { NoFormsTable } from '../NoFormsTable';
|
||||||
|
|
||||||
|
import classes from './formsList.module.css';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
forms: FormTemplateResponse;
|
||||||
|
onDelete: (id: number) => void;
|
||||||
|
onClickRow?: (e: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormsList = ({ forms, onClickRow, onDelete }: Props) => (
|
||||||
|
<>
|
||||||
|
{forms?.data?.length > 0 ? (
|
||||||
|
<Table className={`table ${classes.formListWrapper}`}>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<Th>Name</Th>
|
||||||
|
<Th>Date Created</Th>
|
||||||
|
<Th />
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{forms.data.map(({ id, created_at: createdAt, name }: FormTemplate) => (
|
||||||
|
<TableRow key={id}>
|
||||||
|
<TableColumn dataId={id} tdOnClick={onClickRow}>
|
||||||
|
<p>{name}</p>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn dataId={id} tdOnClick={onClickRow} className={classes.columnContent}>
|
||||||
|
<p className={classes.numberAndDate}>{formatDate(createdAt, 'PP')}</p>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn>
|
||||||
|
<button
|
||||||
|
className={classes.deleteBtn}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onDelete(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon type="trash" />
|
||||||
|
</button>
|
||||||
|
</TableColumn>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
) : (
|
||||||
|
<NoFormsTable />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
FormsList.defaultProps = {
|
||||||
|
onClickRow: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormsListMemo = memo(FormsList, areEqual);
|
||||||
|
|
||||||
|
export { FormsListMemo as FormsList };
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
.formListContainer {
|
||||||
|
min-height: 700px;
|
||||||
|
}
|
||||||
|
.formListWrapper {
|
||||||
|
font-family: IBM Plex Sans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: #5b476b;
|
||||||
|
border-color: #e8e7ed;
|
||||||
|
background-color: #ffffff;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formListWrapper thead th:first-child,
|
||||||
|
.formListWrapper tbody td:first-child {
|
||||||
|
padding-left: 72px;
|
||||||
|
}
|
||||||
|
.formListWrapper thead th:last-child,
|
||||||
|
.formListWrapper tbody td:last-child {
|
||||||
|
padding-right: 72px;
|
||||||
|
}
|
||||||
|
.formListWrapper thead th {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #777185;
|
||||||
|
padding-bottom: 1.2em;
|
||||||
|
border-bottom-color: #e8e7ed !important;
|
||||||
|
}
|
||||||
|
.formListWrapper tbody tr:hover {
|
||||||
|
background-color: #f4e5ff;
|
||||||
|
transition: 0.2s ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.formListWrapper tbody td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.formListWrapper p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.deleteBtn {
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 3px;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background-color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.deleteBtn:hover {
|
||||||
|
background-color: rgba(255, 0, 0, 0.399);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FormsList } from './FormsList';
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import { Table, TableHeader, TableRow, Th } from 'Components/Table';
|
||||||
|
import { Icon } from 'Components/Icons';
|
||||||
|
|
||||||
|
import classes from './noFormsTable.module.css';
|
||||||
|
|
||||||
|
const NoFormsTable = () => (
|
||||||
|
<div>
|
||||||
|
<Table className={`table ${classes.formListWrapper}`}>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<Th>Address</Th>
|
||||||
|
<Th>Date Created</Th>
|
||||||
|
<Th />
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
</Table>
|
||||||
|
<div className={`d-flex justify-content-center align-items-center flex-column w-100 ${classes.noFormsContent}`}>
|
||||||
|
<p className={classes.noFormsText}>No forms yet. Create a new form.</p>
|
||||||
|
<Icon type="projects" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const NoFormsTableMemo = memo(NoFormsTable, areEqual);
|
||||||
|
|
||||||
|
export { NoFormsTableMemo as NoFormsTable };
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { NoFormsTable } from './NoFormsTable';
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
.noFormsContent {
|
||||||
|
padding-top: 240px;
|
||||||
|
padding-bottom: 310px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formListWrapper {
|
||||||
|
font-family: IBM Plex Sans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: #5b476b;
|
||||||
|
border-color: #e8e7ed;
|
||||||
|
background-color: #ffffff;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formListWrapper thead th:first-child,
|
||||||
|
.formListWrapper tbody td:first-child {
|
||||||
|
padding-left: 72px;
|
||||||
|
}
|
||||||
|
.formListWrapper thead th:last-child,
|
||||||
|
.formListWrapper tbody td:last-child {
|
||||||
|
padding-right: 72px;
|
||||||
|
}
|
||||||
|
.formListWrapper thead th {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #777185;
|
||||||
|
padding-bottom: 1.2em;
|
||||||
|
border-bottom-color: #e8e7ed !important;
|
||||||
|
}
|
||||||
|
.formListWrapper tbody tr:hover {
|
||||||
|
background-color: #f4e5ff;
|
||||||
|
transition: 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
.formListWrapper tbody td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.formListWrapper p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.noFormsText {
|
||||||
|
font-family: IBM Plex Sans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 30px;
|
||||||
|
color: #b3abc6;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export { ContractForms } from './ContractForms';
|
||||||
|
export { FormsList } from './FormsList';
|
||||||
|
export { DeleteFormModal } from './DeleteFormModal';
|
||||||
|
export { DeleteToast } from './DeleteToast';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FormsList, ContractForms } from './FormTabs';
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { Icon } from 'Components/Icons';
|
||||||
|
import React, { memo, ReactNode, useState } from 'react';
|
||||||
|
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import { width } from 'Utils/screen';
|
||||||
|
import { Tab } from '../Tab';
|
||||||
|
import classes from './forms.tabs.module.css';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
id?: string;
|
||||||
|
className?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTabs = (activeTab: string, onTabClick: (e: any) => void) => (
|
||||||
|
<>
|
||||||
|
<Tab
|
||||||
|
key="contract-forms-tab"
|
||||||
|
id="contract-forms-tab"
|
||||||
|
className={`${classes.flexCenter} ${classes.button} ${
|
||||||
|
activeTab === 'contract-forms-tab' ? `active ${classes['active-Tab']}` : ''
|
||||||
|
}`}
|
||||||
|
target="contract-forms"
|
||||||
|
onClick={onTabClick}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<Icon type="projects" className={classes.icon} />
|
||||||
|
<span>Contract Forms</span>
|
||||||
|
</>
|
||||||
|
</Tab>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
In order to override bootstraps active class on tabs, there is a click event onTabClick, which will get the name of the tab that was clicked
|
||||||
|
and then trigger a re-render. Note in the createTabs method above, where the active class is added or not, based on which tab was clicked.
|
||||||
|
*/
|
||||||
|
const FormTabs = ({ id = 'tabs', className, children }: Props) => {
|
||||||
|
// We want to set the initial active tab to the first tab in the incoming tabList
|
||||||
|
const [activeTab, setActiveTab] = useState('contract-forms-tab');
|
||||||
|
|
||||||
|
const onTabClick = (e: any) => {
|
||||||
|
// Occasionally, e.currentTarget is undefined. Set the current activeTab if we run into this bug
|
||||||
|
setActiveTab(e?.currentTarget?.id || activeTab);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col">
|
||||||
|
<div className={classes.formTabWrapper}>
|
||||||
|
<div className={classes.tabsContainer}>
|
||||||
|
<ul
|
||||||
|
className={`nav nav-tabs ${width < 576 ? 'flex-sm-column' : 'width'} ${classes.tabs} ${
|
||||||
|
className || ''
|
||||||
|
}`}
|
||||||
|
id={id}
|
||||||
|
role="tablist"
|
||||||
|
>
|
||||||
|
{createTabs(activeTab, onTabClick)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="tab-content w-100 h-100 d-inline-block" id="formTabContent" style={{ height: 'auto' }}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FormTabs.defaultProps = {
|
||||||
|
id: undefined,
|
||||||
|
className: undefined,
|
||||||
|
children: undefined,
|
||||||
|
};
|
||||||
|
const FormTabsMemo = memo(FormTabs, areEqual);
|
||||||
|
export { FormTabsMemo as FormTabs };
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
.formTabWrapper {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
.tabsContainer {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.tabs {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.active-Tab,
|
||||||
|
.active-Tab:hover {
|
||||||
|
color: #ffffff !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 0 0 16px 16px;
|
||||||
|
background: linear-gradient(316.14deg, #6d00e6 1.84%, #9a00ff 96.25%) !important;
|
||||||
|
}
|
||||||
|
.active-Tab::after {
|
||||||
|
content: attr(data-text);
|
||||||
|
height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
.icon path {
|
||||||
|
fill: #9a00ff;
|
||||||
|
}
|
||||||
|
.active-Tab .icon path {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-phone {
|
||||||
|
padding-bottom: 0.265em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexTop {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.flexCenter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterButtonContainer {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-right: 24px;
|
||||||
|
width: 55px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterButton {
|
||||||
|
font-family: IBM Plex Sans;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-transform: capitalize;
|
||||||
|
color: #9a00ff;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterButton:hover,
|
||||||
|
.filterButton:focus {
|
||||||
|
color: #9a00ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterIcon {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FormTabs } from './FormTabs';
|
||||||
@@ -4,3 +4,4 @@ export { MobileProjectsTabs } from './ProjectsTabs';
|
|||||||
export { TabContent } from './TabContent';
|
export { TabContent } from './TabContent';
|
||||||
export { ProjectTabMenu } from './ProjectTabMenu';
|
export { ProjectTabMenu } from './ProjectTabMenu';
|
||||||
export { ProjectsTabMenu } from './ProjectsTabMenu';
|
export { ProjectsTabMenu } from './ProjectsTabMenu';
|
||||||
|
export { FormTabs } from './FormTabs';
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import React, { memo, useEffect, useCallback, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import {
|
||||||
|
contractFormsSelector,
|
||||||
|
fetchingContractFormsSelector,
|
||||||
|
companyIdSelector,
|
||||||
|
deletingContractFormSelector,
|
||||||
|
contractFormDeletedSelector,
|
||||||
|
} from 'Containers/Forms/selectors';
|
||||||
|
import { ContractForms } from 'Components/Forms';
|
||||||
|
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) {
|
||||||
|
dispatch(listCompanyContractForms(companyId));
|
||||||
|
}
|
||||||
|
}, [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 (
|
||||||
|
<>
|
||||||
|
<ContractForms
|
||||||
|
forms={contractForms}
|
||||||
|
fetching={fetching}
|
||||||
|
onAdd={() => {}}
|
||||||
|
onDelete={deleteButtonClick}
|
||||||
|
onClickRow={() => {}}
|
||||||
|
// deleting={deleting}
|
||||||
|
// deletedId={deletedId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DeleteFormModal
|
||||||
|
id={deleteModal.id as number}
|
||||||
|
isOpen={deleteModal.isOpen}
|
||||||
|
modalCloseClick={() =>
|
||||||
|
setDeleteModal({
|
||||||
|
isOpen: false,
|
||||||
|
id: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onDelete={handleDelete}
|
||||||
|
/>
|
||||||
|
{deletedId && <DeleteToast isDisplayed={showDeletedToast} closeToast={closeToast} message="Form Deleted" />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContractFormsContainerMemo = memo(ContractFormsContainer, areEqual);
|
||||||
|
|
||||||
|
export { ContractFormsContainerMemo as ContractFormsContainer };
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ContractFormsContainer as ContractForms } from './ContractForms';
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { FormTabs } from 'Components/Tabs';
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import { ContractForms } from '../ContractForms';
|
||||||
|
|
||||||
|
const FormTabsContainer = () => (
|
||||||
|
<>
|
||||||
|
<FormTabs id="form-tabs">
|
||||||
|
<ContractForms />
|
||||||
|
</FormTabs>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const FormTabsContainerMemo = memo(FormTabsContainer, areEqual);
|
||||||
|
|
||||||
|
export { FormTabsContainerMemo as FormTabs };
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FormTabs } from './FormTabs';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { FormTabs } from './FormTabs';
|
||||||
|
export { ContractForms } from './ContractForms';
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { areEqual } from 'Utils/equalityChecks';
|
||||||
|
import { FormTabs } from './FormTabs';
|
||||||
|
|
||||||
|
const FormsContainer = () => <FormTabs />;
|
||||||
|
|
||||||
|
const FormsContainerMemo = memo(FormsContainer, areEqual);
|
||||||
|
|
||||||
|
export { FormsContainerMemo as FormsContainer };
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
export type FormTemplate = {
|
||||||
|
company_id: number;
|
||||||
|
name: string;
|
||||||
|
replacement_tags: string;
|
||||||
|
template: string;
|
||||||
|
has_signature: boolean;
|
||||||
|
status: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
deleted_at: string | null;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FormTemplateResponse = {
|
||||||
|
data: FormTemplate[];
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export type { FormTemplate, FormTemplateResponse } from './FormModel';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export type { FormTemplate, FormTemplateResponse } from './FormsModel';
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { handleApiRequest } from 'Utils/handleApiRequest';
|
||||||
|
import { Api } from 'Utils/api';
|
||||||
|
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 {
|
||||||
|
type: keyof FormsActionTypes;
|
||||||
|
payload: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SetFormsActionTypes = MessageAction;
|
||||||
|
|
||||||
|
// Thunk to fetch contract forms for a company
|
||||||
|
export const listCompanyContractForms = (companyId: number) => async (dispatch: any) => {
|
||||||
|
const response = await handleApiRequest(
|
||||||
|
dispatch,
|
||||||
|
Api.get(`companies/${companyId}/contract-forms`),
|
||||||
|
'',
|
||||||
|
FETCHING_CONTRACT_FORMS
|
||||||
|
);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: CONTRACT_FORMS,
|
||||||
|
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 });
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FormsContainer as Forms } from './Forms';
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case CONTRACT_FORMS: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
data: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case FETCHING_CONTRACT_FORMS: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingContractForms: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case DELETING_CONTRACT_FORM: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
deletingContractForm: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case CONTRACT_FORM_DELETED: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
contractFormDeleted: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Contract Forms selectors
|
||||||
|
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;
|
||||||
@@ -11,4 +11,10 @@ export const navItems = [
|
|||||||
path: '/people',
|
path: '/people',
|
||||||
icon: 'people',
|
icon: 'people',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: 'Forms',
|
||||||
|
path: '/form',
|
||||||
|
icon: 'projects',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import { lossDataReducer } from 'Containers/ProjectData/LossData/reducer';
|
|||||||
import { propertyDataReducer } from 'Containers/ProjectData/PropertyData/reducer';
|
import { propertyDataReducer } from 'Containers/ProjectData/PropertyData/reducer';
|
||||||
import { reportsReducer } from 'Containers/ReportsAndDocuments';
|
import { reportsReducer } from 'Containers/ReportsAndDocuments';
|
||||||
import { rocketDryReducer } from 'Containers/RocketDry/reducer';
|
import { rocketDryReducer } from 'Containers/RocketDry/reducer';
|
||||||
|
import { formsReducer } from 'Containers/Forms/reducer';
|
||||||
|
|
||||||
// Combine them
|
// Combine them
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
@@ -113,4 +114,5 @@ export default combineReducers({
|
|||||||
propertyData: propertyDataReducer,
|
propertyData: propertyDataReducer,
|
||||||
reports: reportsReducer,
|
reports: reportsReducer,
|
||||||
rocketDry: rocketDryReducer,
|
rocketDry: rocketDryReducer,
|
||||||
|
forms: formsReducer,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user